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

りょうのゲーム塾

再開、準備中。

チェックポイント
【注意】この記事は前回のサンプルのヒントとなる記事です。
同じ敵の複製(問題編)を先に呼んでくださいね^^


プログラムで扱うデータには同じ種類のデータが複数存在する場合があります。
そのときに使用するのが配列です。
では配列についてサンプルで学んでみましょう。


ここでは2つのサンプルをみていただきます。
内容は3つの商品の金額を入力し合計金額を表示するプログラムです。
ただし今回の場合は少し条件があります。
プログラムはいろんなことができるのでなにも配列が必要なわけではありまあせん。
あくまでも配列の使用方法を確認するためのサンプルなので3つの商品の金額を入力したあとに合計金額を計算するものと限定します。

では配列を使わないサンプルプログラムです

■プログラム
0001 ' sample
0002 INPUT KINGAKU1
0003 INPUT KINGAKU2
0004 INPUT KINGAKU3
0005 GOUKEI = KINGAKU1 + KINGAKU2 + KINGAKU3
0006 PRINT GOUKEI
0007 END


確かに大した苦労もいりません。
商品の金額を入力する変数に金額を入力し合計した後で表示する。
簡単なものです。

ではもし商品が3個じゃなく1000個だったら?
どうします?
1000個も変数に入力して合計しますか?

たぶんこの時点でほとんどの人が面倒だと感じるでしょう。

コンピュタでの作業にはこの様な作業の方が多いのです。
そのために配列があります。

配列とは同じ種類の変数を複数作成し利用するものです。
例えば今回は商品の金額がそうですよね。

配列は普段の変数と違うので予め使用することを宣言する必要があります。
宣言方法は下記の通りです。

DIM 変数名(必要数)

今回の場合は商品の金額を KINGAKU1,KINGAKU2,KUNGAKU3 に入力していましたがこれはすべて商品の金額という同じ種類の値でした。
しかし変数名がすべて違うので面倒だったのです。
そこで配列は同じ名前の変数を必要な数だけ作成してくれます。
今回は金額が3つ必要なので宣言は下記のようになります。

DIM KINGAKU(3)

宣言した配列を使うのは変数の後にかっこをつけて配列の何番目を使うのかを記述します。
上記の場合 KINGAKU という変数が3つ作られました。
使用できる変数は下記の3つです。

KINGAKU(0)
KINGAKU(1)
KINGAKU(2)

コンピュータの場合順番は0から始まります。
3つあるんで0番目、1番目、2番目の3つとなります。

ではこの配列を使用した場合サンプルはどう変わるでしょうか?

■プログラム
0001 ' sample 2
0002 CLEAR
0003 DIM KINGAKU(3)
0004 INPUT KINGAKU(0)
0005 INPUT KINGAKU(1)
0006 INPUT KINGAKU(2)
0007 GOUKEI = KINGAKU(0) + KINGAKU(1) + KINGAKU(2)
0008 PRINT GOUKEI
0009 END


0002行目の CLEAR は配列を使用したときに2回目以降でエラーがでます。これはプチコンの仕様のようです。
私は始めて経験しました。

配列を宣言し各配列に金額を入力し合計を計算します。

ん?
最初のサンプルとあまり変わらないし、逆に面倒になったんでは?

このままだとそうなりますね。

そこでこのサンプルは進化します。

■プログラム
0001 ' sample 2
0002 CLEAR
0003 DIM KINGAKU(3)
0004 FOR I=0 TO 2
0005 INPUT KINGAKU(I)
0006 NEXT I
0007 GOUKEI = 0
0008 FOR I=0 TO 2
0009 GOUKEI = GOUKEI + KINGAKU(I)
0010 NEXT I
0011 PRINT GOUKEI
0012 END


ここではループ処理でよく使われる FOR 文が使われています。
書式は

FOR 変数名=初期値 TO 終値 STEP 増分

  繰り返す処理

NEXT 変数名

使用する変数名はループの際のインデックスとして使用できます。
NEXT の後に書かれる変数名は当然 FOR 文で記述した変数名と同じです。

0004 FOR I=0 TO 2
0005 INPUT KINGAKU(I)
0006 NEXT I


これは変数Iの値が0から2まで1ずつ増えていくという意味です。
実際には

最初のループでは I の値は0です。
次のループでは I の値は1です。
次のループでは I の値は2です。
I が終値になったのでループは終了です。

言い換えれば

最初のループでは I の値は0です。
 KINGAKU(0)の入力をします。
次のループでは I の値は1です。
 KINGAKU(1)の入力をします。
次のループでは I の値は2です。
 KINGAKU(2)の入力をします。
I が終値になったのでループは終了です。

後の FOR 文も同様です。

ここまで来るとなんとなくわかったかもしれません。
少々書き方が面倒にはなりましたが FOR 文によるループを使えば同じ処理が複数回実行できるということです。

例えばこのサンプルで1000個の商品を計算する場合は配列を1000個にしループの終値を999に変更すればいいだけです。

ここまでくればこのやり方がいい方法であることが理解できたのでは?
チェックポイント
このままではゲームとしては寂しいものがあります。
そこで敵を増やしてみたいと思います。
ただ、敵のプログラムを今までと同様に作っていくのは大変なので同じ種類の敵を複数だすことを考えてみます。
ただその場合に重要なプログラムの要素である配列を知っておかないといけません。
そこで今回はシンプルなプログラムを使い配列に挑戦してみましょう。


では下記のプログラムは実行するとどうなるでしょうか?

■プログラム
0001 ' initialize
0002 GOSUB @TMR_INI
0003 GOSUB @TK1_INI
0004 @LOOP
0005 VSYNC 1
0006 CLS
0007 GOSUB @TMR_TASK
0008 GOSUB @TK1_TASK
0009 GOTO @LOOP
0010 '
0011 @TMR_INI
0012 TMR_INT = 2*60
0013 TMR_C = TMR_INT
0014 RETURN
0015 '
0016 @TMR_TASK
0017 TMR_C = TMR_C - 1
0018 IF TMR_C >= 0 THEN RETURN
0019 TMR_C = TMR_INT
0020 '
0021 IF TK1_F THEN RETURN
0022 TK1_F = 1
0023 TK1_X = 0
0024 TK1_Y = 0
0025 RETURN
0026 '
0027 @TK1_INI
0028 TK1_F = 0
0029 RETURN
0030 '
0031 @TK1_TASK
0032 ON TK1_F GOTO @TK1_WAIT,@TK1_MOVE
0033 STOP
0034 '
0035 @TK1_WAIT
0036 RETURN
0037 '
0038 @TK1_MOVE
0039 LOCATE TK1_X,TK1_Y
0040 PRINT "@"
0041 TK1_Y = TK1_Y + 0.1
0042 IF TK1_Y < 23 THEN RETURN
0043 TK1_F = 0
0044 RETURN
0945 'End of line


このプログラムは今までのプログラムとほとんど同じです。
プログラムを入力しながらその部分が何をしているのかを考えてください。
今回は一度考えていただきたいのでしばらくしてから更新します。

では問題ですがこのプログラムでは敵が画面に1体だけしか現れません。
同じ敵を画面に2体以上出すにはどうしたらいいでしょうか?

チェックポイント
武器の発射ができたら今度は当たり判定です。
今回のゲームは敵キャラや武器キャラがキャラクタ単位で移動し表示されています。
表示に関しては実際には小数部を持つ実数での移動なんですが表示座標が整数なので小数部分が切り捨てられています。
この場合の当たり判定は敵と武器の位置が一致しているかを判断すればいいのです。


■プログラム
0001 ' initialize
0002 GOSUB @SYS_INI
0003 GOSUB @PLY_INI
0004 GOSUB @WPN_INI
0005 GOSUB @UFO_INI
0006 ' game loop
0007 @LOOP
0008 GOSUB @SYS_MAIN
0009 ' task
0010 GOSUB @PLY_TASK
0011 GOSUB @WPN_TASK
0012 GOSUB @UFO_TASK
0013 GOTO @LOOP
0014 '
0015 ' system
0016 '
0017 @SYS_INI
0018 SYS_CNT = 0
0019 RETURN
0020 '
0021 @SYS_MAIN
0022 VSYNC 1
0023 CLS
0024 SYS_CNT = SYS_CNT + 1
0025 SYS_KEY = BUTTON()
0026 RETURN
0027 '
0028 ' UFO task
0029 '
0030 @UFO_INI
0031 UFO_SX = 0.1
0032 UFO_F = 0
0033 UFO_CNT = 60
0034 RETURN
0035 '
0036 @UFO_TASK
0037 ON UFO_F GOTO @UFO_WAIT,@UFO_MVR,@UFO_MVL
0038 STOP
0039 '
0040 @UFO_WAIT
0041 UFO_CNT = UFO_CNT + 1
0042 IF UFO_CNT > 0 THEN RETURN
0043 GOTO @UFO_RSET
0044 ' move right
0045 @UFO_MVR
0046 LOCATE UFO_X, 0
0047 PRINT "A"
0048 UFO_X = UFO_X + UFO_SX
0049 IF UFO_X > 32 GOTO @UFO_LSET
0050 HITX = UFO_X
0051 HITY = 0
0052 GOSUB @WPN_HITC
0053 IF HITF GOTO @UFO_DETH
0054 RETURN
0055 '
0056 @UFO_LSET
0057 UFO_X = 31
0058 UFO_F = 2
0059 RETURN
0060 ' move left
0061 @UFO_MVL
0062 LOCATE UFO_X, 0
0063 PRINT "A"
0064 UFO_X = UFO_X - UFO_SX
0065 IF UFO_X < 0 GOTO @UFO_RSET
0066 HITX = UFO_X
0067 HITY = 0
0068 GOSUB @WPN_HITC
0069 IF HITF GOTO @UFO_DETH
0070 RETURN
0071 '
0072 @UFO_RSET
0073 UFO_X = 0
0074 UFO_F = 1
0075 RETURN
0076 '
0077 @UFO_DETH
0078 UFO_F = 0
0079 UFO_CNT = 60
0080 RETURN
0081 '
0082 ' player task
0083 '
0084 @PLY_INI
0085 PLY_X = 15
0086 PLY_SX = 0.5
0087 PLY_F = 0
0088 RETURN
0089 '
0090 @PLY_TASK
0091 LOCATE PLY_X,21
0092 PRINT "P"
0093 IF SYS_KEY AND 16 THEN GOSUB @WPN_FIRE
0094 ON PLY_F GOTO @PLY_STOP,@PLY_MVR,@PLY_MVL
0095 STOP
0096 ' stop
0097 @PLY_STOP
0098 IF SYS_KEY AND 8 GOTO @PLY1
0099 IF SYS_KEY AND 4 GOTO @PLY2
0100 RETURN
0101 @PLY1
0102 PLY_F = 1
0103 RETURN
0104 @PLY2
0105 PLY_F = 2
0106 RETURN
0107 ' move right
0108 @PLY_MVR
0109 PLY_X = PLY_X + PLY_SX
0110 IF PLY_X > 31 THEN PLY_X = 31
0111 IF SYS_KEY AND 8 THEN RETURN
0112 PLY_F = 0
0113 RETURN
0114 ' move left
0115 @PLY_MVL
0116 PLY_X = PLY_X - PLY_SX
0117 IF PLY_X < 0 THEN PLY_X = 0
0118 IF SYS_KEY AND 4 THEN RETURN
0119 PLY_F = 0
0120 RETURN
0121 '
0122 ' weapon task
0123 '
0124 @WPN_INI
0125 WPN_F = 0
0126 WPN_SY = 0.5
0127 RETURN
0128 '
0129 @WPN_TASK
0130 ON WPN_F GOTO @WPN_WAIT,@WPN_MOVE
0131 STOP
0132 '
0133 @WPN_WAIT
0134 RETURN
0135 '
0136 @WPN_MOVE
0137 LOCATE WPN_X,WPN_Y
0138 PRINT "!"
0139 WPN_Y = WPN_Y + WPN_SY
0140 IF WPN_Y >= 0 THEN RETURN
0141 WPN_F = 0
0142 RETURN
0143 '
0144 @WPN_FIRE
0145 IF WPN_F THEN RETURN
0146 BEEP 10
0147 WPN_F = 1
0148 WPN_X = PLY_X
0149 WPN_Y = 20
0150 RETURN
0151 '
0152 @WPN_HITC
0153 HITF = 0
0154 IF WPN_F == 0 THEN RETURN
0155 IF FLOOR(WPN_X) != FLOOR(HITX) THEN RETURN
0156 IF FLOOR(WPN_Y) != FLOOR(HITY) THEN RETURN
0157 HITF = 1
0158 '
0159 BEEP 13
0160 WPN_F = 0
0161 RETURN
0162 'End of line


では変更部分の説明を行います。

0002 GOSUB @SYS_INI
0003 GOSUB @PLY_INI
0004 GOSUB @WPN_INI
0005 GOSUB @UFO_INI
0006 ' game loop
0007 @LOOP
0008 GOSUB @SYS_MAIN
0009 ' task
0010 GOSUB @PLY_TASK
0011 GOSUB @WPN_TASK
0012 GOSUB @UFO_TASK
0013 GOTO @LOOP


まずメインでの変更はUFOのタスクの実行を最後に実行しています。
これは今回の当たり判定を行ううえで武器とその当たる相手(敵)の実行がこの順番の方が都合が良かったからです。
しかし実行の順番はゲームループのなかのタスクを呼んでいる命令を入れ替えればいいだけです。
サブルーチンでなかったらこの入れ替えは大変なことになっていたかもしれませんね。
ちなみに初期化の部分は入れ替えなくても問題は無かったのですが私の血液型がA型のせいか揃えています。

0017 @SYS_INI
0018 SYS_CNT = 0
0019 RETURN
0020 '
0021 @SYS_MAIN
0022 VSYNC 1
0023 CLS
0024 SYS_CNT = SYS_CNT + 1
0025 SYS_KEY = BUTTON()
0026 RETURN


システム部分は変更はありませんが、前回の内容についての確認のために説明します。
初期化の部分では SYS_CNT を0で初期化してあります。
これは今後このゲーム中で時間の概念を入れる際のストップウォッチのようなものです。
ここでストップウォッチをスタートしたようなものです。
@SYS_MAIN はゲームループ内から呼び出されます。
SYS_CNT が+1されカウントされています。

0030 @UFO_INI
0031 UFO_SX = 0.1
0032 UFO_F = 0
0033 UFO_CNT = 60
0034 RETURN
0035 '
0036 @UFO_TASK
0037 ON UFO_F GOTO @UFO_WAIT,@UFO_MVR,@UFO_MVL
0038 STOP


UFO部分は少々変更が多いので重要な点だけ解説します。
今までUFOはいきなり画面に現れましたが少しだけ時間を空けて(間を取って)現れるように変更しました。
今までUFOは右へ移動しその後左に移動する、これらを繰り返したわけですが画面に出る前に待つという状態も追加されました。
そこでプレイヤと同じようなプログラムへ変更されました。
UFO_CNT はUFOが画面現れるまでの待ち時間を表します。
今回60という値が代入されていますがこれはどういう意味かわかりますか?

0040 @UFO_WAIT
0041 UFO_CNT = UFO_CNT - 1
0042 IF UFO_CNT > 0 THEN RETURN
0043 GOTO @UFO_RSET


実際にUFOが待つタスクです。
UFO_CNT から1ずつ引いて0でなければ今のままですね。
0になると @UFO_RSET へジャンプしますがこれはただ単に今までの画面左上から右上まで移動するタスクへと変更しているだけです。

すると・・・

そうです1秒間待つのです。
ゲームループを60回待つと約1秒になるからです。
今後、この時間の概念は忘れないこと!!

0045 @UFO_MVR
0046 LOCATE UFO_X, 0
0047 PRINT "A"

0048 UFO_X = UFO_X + UFO_SX
0049 IF UFO_X > 32 GOTO @UFO_LSET
0050 HITX = UFO_X
0051 HITY = 0
0052 GOSUB @WPN_HITC
0053 IF HITF GOTO @UFO_DETH

0054 RETURN
0055 '
0056 @UFO_LSET
0057 UFO_X = 31
0058 UFO_F = 2
0059 RETURN
0060 ' move left
0061 @UFO_MVL
0062 LOCATE UFO_X, 0
0063 PRINT "A"

0064 UFO_X = UFO_X - UFO_SX
0065 IF UFO_X < 0 GOTO @UFO_RSET
0066 HITX = UFO_X
0067 HITY = 0
0068 GOSUB @WPN_HITC
0069 IF HITF GOTO @UFO_DETH

0070 RETURN
0071 '
0072 @UFO_RSET
0073 UFO_X = 0
0074 UFO_F = 1
0075 RETURN


UFOの移動部分はかなり変更されているようにみえますがそうでもありません。
UFOの表示が移動してきました。
これは画面に表示せずに待つタスクが追加されたのでここで表示しないと都合が悪くなったからです。
右も左も同様です。
IF 文は判定が逆になりました。
画面端にきたら右移動は左移動へジャンプ、左移動は右移動へジャンプしています。
では追加されたのは?

0066 HITX = UFO_X
0067 HITY = 0
0068 GOSUB @WPN_HITC
0069 IF HITF GOTO @UFO_DETH


これは当たり判定のサブルーチンを呼び出しています。
呼び出す前に今回当たり判定するための位置データを HITX および HITY にコピーしています。
なぜコピーするかはおいおいわかってきますのでここでは気にしないでください。
例えば上のプログラムの場合当たり判定したい対象、今回はUFOなのでUFOのX座標とY座標を代入しています。
@WPN _HITC は武器のヒット(当たり)をチェックするサブルーチンです。
その結果が HITF に代入されて返ってきます。
HITF が0でないときUFOが消滅するプログラムへジャンプします。

0077 @UFO_DETH
0078 UFO_F = 0
0079 UFO_CNT = 60
0080 RETURN


再びUFOを発生させるためフラグを0にしカウントを60にし60フレーム待つタスクへジャンプすることになります。
タスクのジャンプは他のプログラムのように GOTO 文は使いません。
ですのでジャンプという呼び方は不適当です。

今後はタスクが移行すると呼ぶことにします。

0144 @WPN_FIRE
0145 IF WPN_F THEN RETURN
0146 BEEP 10
0147 WPN_F = 1
0148 WPN_X = PLY_X
0149 WPN_Y = 20
0150 RETURN


武器の発射ルーチンはデバッグの際にわかりやすいように発射音を鳴らしました。

0152 @WPN_HITC
0153 HITF = 0
0154 IF WPN_F == 0 THEN RETURN
0155 IF FLOOR(WPN_X) != FLOOR(HITX) THEN RETURN
0156 IF FLOOR(WPN_Y) != FLOOR(HITY) THEN RETURN
0157 HITF = 1
0158 '
0159 BEEP 13
0160 WPN_F = 0
0161 RETURN


最後に当たり判定のルーチンです。
サブルーチンの実行前に HITX および HITY に当たり判定の対象の座標が入っているのでこれと武器の座標を比べます。
最初に HITF に0を代入しているのは当たっていない条件を判断してその場合に終了するため最初は判定はNGの意味で0が代入されています。
0154行目は武器フラグが0のときは発射されていないのでNGで戻ります。
0155行目は武器のX座標と対象のX座標が違う場合NGとなります。
0156行目は同様にY座標の場合です。
ここまで来るとすべての条件がOKだったので当たったと判断しています。
当たったら武器は発射前の待ちタスクへ移行します。

さあ、どうでしょうか?
理解できましたか?

ちなみに座標の判定を行っている部分で FLOOR という関数を使っています。
これは引数(かっこの中に書いた変数の値)の小数部分を切り捨てた値、要するに整数部分を返します。

なぜだかわかりますよね?