りょうのゲーム塾 -111ページ目

りょうのゲーム塾

再開、準備中。

チェックポイント
サンプルゲームに乱数を導入してみました。
乱数の導入により、よりゲームらしくなったと思います。


サンプルゲームの変更部分のプログラムです。

■プログラム
'
' system
'
@SYS_INI
CLEAR
SYS_CNT = 0
SYS_KEY = BUTTON()
RETURN
'
@SYS_MAIN
VSYNC 1
CLS
SYS_CNT = (SYS_CNT + 1) AND &H3FFFF
OLD_KEY = SYS_KEY
SYS_KEY = BUTTON()
TRG_KEY = SYS_KEY XOR OLD_KEY
ON_KEY = TRG_KEY AND SYS_KEY
RETURN
'
' UFO task
'
@UFO_INI
UFO_SX = 0.1
UFO_F = 0
UFO_CNT = 60
RETURN
'
@UFO_TASK
ON UFO_F GOTO @UFO_WAIT,@UFO_MVR,@UFO_MVL
STOP
'
@UFO_WAIT
UFO_CNT = UFO_CNT - 1
IF UFO_CNT > 0 THEN RETURN

IF RND(2) GOTO @UFO_LSET

GOTO @UFO_RSET
' move right
@UFO_MVR
LOCATE UFO_X,UFO_Y
PRINT "A"
UFO_X = UFO_X + UFO_SX
IF UFO_X > 32 GOTO @UFO_DETH
HITX = UFO_X
HITY = 0
GOSUB @WPN_HITC
IF HITF GOTO @UFO_DETH
RETURN
'
@UFO_LSET
UFO_X = 31
UFO_Y = RND(10)
UFO_F = 2
RETURN
' move left
@UFO_MVL
LOCATE UFO_X,UFO_Y
PRINT "A"
UFO_X = UFO_X - UFO_SX
IF UFO_X < 0 GOTO @UFO_DETH
HITX = UFO_X
HITY = 0
GOSUB @WPN_HITC
IF HITF GOTO @UFO_DETH
RETURN
'
@UFO_RSET
UFO_X = 0
UFO_Y = RND(10)
UFO_F = 1
RETURN
'
@UFO_DETH
UFO_F = 0
UFO_CNT = RND(60)
RETURN


システム部分に変更があります。
変更部分はシステムカウンタのカウントアップ部分です。

SYS_CNT = (SYS_CNT + 1) AND &H3FFFF

これはSYS_CNTに1を足していきますが、すぐにはエラーがでません。
しかしコンピュータの変数は有限なデータなので上限値が決まっています。
これをオーバーするとオーバーフローエラーが発生します。
それを起こさないためにマスクをしています。
マスクについては解説してありますが今回のケースは前回のマスクとは意味合いが違うため必要になった時に解説します。
ここでは変更だけしておいてください。

あとはUFO部分の変更です。

①ラベル@UFO_WAIT部分の変更点


IF RND(2) GOTO @UFO_LSET


1行追加されています。
今まではUFOが画面に出るまで60フレーム待ちました。
時間になると@UFO_RSETへジャンプしUFOが画面左端に表示され移動し始めます。

今回の変更でRND(2)により0から1までの乱数が発生します。
ここでUFOを右端から出すか、左端から出すかを決めているのです。
それの乱数を使うので右か左かは分かりません(ランダムです)。


②UFO表示の変更
右移動および左移動ともにUFOの表示を行っている部分が下記のように変更されています。

LOCATE UFO_X,UFO_Y
PRINT "A"

今までUFOは画面一番上の行を移動していましたが新たに変数UFO_Yを使いY座標を変更できるようにしました。


③UFOの画面端に到達したときの処理を変更

UFO_X = UFO_X + UFO_SX
IF UFO_X > 32 GOTO @UFO_DETH

右移動の部分で説明します。
右に移動し端に到達したか判定し端に来た場合、今までは反転していました。
そこを端まで来たらUFOを消しています。
要するにUFOは往復しません。


④移動の初期化を変更

@UFO_RSET
UFO_X = 0
UFO_Y = RND(10)
UFO_F = 1
RETURN

右移動および左移動の初期化部分で新たに変数UFO_Yの初期化を行っています。
Y方向の座標を与えていますがRND(10)により0から9までの乱数が代入されるのでUFOの位置をランダムに変更できます。


⑤UFOが画面に発生するまでの待ち時間処理の変更

@UFO_DETH
UFO_F = 0
UFO_CNT = RND(60)
RETURN

UFOがプレイヤの武器で破壊されたか画面移動で端に到達した場合、いったんUFOを消し新たに画面に登場させるまでの待ち時間を今まで60フレーム固定にしていました。
それを0から59フレームの乱数にしました。
これで現れるタイミングにばらつきができます。
チェックポイント
現在のサンプルではUFOがいつも画面右上から現れ、
左右を往復しています。
敵の動きとしてはあまりにも単調です。
そこで敵の動きに変化を与えてみましょう。
そんな時に使用するのが乱数です。
乱数とはどんなものか、
どうやって使うのかを単純なサンプルを使って説明します。


■プログラム
0001 @LOOP
0002 VSYNC 60
0003 CLS
0004 FOR I=1 TO 100
0005 X = RND(32)
0006 Y = RND(23)
0007 LOCATE X,Y
0008 PRINT "★";
0009 NEXT I
0010 GOTO @LOOP


まずはプログラムの解説です。
0007行目と0008行目は画面のX,Y位置に★を表示する命令です。
0005行目と0006行目は表示する位置であるXとYを指定します。
ここでRNDという関数が使われていますがこれについて後ほど説明します。

星を表示する命令を囲んでいるのがFOR文ですね。
このFOR文は繰り返して処理するための命令です。
FOR文にはバグがありますがこのような使い方をする限り問題はありません。
ここではIが1から始まり1づつ増加し100になるまで繰り返すという意味ですが、
今回の場合変数Iをループの中で使っていません。
要するに100回処理を繰り返すということになります。

処理内容は最初にVSYNC命令で60フレーム待ちます。
すでに説明したように60フレームは約1秒なのでここの意味は1秒間隔で処理が実行されることになります。

実際には画面をクリアしたあと、★を表示する命令を100回実行することになりますね。

そして0002行目から0010行目までを無限にループします。

『要するに1秒間隔で画面をクリアし100個の星を表示する処理を無限に繰り返す。』というものです。

実際に実行してみると分かると思いますが、
表示される★は画面の一定の位置でなくいろんな場所に表示されます。

ということは表示する位置が毎回違うということになります。
ここまでくると0005行目と0006行目がXとYをランダムに変化させていることが推測できると思います。

ゲームの中ではこの様にいつも決まった値でなくランダムな値が必要になる場合があります。
その際に使用するのがこのRND関数です。

ちなみにRND関数のRNDとはRANDOMの略です。
このRNDで作られたランダムな値を乱数といいます。

RND関数の書式は

変数=RND(乱数の最大値+1)

となります。
プチコンの説明では最大値となっていますが、最大値を含まないと表記されていますので、
こちらの方が分かりやすいと思います。

例えば0005行目では

X = RND(32)

とありましたが、これは0から32-1ようするに31までの範囲の乱数を返すという意味です。

画面の横方向32桁で表示座標は0から31でしたよね。
ですからこの乱数でX座標をランダムに作っています。

0006行目はY座標を作っています。
Y方向は24行あるのでY座標の範囲は0から23までです。
本来はRND関数では23と書くべきですがある理由でY座標は0から22までしか使用していません。
その理由はそのうち説明することになると思います。
チェックポイント
FOR文のバグ回避の対策済みサンプルプログラムです。


申し訳ありませんがしばらくの間行番号は付加しません。
行番号をつけるのがかなり面倒な作業になってきたので、
省略させていただきます。

■プログラム
' initialize
GOSUB @SYS_INI
GOSUB @PLY_INI
GOSUB @WPN_INI
GOSUB @UFO_INI
' game loop
@LOOP
GOSUB @SYS_MAIN
' task
GOSUB @PLY_TASK
GOSUB @WPN_TASK
GOSUB @UFO_TASK
GOTO @LOOP
'
' system
'
@SYS_INI
CLEAR
SYS_CNT = 0
SYS_KEY = BUTTON()
RETURN
'
@SYS_MAIN
VSYNC 1
CLS
SYS_CNT = SYS_CNT + 1
OLD_KEY = SYS_KEY
SYS_KEY = BUTTON()
TRG_KEY = SYS_KEY XOR OLD_KEY
ON_KEY = TRG_KEY AND SYS_KEY
RETURN
'
' UFO task
'
@UFO_INI
UFO_SX = 0.1
UFO_F = 0
UFO_CNT = 60
RETURN
'
@UFO_TASK
ON UFO_F GOTO @UFO_WAIT,@UFO_MVR,@UFO_MVL
STOP
'
@UFO_WAIT
UFO_CNT = UFO_CNT - 1
IF UFO_CNT > 0 THEN RETURN
GOTO @UFO_RSET
' move right
@UFO_MVR
LOCATE UFO_X, 0
PRINT "A"
UFO_X = UFO_X + UFO_SX
IF UFO_X > 32 GOTO @UFO_LSET
HITX = UFO_X
HITY = 0
GOSUB @WPN_HITC
IF HITF GOTO @UFO_DETH
RETURN
'
@UFO_LSET
UFO_X = 31
UFO_F = 2
RETURN
' move left
@UFO_MVL
LOCATE UFO_X, 0
PRINT "A"
UFO_X = UFO_X - UFO_SX
IF UFO_X < 0 GOTO @UFO_RSET
HITX = UFO_X
HITY = 0
GOSUB @WPN_HITC
IF HITF GOTO @UFO_DETH
RETURN
'
@UFO_RSET
UFO_X = 0
UFO_F = 1
RETURN
'
@UFO_DETH
UFO_F = 0
UFO_CNT = 60
RETURN
'
' player task
'
@PLY_INI
PLY_X = 15
PLY_SX = 0.5
PLY_F = 0
RETURN
'
@PLY_TASK
LOCATE PLY_X,21
PRINT "P"
IF ON_KEY AND 16 THEN GOSUB @WPN_FIRE
ON PLY_F GOTO @PLY_STOP,@PLY_MVR,@PLY_MVL
STOP
' stop
@PLY_STOP
IF SYS_KEY AND 8 GOTO @PLY1
IF SYS_KEY AND 4 GOTO @PLY2
RETURN
@PLY1
PLY_F = 1
RETURN
@PLY2
PLY_F = 2
RETURN
' move right
@PLY_MVR
PLY_X = PLY_X + PLY_SX
IF PLY_X > 31 THEN PLY_X = 31
IF SYS_KEY AND 8 THEN RETURN
PLY_F = 0
RETURN
' move left
@PLY_MVL
PLY_X = PLY_X - PLY_SX
IF PLY_X < 0 THEN PLY_X = 0
IF SYS_KEY AND 4 THEN RETURN
PLY_F = 0
RETURN
'
' weapon task
'
@WPN_INI
WPN_MAX = 3
DIM WPN_F(WPN_MAX)
DIM WPN_X(WPN_MAX)
DIM WPN_Y(WPN_MAX)
FOR I=0 TO WPN_MAX-1
WPN_F(i) = 0
NEXT I
WPN_SY = 0.5
RETURN
'
@WPN_TASK
FOR WPN_I=0 TO WPN_MAX-1
GOSUB @WPN_SUB
NEXT WPN_I
RETURN
'
@WPN_SUB
ON WPN_F(WPN_I) GOTO @WPN_WAIT,@WPN_MOVE
STOP
'
@WPN_WAIT
RETURN
'
@WPN_MOVE
LOCATE WPN_X(WPN_I),WPN_Y(WPN_I)
PRINT "!"
WPN_Y(WPN_I) = WPN_Y(WPN_I) + WPN_SY
IF WPN_Y(WPN_I) >= 0 THEN RETURN
WPN_F(WPN_I) = 0
RETURN
'
@WPN_FIRE
GOSUB @WPN_GET
IF WPN_N < 0 THEN RETURN
BEEP 10
WPN_F(WPN_N) = 1
WPN_X(WPN_N) = PLY_X
WPN_Y(WPN_N) = 20
RETURN
'
@WPN_HITC
HITF = 0

'FOR WPN_N=0 TO WPN_MAX-1
WPN_N=0
@LBL003

IF WPN_F(WPN_N) == 0 GOTO @WPN_NEXT
IF FLOOR(WPN_X(WPN_N)) != FLOOR(HITX) GOTO @WPN_NEXT

IF FLOOR(WPN_Y(WPN_N)) == FLOOR(HITY) GOTO @WPN_HOK

@WPN_NEXT

'NEXT WPN_N
IF WPN_N==WPN_MAX-1 THEN RETURN
WPN_N=WPN_N+1
GOTO @LBL003

'
@WPN_HOK
HITF = 1
'
BEEP 13
WPN_F(WPN_N) = 0
RETURN
'
@WPN_GET

'FOR WPN_N=0 TO WPN_MAX-1
WPN_N=0
@LBL001

IF WPN_F(WPN_N)==0 THEN RETURN

'NEXT WPN_N
IF WPN_N==WPN_MAX-1 GOTO @LBL002
WPN_N=WPN_N+1
GOTO @LBL001
@LBL002

WPN_N = -1
RETURN
'End of line