パソコン活用研究5番街(Visual Basic、Excel(VBA)、BASIC プログラミング研究)

BASICゲームのプログラムサンプル1

簡単なBASICゲーム作成のための説明。
のろのろ動くかめを安全に反対側まで移動させるゲーム
FM8Kame.basのプログラムの説明

ゲームの概要とプログラムのダウンロードはN88互換Basic カメの道路横断をご参照

もともとFM-7/8用に作成したゲームをN88互換BASIC for Win用に少し変更したバージョンです。
ここで説明用に使ったのはFM8Kame for N88互換Basic ver0.7.1a

オリジナルはプログラミングを始めたばかりの頃に作成したもので、そのコードをほぼそのまま引き継いで
いるので、変数の使い方や、キャラの移動処理などはあまりきれいなコードではないです。

1.プログラムの全体構成

プログラムの全体構成は以下の通り
黄色はサブルーチンにしている部分です。



10〜450行 初期設定
460〜570行 メインルーチン
メインルーチンの中で、以下の3つのサブルーチンを順番に呼び出し、最後570行から460行にgotoで
戻して、メインルーチンをぐるぐる回しています。
@自分のキャラであるカメ(@)の移動と当たり判定 
A牛(U)の移動と当たり判定
B虎(T)の移動と当たり判定

メインルーチンの中で、ゲーム終了フラグ(over・・0 ゲーム継続中  1 ゲーム終了  2 面クリア)を
チェックし、over=1 の時はゲーム終了処理(1310行〜)にジャンプし、over=2 の時は、面クリア処理(1150〜1300行)
にジャンプします。

580〜760行 カメ(自分のキャラ)の移動と当たり判定のサブルーチン
770〜940行 牛の移動と当たり判定のサブルーチン
950〜1140行 虎の移動と当たり判定のサブルーチン
1150〜1300行 面クリアした時の処理
1310行〜 ゲーム終了処理

2. プログラム主要部の説明

以下、ゲーム作成でよく使うコードについていくつか説明します。

(1) 変数
50行でdefint a-zと全変数を整数型になるように宣言していますが、これは少しでも処理速度を
速くするためです。実数型より整数型のほうが処理速度が速いというのが通説 −>Basicの実行速度向上

cx( ), cy( ) は牛の座標
tx( ), ty( ) は虎の座標
char( ) はその座標にあるキャラクタの識別用 0 何もいない 1 牛 2 虎
sc スコア  hsc ハイスコア
max 牛の数  tmax 虎の数
over ゲームの状態 0 継続中  1 ゲームオーバー  2 面クリア

50 defint a-z
60 DIM cx(35),cy(35),char(40,20),tx(15),ty(15)
70〜140 省略
150 hsc=0
160 sc=0:max=15:tmax=2:over=0
sorede



(2) 初期設定

@最初の画面の表示

最初の画面の表示部分です。
250行でキャラの動く範囲の座標を初期化しています。
配列char( )にその座標にいるキャラを格納します。ここでは動ける範囲の全座標に0を初期設定しています。
0は何もキャラがいない状態です。牛がいる場合は1、虎がいる場合は2を格納します。

char( )はその座標に移動できるかの判定処理などに使います。

210 FOR e=10 TO 30
220 color 4:LOCATE e,5:PRINT"="
230 'LOCATE e,12:PRINT"="
240 color 5:LOCATE e,14:PRINT"・"
250 for j=5 to 13: char(e,j)=0:next 'char( )=0 No character
260 NEXT

Aキャラの初期配置
270〜280行 カメ(自キャラ)の配置

290〜300行 牛や虎のキャラの配置にあたり、乱数系列を初期化する
time$(時刻)から秒の部分を切り取って、乱数のシードにするのはゲームをやるたびに
違った乱数系列を得るための割と定番なやり方。

乱数の使い方についてはー>
乱数 Basicの乱数の使い方
乱数を使う準備 Cでの乱数の使い方ですが、コンピュータ内部でどうやって乱数を生成しているのかのお話
乱数を使う1 こちらもCで乱数を使うときの話

ここではさらに起動時にユーザに入力させた値(q)も加えて、できるだけいろいろなシードを
与えられるように工夫しています(微々たる工夫ですが)

310〜380行 牛の配置
350行で、既にキャラの配置済みの場所は避けるようにしています。

forのループ文で制御用に使う変数は普通は,i,j,m,k などを使うのだが
ここはeを使っている。(あんまり美しくないね)

390〜440行 虎の配置

270 X=20:Y=13
280 LOCATE X,Y:PRINT"@"
290 time=val(right$(time$,2)) 300 randomize (time+q) 310 color 7 320 FOR e=1 TO max 330 cy(e)=INT(RND(1)*6)+6 340 cx(e)=INT(RND(1)*18)+11 350 if char(cx(e),cy(e))=1 then goto 330 '既にキャラがあれば、やりなおし 360 LOCATE cx(e),cy(e):PRINT"C" 370 char(cx(e),cy(e))=1 380 NEXT 390 color 2 400 FOR e=1 TO tmax 410 ty(e)=INT(RND(1)*6)+6:tx(e)=INT(RND(1)*18)+11 420 if char(tx(e),ty(e)) <>0 then goto 410 430 LOCATE tx(e),ty(e):PRINT"T": char(tx(e),ty(e))=2 440 NEXT

Bメインルーチン
460行 カメ(自キャラ)の移動と当たり判定のサブルーチンへ
490, 530行 虎の移動と当たり判定のサブルーチンへ。虎の移動速度をカメ(自キャラ)の倍にするため
 メインルーチンの中で2回呼んでいる。
510行 牛の移動と当たり判定のサブルーチンへ

480, 500, 520行の3回 overの値を見て、ゲームオーバーの処理(と480行では面クリアの処理)への分岐

最近のハイスペックなPCでは処理が速すぎてゲームにならないので、waitを入れる工夫を
470, 550〜560行でしている。
N88互換BASICにはwaitに該当する命令がないので、メインルーチンで1秒は消費するようにしている。


460 GOSUB 580
470 time1=val(right$(time$,2))
480 if over=1 then 1310 else if over=2 then 1150
490 GOSUB 950
500 if over=1 then 1310
510 GOSUB 770
520 if over=1 then 1310
530 GOSUB 950
540 if over=1 then 1310
550 time2=val(right$(time$,2))
560 if time1=time2 then goto 550  '最近のPCでは速すぎるのでwaitを入れる
570 GOTO 460

C カメ(自キャラ)の移動と当たり判定
カメの移動方向はu$の値を読み取って決めていますが、少しキー入力のオートリピートによる
誤動作を防ぐ工夫を入れています。
キー入力のオートリピートが有効なハードウェア(PC)では、キーを押しっぱなしにしているとキーバッファ
に入力されたキーが蓄積され、思った通りにキャラを移動させられません。
このため、キーバッファを空にし、最後に押されたキーだけが有効になるような工夫を580〜600行では
行っています。完ぺきではありませんが、これである程度キーのオートリピートによるキャラの誤動作を
防止できています。
620行でカメをいったん消しています。
630〜660行で押されたキーにより、カメの次に移動する座標を決めています。
670〜700行では、カメの移動範囲を 10<= X <=30, 5<= Y <=13 の範囲に制限しています。
710行 カメがY=5まで移動した場合は、その面をクリアです。フラグとしてover=2を設定します。
720行 Iは面クリアにかかった時間(メインルーチンを回った回数)をカウントしています。
 早くクリアすればそれだけ大きくスコアが加算されます。
730〜740行 当たり判定をしています。牛や虎のいる場所に移動した場合はゲームオーバーになります。
750行 620行で消したカメを新しい移動先の座標に表示します。

580 u$=""
590 ux$=inkey$
600 if ux$="" then goto 610 else u$=ux$:goto 590
610 IF u$="5"THEN RETURN
620 LOCATE X,Y:PRINT" "
630 IF U$="4" THEN X=X-1
640 IF U$="6" THEN X=X+1
650 IF U$="8" THEN Y=Y-1
660 IF U$="2" THEN Y=Y+1
670 IF X<10 THEN X=10
680 IF X>30 THEN X=30
690 IF Y<5 THEN Y=5
700 IF Y>13 THEN Y=13
710 IF Y=5 THEN over=2
720 I=I+1
730 O=char(X,Y)
740 IF O=1 OR O=2 THEN over=1   'GAME OVER
750 LOCATE X,Y:color 4:PRINT"@"
760 RETURN

D牛の移動と当たり判定
770行 変数Aは、この牛の移動サブルーチンを繰り返すためのカウンタにしています。
790行 3回このサブルーチンを回ったら(=牛が3頭動いたら)、このサブルーチンからReturnします。
  変数Pは牛のNo.です。
800行 maxにはその面で表示する牛の数が入っており、P>max になったらP=1に戻します。
810行 牛を消す処理
820〜900行 牛の次の移動先の決定と表示をします。牛は単純に1マスづつ左に移動します。
830〜840行 牛が左端を超えたら(X<10)、右端(X=30)に移動させます。
880行 決定した移動先に既にキャラが存在しているときは元の位置にとどまらせます。
  r1,t1に元の座標を保持しています。(このあたりの変数の使い方がイマイチきれいでないです)
930行 当たり判定。カメ(自キャラ)に当たったときは、終了フラグover=1をたてます。

なお、790行のように IF文の終わりに、REM文(')を書くとN88互換BASICではエラーになるケースがるよう
です。

770 A=A+1
780 '3頭動いたらreturn returnの後ろのREM文はエラーになるようだ
790 IF A>3 THEN A=0:RETURN ELSE P=P+1  'Pは動かす牛のNo.
800 IF P>max THEN P=1  'maxを超えたら1に戻す
810 LOCATE cx(P),cy(P):PRINT" ":char(cx(p),cy(p))=0:r1=cx(p):t1=cy(p)
820 cx(P)=cx(P)-1
830 IF cx(P)<10 THEN GOTO 840 ELSE 870
840 cx(P)=30
850 LOCATE 10,cy(P):PRINT" ":char(10,cy(p))=0
860 cy(P)=INT(RND(1)*6)+6
870 oi=char(cx(p),cy(p))
880 if oi<>0 then cx(p)=r1:cy(p)=t1
890 '移動先にキャラがある場合は元の位置にとどまる
900 LOCATE cx(P),cy(P):color 7:PRINT"C":char(cx(p),cy(p))=1
910 '
920 '当たり判定
930 IF cx(P)=X AND cy(P)=Y THEN over=1
940 GOTO 770

E虎の移動と当たり判定
950行 tmaxがその面で表示する虎の数です。

970〜1110行 虎の次の移動先座標の決定と表示です。
虎の移動先の探索は下の図の@〜Cの順で移動できる場所を探します。
(この移動先決定のコードの書き方はイマイチですね。)
ここでも、移動先に既に別のキャラがいる場合は、元の位置にとどまるようにしています。

1120行で当たり判定をしています。

950 FOR G=1 TO tmax
960 LOCATE tx(G),ty(G):PRINT" ":char(tx(g),ty(g))=0:QT1=tx(G):QW1=ty(G)
970 tx(G)=tx(G)-1
980 OI=char(tx(G),ty(G))
990 IF OI<>0 THEN ty(G)=ty(G)-1 ELSE 1060
1000 OI=char(tx(G),ty(G))
1010 IF OI<>0 OR (ty(g)<=5 and ty(g)<12) THEN ty(G)=ty(G)+2 ELSE 1060
1020 OI=char(tx(G),ty(G))
1030 IF OI<>0 THEN tx(G)=tx(G)+1 ELSE 1060
1040 OI=char(tx(G),ty(G))
1050 IF OI<>0 THEN tx(G)=QT1: ty(G)=QW1
1060 IF tx(G)=<9 THEN 1070 ELSE 1110
1070 tx(G)=30:ty(G)=INT(RND(1)*6)+6
1080 IF ty(G)<6 THEN ty(G)=6 ELSE IF ty(G)>12 THEN ty(G)=12
1090 oi=char(tx(g),ty(g))
1100 if oi<>0 then tx(g)=qt1:ty(g)=qw1
1110 LOCATE tx(G),ty(G):color 2:PRINT"T":char(tx(g),ty(g))=2
1120 IF tx(G)=X AND ty(G)=Y THEN over=1  '当たり判定
1130 NEXT
1140 RETURN

F面クリアの処理
1190〜1200行はスコアの加算です。変数iは時間(=メインルーチンを回った回数)です。
 早く面クリアできればそれだけスコアの増加が大きくなります。最低加算点は1点になります。
1250〜1260行はキーのオートリピート等でキー入力バッファにたまった余計なキーを削除しています。
 次の面の開始時にカメ(自キャラ)が不用意な動作をしないためです。
1270行 面をクリする毎に牛と虎の数を増やします。だんだん難しくしていく。虎が7頭を超えてくるとそれなりに
 難しくなります。
1280〜1290行 ただし牛の最大数は30、虎の最大数は10に制限します。



1150 LOCATE 14,4:color 7:PRINT"ヤッタネ!!!!!!!!!!!!!"
1160 '
1170 BEEP
1180 '
1190 if i>49 then i=49
1200 sc=sc+50-I
1210 I=0:over=0
1220 LOCATE 10,3 :PRINT"SCORE ";sc
1230 IF hsc<sc THEN hsc=sc
1240 LOCATE 25,3:PRINT"HI-SC ";hsc
1250 ux$=inkey$
1260 if ux$="" then 1270 else 1250
1270 max=max+1 : tmax=tmax+1
1280 if max>30 then max=30
1290 if tmax>10 then tmax=10
1300 GOTO 170

Gゲームオーバー処理
1340〜1380行 過去のScoreのTop5の表示
1390〜1440行 スコアが過去のTop5にランクインしたかどうかの判定
 Top5内に入った場合、1550行目からのサブルーチンでランクイン処理をする。
ランクイン処理が終了したら(ランクイン処理のフラグfin=1の場合)1420行目では
goto文でForループを飛び出している。

1450〜1490行目 過去Top5のファイルの更新
1500〜1540行  終了処理
1550〜1610行 ランクイン処理

1310 LOCATE X,Y:COLOR 6: PRINT "*":color 7
1320 LOCATE 14,4:PRINT" GAME OVER ! ":beep
1330 LOCATE 10,3:PRINT"SCORE ";sc
1340 open "kame_score.txt" for input as #1
1350 locate 45,10:print "Top 5 Score"
1360 for k=1 to 5
1370 input #1, name$(k), topsc(k) : locate 45,k+10: print k;name$(k);" : "; topsc(k)
1380 next
1390 fin=0
1400 for k=1 to 5
1410 if sc>topsc(k) then gosub 1550
1420 if fin=1 then 1440
1430 next
1440 close
1450 open "kame_score.txt" for output as #1
1460 for k=1 to 5
1470 print #1, name$(k);",";topsc(k)
1480 next
1490 close
1500 'sc=0:max=15 この行はなくてもよい
1510 LOCATE 25,3:PRINT"HI-SC ";hsc
1520 LOCATE 5,17:PRINT"ONCE MORE HIT R KEY"
1530 A$=INKEY$
1540 IF A$="R" or A$="r" THEN 160 ELSE 1530
1550 locate 5,16 :print "you get";k;"rank!! input your name" : input your$
1560 for m=4 to k step -1
1570 name$(m+1)=name$(m) : topsc(m+1)=topsc(m)
1580 next
1590 name$(k)=your$ : topsc(k)=sc
1600 fin=1
1610 return





TOP PAGE >Basic活用研究目次