レトロ・ゲームのからくり

 投稿者:山中和義  投稿日:2009年11月27日(金)13時02分5秒
  インベーダーの移動、攻撃を再現してみました。(改修版)
速度調整が必要なら、WAIT命令を実行してください。
また、マウスの左ボタンの押下でインベーダーを消滅できます。
!インベーダー・ゲーム

DEF V2W(x)=4*x !仮想画面の座標を物理画面の座標へ
DEF W2V(x)=INT(x/4) !その逆

PICTURE DOT !物理画面にドットを表示する
   PLOT AREA: -2,-2; 2,-2; 2,2; -2,2 !4x4
END PICTURE

LET vx=180 !仮想画面の大きさ 150×180ドット
LET vy=150

SET bitmap SIZE V2W(vx)+1,V2W(vy)+1 !物理画面の大きさ
SET WINDOW 0,V2W(vx),V2W(vy),0 !スクリーン座標

RANDOMIZE


!------------------------------ 弾
DATA "* " !コマ1 弾1
DATA " *"
DATA "* "

DATA " *" !コマ2
DATA "* "
DATA " *"

DIM bm$(1,2) !弾 3×2ドット
CALL ReadPattern(1,3, bm$)

LET NumOfBeam=5 !弾 最大数5
DIM BM(NumOfBeam,3)
MAT BM=(-1)*CON

!------------------------------ インベーダー
LET m=8 !m×nドット
LET n=11

DATA "**       **" !コマ1 機体0
DATA "*         *"
DATA "           "
DATA "           "
DATA "           "
DATA "           "
DATA "*         *"
DATA "**       **"

DATA "           " !コマ2
DATA "*  *   *  *"
DATA " *  * *  * "
DATA "  *     *  "
DATA "**       **"
DATA "  *     *  "
DATA " *  * *  * "
DATA "*  *   *  *"

DATA "  *     *  " !コマ1 機体1
DATA "*  *   *  *"
DATA "* ******* *"
DATA "*** *** ***"
DATA "***********"
DATA " ********* "
DATA "  *     *  "
DATA " *       * "

DATA "  *     *  " !コマ2
DATA "   *   *   "
DATA "  *******  "
DATA " ** *** ** "
DATA "***********"
DATA "* ******* *"
DATA "* *     * *"
DATA "   ** **   "

DATA "    ***    " !コマ1 機体2
DATA "   *****   "
DATA "  *******  "
DATA " ** *** ** "
DATA " ********* "
DATA "   *   *   "
DATA "  * * * *  "
DATA " * *   * * "

DATA "    ***    " !コマ2
DATA "   *****   "
DATA "  *******  "
DATA " ** *** ** "
DATA " ********* "
DATA "  * *** *  "
DATA " *       * "
DATA "  *     *  "

DATA "           " !コマ1 機体3
DATA "    ***    "
DATA " ********* "
DATA "**  ***  **"
DATA "***********"
DATA "   *****   "
DATA "  ** * **  "
DATA "**       **"

DATA "           " !コマ2
DATA "    ***    "
DATA " ********* "
DATA "**  ***  **"
DATA "***********"
DATA "   ** **   "
DATA "  *     *  "
DATA "   *   *   "

DIM ptn$(4,2) !パターン(1機×2コマ)で読み込む
CALL ReadPattern(4,m, ptn$)

SUB ReadPattern(n,l, ptn$(,))
   FOR k=1 TO n !各機体
      FOR j=1 TO 2 !各コマ
         LET v$=""
         FOR i=1 TO l
            READ s$
            LET v$=v$&s$
         NEXT i
         LET ptn$(k,j)=v$ !登録する
      NEXT j
   NEXT k
END SUB

!------------------------------ 編隊
LET p=6 !p×q
LET q=8
DIM F(p+1,q) !0以上:生存フラグ、パターン番号
MAT F=ZER
DATA  4, 4, 4, 4, 4, 4, 4, 4 !パターン番号 ※偶数
DATA  4, 4, 4, 4, 4, 4, 4, 4
DATA  6, 6, 6, 6, 6, 6, 6, 6
DATA  6, 6, 6, 6, 6, 6, 6, 6
DATA  2, 2, 2, 2, 2, 2, 2, 2
DATA  2, 2, 2, 2, 2, 2, 2, 2
DATA  6, 6, 6, 6, 6, 6, 6, 6 !先頭の番号
MAT READ F

LET ox=n+2 !機体の間隔
LET oy=m+2
LET lx=vx-ox*q !編隊の左上位置
LET ly=4


PICTURE BLOCK(v$,m,n) !インベーダーなどを位置(0,0)-(n,m)に表示する
   FOR i=0 TO m-1 !m×nドット
      FOR j=0 TO n-1
         LET k=i*n+j+1
         IF v$(k:k)<>" " THEN DRAW DOT WITH SHIFT(V2W(j),V2W(i))
      NEXT j
   NEXT i
END PICTURE


LET DX=-1 !移動方向
LET FLG=0 !機体の動作
LET Invaded=0 !侵略状況
DO !ゲームループ

!------------------------------ 当り判定(仮)
   MOUSE POLL mx,my,left,right !マウスの状態を得る
   IF left=1 THEN !左ボタン押下なら
      LET x=W2V(mx)-lx !編隊の領域内なら
      LET y=W2V(my)-ly
      IF x<0 OR x>=ox*q OR y<0 OR y>=oy*p THEN
      ELSE
         LET xx=MOD(x,ox) !機体内なら
         LET yy=MOD(y,oy)
         IF xx<n AND yy<m THEN
            LET xx=INT(x/ox)+1 !該当する機体を爆発→消滅する
            LET yy=INT(y/oy)+1
            IF F(yy,xx)>=2 THEN LET F(yy,xx)=1
         END IF
      END IF
   END IF


   !------------------------------ 描画処理毎(フレームを表示する)
   SET DRAW mode hidden !ちらつき防止開始
   CLEAR
   LET CntOfEnemy=0
   FOR x=1 TO q !インベーダー群を表示する
      IF F(p+1,x)>0 THEN !この列に機体が存在するなら

         LET xx=lx+ox*(x-1)

         LET F(p+1,x)=0
         FOR y=1 TO p
            LET ptn=F(y,x) !生存なら
            IF ptn>=0 THEN
               LET idx=INT(ptn/2) !パターンを選択する
               LET koma=MOD(ptn,2)
               DRAW BLOCK(ptn$(idx+1,koma+1),m,n) WITH SHIFT(V2W(xx),V2W(ly+oy*(y-1)))
               IF ptn=1 THEN
                  LET F(y,x)=-1 !爆発→消滅
               ELSE
                  LET F(y,x)=MOD(ptn+1,2)+idx*2 !ぱたぱたアニメーション

                  LET F(p+1,x)=y !先頭の番号を更新する

                  IF ly+oy*(y-1)>vy*2/3 THEN LET Invaded=1 !侵略したなら
               END IF
               LET CntOfEnemy=CntOfEnemy+1 !機体の数
            END IF
         NEXT y

         IF F(p+1,x)>0 THEN !この列に機体があれば
            IF FLG=0 THEN !折り返しを確認する
               IF xx<=0 OR xx+n>=vx THEN LET FLG=1 !左端、右端なら
            END IF

            IF RND<0.1 THEN !弾を発射する
               FOR y=1 TO NumOfBeam !未発射を探す
                  IF BM(y,3)<0 THEN
                     LET BM(y,1)=xx+INT(n/2)
                     LET BM(y,2)=ly+oy*(F(p+1,x)-1)+INT(m/2)
                     LET BM(y,3)=0 !使用中

                     EXIT FOR !1列に1つずつ
                  END IF
               NEXT y
            END IF
         END IF

      END IF
   NEXT x

   FOR y=1 TO NumOfBeam !弾を表示する
      LET ptn=BM(y,3)
      IF ptn>=0 THEN !使用中なら
         DRAW BLOCK(bm$(1,ptn+1),3,2) WITH SHIFT(V2W(BM(y,1)),V2W(BM(y,2)))
         LET BM(y,3)=MOD(ptn+1,2) !ぱたぱたアニメーション
      END IF
   NEXT y
   SET DRAW mode explicit !ちらつき防止終了


   !------------------------------ 移動処理(次のフレームへ)
   SELECT CASE FLG !インベーダーを移動させる
   CASE 0
      LET lx=lx+DX !横へ移動させる
   CASE 1
      LET ly=ly+2 !折り返しの動作(降下させる)
      LET FLG=2
   CASE ELSE
      LET DX=-DX !降下後、1つ離す
      LET lx=lx+DX
      LET FLG=0
   END SELECT

   FOR i=1 TO NumOfBeam !弾を移動させる
      IF BM(i,3)>=0 THEN !使用中なら
         LET BM(i,2)=BM(i,2)+3 !降下させる
         IF BM(i,2)>vy THEN LET BM(i,3)=-1 !下端なら
      END IF
   NEXT i

   !!!WAIT DELAY 0.3

LOOP UNTIL CntOfEnemy=0 OR Invaded=1 !機体全滅まで


END
 

Re: レトロ・ゲームのからくり

 投稿者:荒田浩二  投稿日:2009年11月27日(金)14時48分15秒
  > No.762[元記事へ]

懐かしいゲームです。「クリックしても弾が発射されない」と思いましたが、インベーダーを直接クリックするのですね。
編隊のすぐ右側をクリックすると「添字が範囲外」のエラーが生じることがあります。
当り判定のルーチンで xx=9 のとき IF F(yy,xx)>=2 THEN LET F(yy,xx)=1 がエラーとなるようです。
x=104 を回避すればよいので、IF x<0 OR x>ox*q OR y<0 OR y>oy*p THEN を
IF x<0 OR x>=ox*q OR y<0 OR y>oy*p THEN とするのはどうでしょうか。
 

Re: レトロ・ゲームのからくり

 投稿者:山中和義  投稿日:2009年11月27日(金)15時57分17秒
  > No.763[元記事へ]

荒田浩二さんへのお返事です。

> x=104 を回避すればよいので、IF x<0 OR x>ox*q OR y<0 OR y>oy*p THEN を
> IF x<0 OR x>=ox*q OR y<0 OR y>oy*p THEN とするのはどうでしょうか。

Xの値は[0,ox*q)ですね。
Yの値についても、同じことが言えると思いますので、
 IF x<0 OR x>=ox*q OR y<0 OR y>=oy*p THEN
としてください。
 

レトロ・ゲームのからくり(ブロック崩し)

 投稿者:山中和義  投稿日:2009年11月28日(土)19時14分17秒
  > No.762[元記事へ]

!ブロック崩し

DEF V2W(x)=4*x !仮想画面の座標を物理画面の座標へ
DEF W2V(x)=INT(x/4) !その逆

PICTURE DOT !物理画面にドットを表示する
   PLOT AREA: -2,-2; 2,-2; 2,2; -2,2 !4x4
END PICTURE

LET vx=100 !仮想画面の大きさ 120×100ドット
LET vy=120

SET bitmap SIZE V2W(vx)+1,V2W(vy)+1 !物理画面の大きさ
SET WINDOW 0,V2W(vx),V2W(vy),0 !スクリーン座標

RANDOMIZE


!------------------------------ ブロック
LET lx=5 !左上の座標
LET ly=20

LET ox=10 !間隔
LET oy=6

LET p=5 !p×q個
LET q=INT(vx/ox)-2

DIM B(0 TO p-1,0 TO q-1) !1:有、0:無
MAT B=2*CON
DIM BC(2) !衝突回数の色(ルックアップテーブル)
DATA 4,10
MAT READ BC


!------------------------------ 壁
LET wx1=lx-1 !左上の座標
LET wy1=ly-15
LET wx2=wx1+ox*q !右下の座標
LET wy2=vy-5


!------------------------------ パッド
LET px=x !左上の座標
LET pw=5 !幅の半分


!------------------------------ ボール
LET dx=3 !移動方向
LET dy=5

LET x1=INT((wx2-wx1)/2) !発射位置
LET y1=ly+oy*p + 10

LET x=x1 !現在の位置
LET y=y1


LET k=0
LET NumOfBlock=p*q !ブロックの総数
LET NumOfPad=3 !パッドの残り数

DO !ゲームループ

!------------------------------ 当り判定
   IF (x<=wx1 AND x1<>wx1) THEN !左側の壁 ※2重衝突防止
   !!!IF x<=wx1 THEN !左側の壁
      LET k=0 !ボールの起点
      LET x1=wx1
      LET y1=y-INT((wx1-x)*dy/dx) !位置を補正 ※壁のすり抜け
      LET x=x1 !ボールの位置
      LET y=y1
      LET dx=-dx !ボールの反射
   END IF
   IF (x>=wx2 AND x1<>wx2) THEN !右側の壁
   !!!IF x>=wx2 THEN !右側の壁
      LET k=0
      LET x1=wx2
      LET y1=y-INT((x-wx2)*dy/dx)
      LET x=x1
      LET y=y1
      LET dx=-dx
   END IF

   LET xx=x-lx !ブロックの領域外なら
   LET yy=y-ly
   IF xx<0 OR xx>=ox*q OR yy<0 OR yy>=oy*p THEN

      IF (y<=wy1 AND y1<>wy1) THEN !上側の壁
      !!!IF y<=wy1 THEN !上側の壁
         LET k=0
         LET x1=x-INT((wy1-y)*dx/dy)
         LET y1=wy1
         LET x=x1
         LET y=y1
         LET dy=-dy
      END IF

      IF ABS(x-px)<=pw AND (y>=wy2 AND y1<>wy2) THEN !パッド
      !!!IF ABS(x-px)<=pw AND y>=wy2 THEN !パッド
         LET k=0
         LET x1=x-INT((y-wy2)*dx/dy)
         LET y1=wy2
         LET x=x1
         LET y=y1
         LET dy=-dy
      END IF
      IF y>=vy THEN !ミス!!!
         LET NumOfPad=NumOfPad-1
         LET x=x1 !ボールの位置
         LET y=y1
         LET px=x !パッドの位置
         WAIT DELAY 1
      END IF

   ELSE !領域内なら

   !※ブロックを厚くするとボールが内部に入った状態になって、何重にも衝突したことになる。
      LET xx=INT(xx/ox) !該当するブロックの位置を算出する
      LET yy=INT(yy/oy)
      IF B(yy,xx)>0 THEN !消滅させる
         LET B(yy,xx)=B(yy,xx)-1
         IF B(yy,xx)=0 THEN LET NumOfBlock=NumOfBlock-1

         LET k=0
         LET x1=x
         LET y1=y
         LET dy=-dy
      END IF

   END IF


   !------------------------------ 描画処理毎(フレームを表示する)
   SET DRAW mode hidden !ちらつき防止開始
   CLEAR

   SET AREA COLOR 1
   FOR xx=wx1 TO wx2 !上側の壁を表示する
      DRAW DOT WITH SHIFT(V2W(xx),V2W(wy1))
   NEXT xx
   FOR yy=wy1 TO wy2
      DRAW DOT WITH SHIFT(V2W(wx1),V2W(yy)) !左側、
      DRAW DOT WITH SHIFT(V2W(wx2),V2W(yy)) !右側
   NEXT yy

   FOR yy=0 TO p-1 !ブロック
      FOR xx=0 TO q-1
         LET c=B(yy,xx)
         IF c>0 THEN !存在するなら
            SET AREA COLOR BC(c) !ルックアップテーブルを参照する
            DRAW BLOCK(oy-1,ox-1) WITH SHIFT(V2W(lx+ox*xx),V2W(ly+oy*yy))
         END IF
      NEXT xx
   NEXT yy

   SET AREA COLOR 1
   DRAW BLOCK(2,2*pw) WITH SHIFT(V2W(px-pw),V2W(wy2)) !パッド ※中心の座標で

   SET AREA COLOR 2
   DRAW BLOCK(3,3) WITH SHIFT(V2W(x-1),V2W(y-1)) !ボール ※中心の座標で

   SET DRAW mode explicit !ちらつき防止終了


   !------------------------------ 移動処理(次のフレームへ)
   IF ABS(dy)<ABS(dx) THEN !xの方が増分が多い
   !□□□■
   !□■■□  y=l*x+m の式として考える
   !■□□□
      LET k=k+SGN(dx) !±1 移動量 ※低解像度だとカクカク動くように見える
      !!!LET k=k+SGN(dx)*3 !±3 移動量
      LET x=x1+k
      LET y=y1+INT(k*dy/dx)
   ELSE !yの方が増分が多い
   !□□■
   !□■□  x=l*y+m の式として考える
   !□■□
   !■□□
      LET k=k+SGN(dy) !±1 移動量
      !!!LET k=k+SGN(dy)*3 !±3 移動量
      LET x=x1+INT(k*dx/dy)
      LET y=y1+k
   END IF

   LET px=x !パッド位置はボールの真下


   !!!WAIT DELAY 0.2

LOOP UNTIL NumOfPad=0 OR NumOfBlock=0 !パッド、ブロックがなくなるまで


PICTURE BLOCK(m,n) !ブロックなどを位置(0,0)-(n,m)に表示する
   PLOT AREA: -2,-2; 4*n-2,-2; 4*n-2,4*m-2; -2,4*m-2
END PICTURE
!PICTURE BLOCK(m,n) !ブロックなどを位置(0,0)-(n,m)に表示する
!   FOR i=0 TO m-1 !m×nドット
!      FOR j=0 TO n-1
!         DRAW DOT WITH SHIFT(V2W(j),V2W(i))
!      NEXT j
!   NEXT i
!END PICTURE

END
 
 

戻る