前編では通路/壁だけの最低限の迷路を移動する処理を作りました。 ここまでできれば、あとはコツコツと要素を追加していくだけです (と言っても、完成までは長いですが)。
後編ではプログラムを追加していく形ではなく、 完成版の要点のみ解説していきます。下記リンクが完成版プログラムです。
前編ではマップの 3 次元配列 M(Z,Y,X)
の値は、「0:未探索、1:通路、2:壁」の 3 つしかありませんでした。
まずはこのあたりを拡張します。
種類 | M() の値 |
DATA 内 |
マップ表示 |
---|---|---|---|
未探索 | 0 | (無し) | 空白 |
通路 | 1 | 空白 | 空白 |
壁 | 2 | * |
■ |
扉(開き) | 3 | @ |
□ |
下り階段 | 4 | - |
v |
上り階段 | 5 | + |
^ |
開始地点 | 6 | # |
# |
ワープ | 7 | ? |
? |
宝箱(閉じ) | 8 | $ |
$ |
宝箱(開き) | 9 | (無し) | $ |
扉(閉じ) | 33+n | A~Z |
× |
鍵 | 65+n | a~z |
! |
閉じた扉と鍵はペアで、A~Z
と a~z
を対応させます。ただし今回は(表示の都合で)8 個を上限としました。
ワープは一方通行もありで、飛び先の情報は別途
DATA
に記述することにしました。
ワープ元座標と飛び先の座標を並べます。-1
で終了です(データの間違いを検出するために記述します)。
4400 DATA 1,8,0,14,11,0
4410 DATA 14,11,0,1,8,0
(中略)
4550 DATA -1
配置物を増やしたので、それぞれの 3D
形状をデザインする必要があります。1000
行以降に描画処理を追加しつつ、DATA
を一時的に書き換えながらテストします。
なおデザインの都合上、 扉は必ず壁に挟まれているものとします。 このような制限はなるべく増やしたくないのですが、時には妥協も必要です。
3D 描画では LOCATE
と PRINT
をひたすら並べるよりも、PUT@
を使うと比較的高速に描画できます。
ただし矩形範囲しか描画できないのと、
配列変数を確保する必要があります。
今回はまず破線の描画に使用しています。
3000 COLOR 7,0,0:PRINT CHR$(12):LOCATE 12,12:PRINT"Wait a moment":LINE 12,1:COLOR 0 3010 LINE(9,0)-(9,16),"█":FOR Y=1 TO 15 STEP 2:LOCATE 9,Y:PRINT"▉":NEXT:GET@(9,0)-(9,16),WB
GET@
で配列変数
WB
に取り込んでいます。整数型の配列 1 要素に 2 文字格納できるので、サイズは「総文字数÷2」を切り上げして求めます。上記の破線は 17
文字なので、必要な要素数は 9 です(DIM
で指定する値は最後の要素番号なので
DIM WB(8)
とします)。
なお、この配列には幅や高さの情報が格納されていません。
そのため PUT@
時に高さを小さくすることで、上部だけを描画することができます。
これで破線の長さを調節しています。
同様に、いろいろなパターンを描画して GET@
しています(3020~3090 行)。3000 行の COLOR 0
を削る(もしくは COLOR 7
にする)ことで様子が見えます。
画面左下/右下の表示に関する部分を、一部抜粋します。
3110 PX=1:PY=1:PZ=0:PD=2:MC=0:KY$="♠♥♦♣●○*#":TR=0:TF=0:LG=0:EV=0 3170 LOCATE 21,18:PRINT"Moves:" 3180 LOCATE 21,19:PRINT"Key:":GOSUB 2060 3190 LOCATE 21,20:PRINT"Treasure:":GOSUB 2080 3200 LG=1:LOCATE 1,21:PRINT"タカラモノヲ ミツケテ モチカエロウ!" 2060 LOCATE 26,19:FOR I=1 TO 8:IF KY(I-1)<>0 THEN PRINT MID$(KY$,I,1);ELSE PRINT" "; 2070 NEXT:RETURN 2080 LOCATE 30,20:PRINT TR:RETURN 40 LOCATE 27,18:PRINT MC:ON PD GOTO 60,70,80 250 PX=XX:PY=YY:MC=MC+1:IF MC>9999 THEN MC=9999
MC
に格納しています(最大 9999 歩)。KY$
が各鍵のマークで、3D 描画の鍵と扉にも使用されます。配列
KY(7)
が各鍵の所持フラグです。TR
に格納しています(今回は 1 つしか配置していません)。LG
にカウンタをセットします。1 歩進むごとに減り、0
になったらメッセージが消去されます。配置物の種類が増えたので、マップの表示を拡張しています。
2000 A=M(Z,Y,X):IF A<>0 THEN RETURN 2010 A$=MID$(M$(Z*16+Y),X+1,1):A=INSTR(" *@-+#?$",A$):IF A=0 THEN A=ASC(A$)-32 2020 M(Z,Y,X)=A 2030 LOCATE X+21,Y+1:IF A<32 THEN PRINT MID$(" █ロv^#?$$",A,1):RETURN 2040 PRINT MID$("╳!",A\32,1):RETURN 2050 A=M(Z,Y,X):GOTO 2030
配列 T
にマップをコピーする処理も、多少変更しています。
40 LOCATE 27,18:PRINT MC:ON PD GOTO 60,70,80 50 XX=-1:XY=0:C=30:GOTO 90 60 XX=0:XY=-1:C=28:GOTO 90 70 XX=1:XY=0:C=31:GOTO 90 80 XX=0:XY=1:C=29 90 X0=PX-XX:Y0=PY-XY:Z=PZ:FOR I=0 TO 2:IF X0<0 OR X0>15 OR Y0<0 OR Y0>15 GOTO 130 100 X=X0:Y=Y0:FOR J=0 TO 2:IF I<>2 GOTO 110 ELSE A=T(1,1):B=T(1,J): IF(A=2 OR(A AND 96)=32)AND(B=2 OR(B AND 96)=32)THEN T(I,J)=0:GOTO 120 110 GOSUB 2000:T(I,J)=M(Z,Y,X) 120 X=X+XX:Y=Y+XY:NEXT 130 X0=X0-XY:Y0=Y0+XX:NEXT:POKE&HF3A4+PY*120+PX*2,C:GOSUB 1000
YX=-XY、YY=XX
という法則があるので、この変数を削りました。LINE
から POKE
に変えてみました。ただしアドレス計算を間違えると暴走する可能性があるので、
細心の注意が必要です。BASIC では描画処理が重いので、特に 8
キー押しっぱなしで前進したいところです。INKEY$
ではキーの押しっぱなしを判定できないので、INP
に変更しました。
150 IF TF=1 GOTO 190 160 A=INP(0):IF A=239 THEN PD=(PD+3)AND 3:GOTO 40 170 IF A=191 THEN PD=(PD+1)AND 3:GOTO 40 180 IF A=251 THEN PD=(PD+2)AND 3:GOTO 40 190 IF INP(1)<>254 GOTO 150 200 TF=0:XX=PX+(PD=3)-(PD=1):YY=PY+(PD=0)-(PD=2)
なお、変数 TF
は宝箱を開けたときに 1
になり、前進して宝物を取ることを強制しています。
引き続き、8 キーが押された場合の処理です。
210 A=M(PZ,YY,XX):IF A<32 THEN B=A ELSE B=A\32+9:A=A AND 31 220 ON B GOTO 240,280,240,300,300,300,300,360,300,460,300 230 PRINT"Data error":STOP 240 X=PX:Y=PY:Z=PZ:GOSUB 2050 300 EV=B:GOTO 240
1
歩進んでから処理を行うもの(階段、ワープなど)の場合は、変数
EV
に処理番号をセットしてから前進します。そして前進後、
140 ON EV GOTO,,,310,320,330,370,440,450,,480
ここでそれぞれの処理に分岐します。
前編では階層の移動がありませんでしたが、
階層を移動するとマップの全体書き換えが必要になります。1
マスずつ書き換えるのは非常に重たいので、ここでも GET@/PUT@
を利用します。
410 IF PZ=0 THEN GET@(21,1)-(36,16),M0 ELSE GET@(21,1)-(36,16),M1 420 PZ=TZ:IF PZ=0 THEN PUT@(21,1)-(36,16),M0 ELSE PUT@(21,1)-(36,16),M1
階層ごとに別々の配列変数が必要なのが、やや難点です。 この処理は階段だけでなく、階層をまたぐワープでも使用しています。
ちょっと横道にそれますが、一つハマった点があるので書いておきます。
370 EV=0:GOSUB 2140:RESTORE 4400 380 READ SX:IF SX<0 THEN PRINT"Data error":STOP 390 READ SY,SZ,TX,TY,TZ:IF PX<>SX OR PY<>SY OR PZ<>SZ GOTO 380
これはワープ時の処理で、DATA
から該当する座標を探しています。
ここでは解説用に見やすくスペースを挿入していますが、実際には、
390 READSY,SZ,TX,TY,TZ:IFPX<>SX ORPY<>SYORPZ<>SZGOTO380
こうなっています。なぜ SX
の後ろだけスペースを入れているかというと、入れないと
IF PX<>S XOR
と解釈されてしまうのです。
当然ながら意図した挙動をせず、しばらく悩みました。ご注意ください。
最後に、発音サブルーチン群です。
2090 BEEP 1:BEEP 0:RETURN 2100 FOR I=1 TO 10:GOSUB 2090:NEXT:RETURN 2110 FOR I=1 TO 30:GOSUB 2090:NEXT:RETURN 2120 FOR I=1 TO 3:GOSUB 2090:FOR J=1 TO 500:NEXT:NEXT:RETURN 2130 FOR I=1 TO 10:GOSUB 2090:FOR J=1 TO 100:NEXT:NEXT:RETURN 2140 FOR I=1 TO 3:GOSUB 2090:FOR J=1 TO 150:NEXT:NEXT:RETURN 2150 FOR I=1 TO 5:BEEP 1:FOR J=1 TO 100:NEXT:BEEP 0:FOR J=1 TO 100:NEXT:NEXT:RETURN
BEEP 1/0
を適当な間隔で実行しているだけです。BASIC
だと遅くて、たいしたことはできないですね。
オマケとして、3D 部分を手前から描画するバージョンも参考までに載せておきます。
毎回全体を消さずに、必要な部分だけを描き換えているので、 かなり複雑な処理になっています(描画を省いたところも一部あります)。
バージョン 1/バージョン 2/マシン語版の比較動画を用意しました。
どうですか?マシン語化したくなるでしょう?
→マシン語版(前編)へ続く