今日は7日目の続きです。
迷路の中を歩くことができるようになったので、こんどは同時に敵も迷路の中に表示して動かしてみます。
敵の進行方向を決めるアルゴリズムはいろいろ考えられますが、今回はランダムに動かすことにします。
もちろん、壁を通り抜けて移動することはできません。
まずランダムに方向を決め、そちらへ移動できるかどうかを調べ、移動できなければ再度ランダムに方向を決めます。
効率は良くないですが、最悪でも来た方向へ戻ることはできるので、無限ループにはなりません。
さて、ランダムといっても、1ピクセルごとにランダムに動いたのでは足踏みに近い状態になってしまいます。
そこで迷路の1部屋ごとに、次にどの部屋へ向かうかをランダムに決めることにします。
具体的には、敵の座標が部屋の中央になったときだけ、方向転換ができることにします。
ここで「部屋の中央」の定義ですが、下の図を見てください。
今制作しているプログラムでは、迷路の1部屋は32×32ピクセルで、敵を表すスプライトは16×16ピクセルです。
また、部屋の上端8ピクセルと左端8ピクセルは、壁がある可能性がありますので空けておかなければなりません。
残り24×24ピクセルの中央に敵を現すスプライトを置くと、ちょうど通路の中央にあるように見えます。
つまり、部屋の左上隅から12ピクセルだけ右下に下がった座標が、部屋の中央ということになります。
ではプログラムの主要部分です。(全体は最後に掲載します。)
DIM DIR_X[4],DIR_Y[4] @DIR:DATA 1,0,0,1,-1,0,0,-1 'E,S,W,N RESTORE @DIR FOR I=0 TO 3:READ DIR_X[I],DIR_Y[I]:NEXT E_MAX=8 DIM EX[E_MAX+1],EY[E_MAX+1] DIM EDX[E_MAX+1],EDY[E_MAX+1] FOR I=0 TO E_MAX EX[I]=12+32*RND(M_W-1) EY[I]=12+32*RND(M_H-1) SPSET 1+I,1022 NEXT @LOOP FOR I=0 TO E_MAX IF ((EX[I]-12) MOD 32)==0 && ((EY[I]-12) MOD 32) ==0 THEN REPEAT D=RND(4) TMP_X=EX[I]+DIR_X[D]*9 TMP_Y=EY[I]+DIR_Y[D]*9 UNTIL COUNT_WALL(MAP,TMP_X>>3,TMP_Y>>3)==0 EDX[I]=DIR_X[D]:EDY[I]=DIR_Y[D] ENDIF EX[I]=EX[I]+EDX[ I]:EY[I]=EY[I]+EDY[I] SPOFS 1+I,EX[I]-X+192,EY[I]-Y+112 NEXT
まず、4つの方向(Direction)へ移動するときに、XとYをそれぞれどう変化させるかを表す配列DIR_XとDIR_Yを作っています。
4つの方向はそれぞれ右(1,0)下(0,1)左(-1,0)上(0,-1)となります。
今回は、この値をDATA文で書いておいてREADでDIR_XとDIR_Yに読み込んでいます。
次に、敵の座標と敵の移動のための加算値を配列で用意します。
EX・EYが敵の座標、EDX・EDYが加算値です。
敵の数はいくつでも良いのですが、ここでは9匹にしました。
E_MAX=8
となっていますが、配列の添え字は0から始まるので、それも含めて9になるように、それより1小さい値にしています。
FOR~NEXTループで、EX・EYをランダムに設定しています。
ただし、座標値は先ほど述べたように部屋の中央に来るようにしています。
同時にSPSETでスプライトの絵を指定しています。
「1022」は幽霊のキャラクタ番号です。
@LOOP以降がメインループです。
FOR~NEXTループで敵の座標を順に処理します。
IF文の条件式
((EX[I]-12) MOD 32)==0 && ((EY[I]-12) MOD 32) ==0
で、座標が部屋の中央かどうかを判定しています。
中央なら、DIR_X[0]・DIR_Y[0]~DIR_X[3]・DIR_Y[3]のうちのいずれかの組をランダムに選びます。
そして、移動した場合の新しい座標(TMP_X,TMP_Y)を計算します。このとき
TMP_X=EX[I]+DIR_X[D]*9
のように「*9」がついていますが、これはマップの配列の1マスが8×8ピクセルに相当するので、(TMP_X,TMP_Y)がマップ上で隣のマス目になるようにするには、4より大きく12より小さいピクセル数分、移動させる必要があるからです。
最後にUNTIL文の条件式で、(TMP_X,TMP_Y)の方向に進むことができるかどうかを判断しています。
進むことができる場合はループを抜けますので、進行方向を表すDIR_X,DIR_Yの値をEDX,EDYに設定します。
IF文を抜けた後、EXとEYにそれぞれEDX,EDYを加算して敵の座標を更新します。
そして、更新した敵座標に応じて、敵のスプライトの座標も更新します。
スプライトの座標はBGに描かれた迷路の画像と合っていなければなりません。
スプライトの座標はスクリーン左上を原点とした座標で指定する必要があり、プログラムでは
EX[I]-X+192,EY[I]-Y+112
となっています。
これらの座標の関係は下図のようになっています。
太い赤い矢印で示しているのが求めたい座標です。
この座標は、(EX-X,EY-Y)と(192,112)の合成で表せることが分かります。
以上で、とりあえず敵をランダムにウロウロさせることができるようになりました。
最後にプログラム全体を掲載しておきますが、今回説明した以外に、一点変更があります。
COUNT_WALLSの中で使っている変数I,J,Nを「_I,_J,_N」にしました。
これは、メインルーチンと関数定義で同じ名前の変数を使っている場合に、関数定義の中の変数がメインルーチンの中の変数(グローバル変数)として扱われるという問題(プチコン3号のバグ?)があったためです。
プログラム自体も、だいぶ長くなってきましたので、少し構造を整理したほうが良さそうですね。
M_W=15:M_H=10 DIM R[M_W+2,M_H+2] FOR J=0 TO M_H+1 FOR I=0 TO M_W+1 IF I==0 || I==M_W+1 || J==0 || J==M_H+1 THEN R[I,J]=0 ELSE R[I,J]=3 ENDIF NEXT NEXT FOR J=1 TO M_H FOR I=1 TO M_W R_N=R[I,J-1] R_W=R[I-1,J] IF R_N==0 && R_W>0 THEN R[I,J]=1 IF R_N>0 && R_W==0 THEN R[I,J]=2 IF R_N>0 && R_W>0 THEN R[I,J]=1+RND(2) NEXT NEXT ACLS BGSCREEN 0,M_W*2+1,M_H*2+1 W1=292:W2=12580:W3=291:WE=257 FOR J=1 TO M_H FOR I=1 TO M_W IF R[I,J]==3 THEN C1=W3:C2=W1:C3=W2 IF R[I,J]==2 THEN C1=W2:C2=WE:C3=W2 IF R[I,J]==1 THEN C1=W1:C2=W1:C3=WE BGPUT 0,I*2-2,J*2-2,C1 BGPUT 0,I*2-1,J*2-2,C2 BGPUT 0,I*2-2,J*2-1,C3 BGPUT 0,I*2-1,J*2-1,WE NEXT BGPUT 0,M_W*2,(J-1)*2,W2 BGPUT 0,M_W*2,J*2-1,W2 NEXT BGFILL 0,0,M_H*2,M_W*2-1,M_H*2,292 BGPUT 0,M_W*2,M_H*2,256 DIM MAP[M_W*4+1,M_H*4+1] FOR J=1 TO M_H FOR I=1 TO M_W X=I*4-4 Y=J*4-4 MAP[X,Y ]=1 IF (R[I,J] AND 1) > 0 THEN MAP[X+1,Y]=1 MAP[X+2,Y]=1 MAP[X+3,Y]=1 ENDIF IF (R[I,J] AND 2) > 0 THEN MAP[X,Y+1]=1 MAP[X,Y+2]=1 MAP[X,Y+3]=1 ENDIF NEXT X=M_W*4 Y=J*4-4 MAP[X,Y]=1 MAP[X,Y+1]=1 MAP[X,Y+2]=1 MAP[X,Y+3]=1 NEXT FOR I=0 TO M_W*4 MAP[I,M_H*4]=1 NEXT SPSET 0,502 SPOFS 0,192,112,0 X=8:Y=8 DIM DIR_X[4],DIR_Y[4] @DIR:DATA 1,0,0,1,-1,0,0,-1 'E,S,W,N RESTORE @DIR FOR I=0 TO 3:READ DIR_X[I],DIR_Y[I]:NEXT E_MAX=8 DIM EX[E_MAX+1],EY[E_MAX+1] DIM EDX[E_MAX+1],EDY[E_MAX+1] FOR I=0 TO E_MAX EX[I]=12+32*RND(M_W-1) EY[I]=12+32*RND(M_H-1) SPSET 1+I,1022 NEXT @LOOP FOR I=0 TO E_MAX IF ((EX[I]-12) MOD 32)==0 && ((EY[I]-12) MOD 32) ==0 THEN REPEAT D=RND(4) TMP_X=EX[I]+DIR_X[D]*9 TMP_Y=EY[I]+DIR_Y[D]*9 UNTIL COUNT_WALL(MAP,TMP_X>>3,TMP_Y>>3)==0 EDX[I]=DIR_X[D]:EDY[I]=DIR_Y[D] ENDIF EX[I]=EX[I]+EDX[ I]:EY[I]=EY[I]+EDY[I] SPOFS 1+I,EX[I]-X+192,EY[I]-Y+112 NEXT STICK OUT DX,DY X_NEW=X+DX*3:Y_NEW=Y-DY*3 IF COUNT_WALL(MAP,X_NEW>>3,Y>>3)==0 THEN X=X_NEW ELSE X=MAX(MIN(X_NEW,X OR 7),X-(X AND 7)) ENDIF IF COUNT_WALL(MAP,X>>3,Y_NEW>>3)==0 THEN Y=Y_NEW ELSE Y=MAX(MIN(Y_NEW,Y OR 7),Y-(Y AND 7)) ENDIF BGOFS 0,X-192,Y-112,0 VSYNC 1 GOTO @LOOP DEF COUNT_WALL(MAP,PX,PY) _N=0 FOR _J=0 TO 2 FOR _I=0 TO 2 _N=_N+MAP[PX+_I,PY+_J] NEXT NEXT RETURN _N END
コメント