プチコン3号 6日目 迷路を背景グラフィックスにしてスクロールさせる

前回、迷路を生成しましたので、今度は迷路をBGに表示させて、4日目のようにスライドパッドでスクロールさせてみたいと思います。
こんな感じの表示で、スライドパッドで迷路をスクロールさせます。
いずれは画面中央にキャラクターを置いて、迷路の中を歩けるようにしたいと思いますが、今日はまだスクロールさせるだけです。

pc6-1.jpg

前回はPRINT文で罫線を使って迷路を表示しましたが、今回はBGPUTでBGに描画します。
使用する絵は、「291,292,256,257」の組を選びました。
291が状態3の部屋、292が状態1の部屋、256は右下隅の表示欠けの埋め、257は空白です。
他にもSmileツールで探せば、迷路の表示に使える絵がいろいろと用意されています。

pc6-2.jpg
pc5-4.jpg

状態2の部屋に使う絵は、用意されてません。
これは状態1の部屋に使う292番のキャラクタを、270度回転して使います。
270度回転するには、キャラクタ番号に12288を足します。
12288+292=12580が、状態2の部屋の画像になります。

BGキャラクタの回転に関しては、プチコン3号のマニュアルにも現時点ではあまり詳しく書かれていなかったので、ここでちょっと補足しておきます。
キャラ番号は16ビットで指定しますが、上位4ビットが図のように回転・横反転・縦反転の指定に使われます。
簡単には、
・回転: 回転の状態(0~3)に4096を掛けた数
・横反転: 16384
・縦反転: 32768
をキャラ番号に加算すれば、回転・反転したキャラクタを表示できます。

ちなみにキャラ番号を16進数3桁で表現すれば、頭に1桁の16進数を付加するだけなので、足し算を計算しなくて済みます。
たとえば、292番は16進数で&H124ですので、270度回転したキャラクタは&H3124になります。
このへんの話になると、プログラミング初心者の方には難しいかもしれませんね・・・

pc6-3.jpg

さて、プログラムに戻ります。
BGもスプライトも16×16ピクセルですので、迷路の各部屋をBG1枚に割り当てると、壁の厚みがあるために窮屈な感じになりそうです。
そこで、各部屋をBG4枚で表現することにしました。
具体的には、下図のような具合です。
太い青線が1部屋分、細い青線がBG1枚です。
たとえば左上の部屋を表現するには、以下のように4回BGに書き込みます。
(0,0)→ 291
(1,0)→ 292
(0,1)→ 12580
(1,1)→ 257

pc6-4.jpg

また、一番右の列には125800、一番下の部屋の下部には292番のキャラクタを描きます。
これだけだと、右下の壁が少し欠けてしまいますので、そこに256番のキャラクタを描きます。

スクロール操作は、BGの大きさが違うことを除けば、基本的には4日目の記事のままです。

以上をプログラムにしたものが以下になります。
画面は、迷路の部分(1行~20行)は前回と同じですので省略しました。

pc6-5.jpg
M_W=49:M_H=24
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
X=0:Y=0:XMIN=-200:YMIN=-120
XMAX=16*(M_W*2)-200:YMAX=16*(M_H*2)-120
@LOOP
STICK OUT DX ,DY
X=MAX(XMIN,X+DX*8)
X=MIN(X,XMAX)
Y=MAX(YMIN,Y-DY*8)
Y=MIN(Y,YMAX)
BGOFS 0,X,Y,500
VSYNC 1
GOTO @LOOP

今回の追加部分はACLS以降の部分です。
BGのサイズは、迷路一部屋あたり2×2のBGを使いますので、迷路のサイズ(M_W、M_H)の2倍、それに右端の1列と最下行の1行を追加したものになります。
W1、W2、W3、WEは、3種類の壁および壁が無い状態のキャラ番号です。

C1~C3は、1つの部屋に対するBGのキャラ番号を代入します。
C1が左上、C2が右上、C3が左下です。右下は常に壁無しです。
各部屋の状態R[I,J]に応じて、C1~C3にW1~WEを代入します。
そのあと、BGPUTでBGにキャラクタを書き込んでいます。

39行目以降は、スライドパッドでスクロールするためのコードです。
画面の中央のポイント(200,120)が迷路からはみ出さないようにXとYの最大値・最小値を決めています。
また、端までスクロールしたときの判定を以前はIF文で行っていたところを、MINとMAXを使って書き換えてみました。

これで迷路をスクロールさせることができました。
キャラクターを迷路の中で移動させる場合は、壁があるかどうか判定を追加する必要があります。
それはまた次回に作りたいと思います。

プチコン3号 5日目 迷路を作ってみる

pc5-1.jpg

前回、背景グラフィックスを描いてスクロールさせてみましたが、今度はこれを応用して迷路を抜けるゲームを何回かかけて作ってみたいと思います。
そこで、まずは迷路を自動生成するプログラムを作ってみました。

迷路の生成アルゴリズムは、いろいろあります。
今回は、そのなかでももっとも単純なものを使いました。

まず部屋が縦横に規則正しく並んでいて、各部屋は「西」と「北」に壁があるものと考えます。
図では、青が西の壁、赤が北の壁です。

pc5-2.jpg

次に、各部屋について、西か北、どちらかの壁をランダムに壊します。
ただし、隣に部屋が無い壁は壊すことができません。
すると、たとえば下図のようになります。
これで迷路の出来上がりです。

pc5-3.jpg

この方法で作る迷路は欠点があります。
一番上の行は、北の壁が壊せないので常に西の壁が壊されます。
一番左の列は、西の壁が壊せないので常に北の壁が壊されます。
各部屋の北か西の壁を必ず壊さなければならないので、東や南にしか出口のない部屋が作れません。

こういった欠点のない様々な迷路の生成アルゴリズムが考案されています。
興味のある方は、下記のページをごらんください。
英語ですが、動作するサンプルが豊富で楽しめます。

"Algorithm" is Not a Four-Letter Word

さて、上記のアルゴリズムをBASICで書いてみました。
表示部分も含めて何とか1画面に納まりました。

pc5-2.jpg
M_W=49:M_H=24
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 OR I==M_W+1 OR J==0 OR 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 AND R_W>0 THEN R[I,J]=1
IF R_N>0 AND R_W==0 THEN R[I,J]=2
IF R_N>0 AND R_W>0 THEN R[I,J]=1+RND(2)
NEXT
NEXT
FOR J=1 TO M_H
FOR I=1 TO M_W
IF R[I,J]==3 THEN PRINT "┌";
IF R[I,J]==2 THEN PRINT "─";
IF R[I,J]==1 THEN PRINT "│";
NEXT
PRINT "│"
NEXT
PRINT "─"*M_W

まず、ずっと飛ばして最後のFOR~NEXTループを見てください。
これは迷路の画面表示部分です。
壁の有無について、以下のように数字を割り当てました。
・北、西に壁:3
・北に壁:2
・西に壁:1
FOR~NEXTループでは、この数字に従って罫線を1文字出力しています。
なお、罫線は記号入力モードで入力できます。記号入力モードにするには、EDITボタンの上のハートマークのキーを押します。

部屋の状態は、上記の3つの数に加えて
・部屋がない:0
という数値も割り当てます。
数値0の部屋を、迷路を取り囲むように配置します。

すると、初期状態(全部の部屋の北・西の壁がある状態)は下図のようになります。(迷路のサイズは5×5にしています。)

pc5-4.jpg

それでは、プログラムの最初から説明していきます。
先頭部分、M_HとM_Wが迷路(Maze)の大きさです。
M_Hが縦の大きさ(Height)、M_Wが横の大きさ(Width)を表します。
今回は、プチコンの画面サイズが50文字×30行なので、それより小さくしています。
次に、迷路の部屋を現す配列を宣言しています。
サイズが「M_W+2,M_H+2」となっているのは、迷路の周囲を取り囲む「0」の部屋の分を含めているためです。

続いて、配列を初期状態にします。配列と迷路の部屋との対応は下図のようになっています。

pc5-5.jpg

一番上の行(J==0)と一番下の行(J==M_H+1)、一番左の列(I==0)と一番右の列(I==M_H+1)は0、それ以外は3にします。

FOR J=0 TO M_H+1
FOR I=0 TO M_W+1
IF I==0 OR I==M_W+1 OR J==0 OR J==M_H+1 THEN
R[I,J]=0
ELSE
R[I,J]=3
ENDIF
NEXT
NEXT

次に、「0」の部屋以外の各部屋について、北の壁と西の壁のどちらかを消します。
実際には、配列の要素の値を1または2にしていきます。

R_N=R[I,J-1]
R_W=R[I-1,J]
IF R_N==0 AND R_W>0 THEN R[I,J]=1
IF R_N>0 AND R_W==0 THEN R[I,J]=2
IF R_N>0 AND R_W>0 THEN R[I,J]=1+RND(2)

R_Nは部屋の北隣の部屋の状態、R_Wは部屋の西隣の部屋の状態です。

北隣が0で西隣が0以外なら、西の壁を壊します。(北の壁がある状態=1)
西隣が0で北隣が0以外なら、北の壁を壊します。(西の壁がある状態=2)
両方とも0以外の場合は、状態1と状態2のどちらかをランダムに選びます。
RND(2)は0以上2未満の整数をランダムに返しますので、1+RND(2)で1か2のどちらかの値になります。

以上で迷路ができました。
表示部分は最初に解説しましたので、プログラムはこれで終わりです。

次は、キャラクターを操作して迷路を抜けるプログラムを作りたいと思います。

プチコン3号 4日目 背景をスクロールさせる

昨日はスプライトを動かしてみましたが、今日はRPGでよくあるような、主人公を中央に固定して背景をスクロールさせるプログラムを作ってみました。

プチコン3号には背景用グラフィックス(以下BG)のための命令が数多く備わっています。
スプライトとBGがあれば、2Dのゲームの画像表示そのもので悩むことはほぼ無いのではないかと思います。

今回はBGを以下のように使います。

pc4-1.png

まず、スクリーンの3倍の大きさのBGを用意します。
そして、スプライトはスクリーン中央に固定し、スクリーン全体をBGの上で移動させます。
すると、結果的にスプライトがBGの上を移動しているように見えることになります。

ただし、画面の端からはみ出さないように気をつけなければなりません。
移動範囲はスクリーンの2倍、Xが0から799まで、Yが0から480までとなります。
それ以上動かすと、BGの外側の何もない部分が見えてしまいます。

pc4-2.png

実際に作ってみたプログラムは以下の通りです。
基本的には、前回と同じプログラムで、スプライトを動かす代わりにBGを動かしているだけです。
他に、最初にBGに絵を描いている処理が増えています。

pc4-0.jpg

X=400:Y=240:Z=500
DZ=0
SPSET 0,506
SPOFS 0,184,104,Z
BGSCREEN 0,75,45
BGFILL 0,0,0,74,44,684
BGFILL 0,11,6,63,38,574
BGPUT 0,37,22,246
@LOOP
STICK OUT DX,DY
X=X+DX*8
IF X>800 THEN X=800
IF X<0 THEN X=0 Y=Y-DY*8 IF Y>480 THEN Y=480
IF Y<0 THEN Y=0 IF DZ==0 THEN DZ=16 AND BUTTON(2) Z=Z-DZ IF Z<-200 THEN DZ=-DZ IF Z>500 THEN DZ=0:Z=500
BGOFS 0,X,Y,500
S=1.0+(500-Z)/500
SPSCALE 0,S,S
VSYNC 1
GOTO @LOOP

まず最後から5行目の「BGOFS」命令ですが、これはSPOFS同様、指定した番号のBGのX,Y,Zの値を指定します。
XとYは、スクリーンの原点(左上)が、BG上でどの座標にあるかを表します。

Xが大きくなると、スクリーンはBGの上を右へ移動していきます。
そのとき3DSの画面上では、BGの画像が右から左へとスクロールしているように見えることになります。

BGSCREEN 0,75,45

はBGの大きさを指定しています。
プチコン3号はBGを4枚持っており、最初に番号でBGを指定します。今回は0番です。
大きさは、16ピクセル単位での指定となります。横75だと、75*16=1200ピクセルとなります。

BGFILL 0,0,0,74,44,684

は、BGの中の四角形の領域を、指定した画像で塗りつぶします。
領域の座標は、やはり16ピクセル単位での指定となります。

画像と番号との対応は、スプライトと同じくSmileツールの中で確認できます。
今回は、0番のBGの全体を684番の画像で塗りつぶしています。
684番の画像は、下の画面で言うと四角のコンクリートのような画像です。

BGFILL 0,11,6,63,38,574

も同じ命令ですが、画像は574番で、これは草地の画像です。
これでスプライトが移動できる範囲を塗りつぶしています。
その次の

BGPUT 0,37,22,246

は画像を1つ(16×16ピクセル)だけ描く命令です。
BGの中央に、246番の「家」の画像を描いています。

pc4-3.png