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

りょうのゲーム塾

再開、準備中。

チェックポイント
今回はキー入力に関する説明を行う際に論理演算について説明が必要になります。
論理演算の説明の前にプログラムの修正を行います。


■プログラム
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 CLEAR
0019 SYS_CNT = 0
0020 SYS_KEY = BUTTON()
0021 RETURN
0022 '
0023 @SYS_MAIN
0024 VSYNC 1
0025 CLS
0026 SYS_CNT = SYS_CNT + 1
0027 OLD_KEY = SYS_KEY
0028 SYS_KEY = BUTTON()
0029 TRG_KEY = SYS_KEY XOR OLD_KEY
0030 ON_KEY = TRG_KEY AND SYS_KEY
0031 RETURN
0032 '
0033 ' UFO task
0034 '
0035 @UFO_INI
0036 UFO_SX = 0.1
0037 UFO_F = 0
0038 UFO_CNT = 60
0039 RETURN
0040 '
0041 @UFO_TASK
0042 ON UFO_F GOTO @UFO_WAIT,@UFO_MVR,@UFO_MVL
0043 STOP
0044 '
0045 @UFO_WAIT
0046 UFO_CNT = UFO_CNT + 1
0047 IF UFO_CNT > 0 THEN RETURN
0048 GOTO @UFO_RSET
0049 ' move right
0050 @UFO_MVR
0051 LOCATE UFO_X, 0
0052 PRINT "A"
0053 UFO_X = UFO_X + UFO_SX
0054 IF UFO_X > 32 GOTO @UFO_LSET
0055 HITX = UFO_X
0056 HITY = 0
0057 GOSUB @WPN_HITC
0058 IF HITF GOTO @UFO_DETH
0059 RETURN
0060 '
0061 @UFO_LSET
0062 UFO_X = 31
0063 UFO_F = 2
0064 RETURN
0065 ' move left
0066 @UFO_MVL
0067 LOCATE UFO_X, 0
0068 PRINT "A"
0069 UFO_X = UFO_X - UFO_SX
0070 IF UFO_X < 0 GOTO @UFO_RSET
0071 HITX = UFO_X
0072 HITY = 0
0073 GOSUB @WPN_HITC
0074 IF HITF GOTO @UFO_DETH
0075 RETURN
0076 '
0077 @UFO_RSET
0078 UFO_X = 0
0079 UFO_F = 1
0080 RETURN
0081 '
0082 @UFO_DETH
0083 UFO_F = 0
0084 UFO_CNT = 60
0085 RETURN
0086 '
0087 ' player task
0088 '
0089 @PLY_INI
0090 PLY_X = 15
0091 PLY_SX = 0.5
0092 PLY_F = 0
0093 RETURN
0094 '
0095 @PLY_TASK
0096 LOCATE PLY_X,21
0097 PRINT "P"
0098 IF ON_KEY AND 16 THEN GOSUB @WPN_FIRE
0099 ON PLY_F GOTO @PLY_STOP,@PLY_MVR,@PLY_MVL
0100 STOP
0101 ' stop
0102 @PLY_STOP
0103 IF SYS_KEY AND 8 GOTO @PLY1
0104 IF SYS_KEY AND 4 GOTO @PLY2
0105 RETURN
0106 @PLY1
0107 PLY_F = 1
0108 RETURN
0109 @PLY2
0110 PLY_F = 2
0111 RETURN
0112 ' move right
0113 @PLY_MVR
0114 PLY_X = PLY_X + PLY_SX
0115 IF PLY_X > 31 THEN PLY_X = 31
0116 IF SYS_KEY AND 8 THEN RETURN
0117 PLY_F = 0
0118 RETURN
0119 ' move left
0120 @PLY_MVL
0121 PLY_X = PLY_X - PLY_SX
0122 IF PLY_X < 0 THEN PLY_X = 0
0123 IF SYS_KEY AND 4 THEN RETURN
0124 PLY_F = 0
0125 RETURN
0126 '
0127 ' weapon task
0128 '
0129 @WPN_INI
0130 WPN_MAX = 3
0131 DIM WPN_F(WPN_MAX)
0132 DIM WPN_X(WPN_MAX)
0133 DIM WPN_Y(WPN_MAX)
0134 FOR I=0 TO WPN_MAX-1
0135 WPN_F(i) = 0
0136 NEXT I
0137 WPN_SY = 0.5
0138 RETURN
0139 '
0140 @WPN_TASK
0141 FOR WPN_I=0 TO WPN_MAX-1
0142 GOSUB @WPN_SUB
0143 NEXT WPN_I
0144 RETURN
0145 '
0146 @WPN_SUB
0147 ON WPN_F(WPN_I) GOTO @WPN_WAIT,@WPN_MOVE
0148 STOP
0149 '
0150 @WPN_WAIT
0151 RETURN
0152 '
0153 @WPN_MOVE
0154 LOCATE WPN_X(WPN_I),WPN_Y(WPN_I)
0155 PRINT "!"
0156 WPN_Y(WPN_I) = WPN_Y(WPN_I) + WPN_SY
0157 IF WPN_Y(WPN_I) >= 0 THEN RETURN
0158 WPN_F(WPN_I) = 0
0159 RETURN
0160 '
0161 @WPN_FIRE
0162 GOSUB @WPN_GET
0163 IF WPN_N < 0 THEN RETURN
0164 BEEP 10
0165 WPN_F(WPN_N) = 1
0166 WPN_X(WPN_N) = PLY_X
0167 WPN_Y(WPN_N) = 20
0168 RETURN
0169 '
0170 @WPN_HITC
0171 HITF = 0
0172 FOR WPN_N=0 TO WPN_MAX-1
0173 IF WPN_F(WPN_N) == 0 GOTO @WPN_NEXT
0174 IF FLOOR(WPN_X(WPN_N)) != FLOOR(HITX) GOTO @WPN_NEXT
0175 IF FLOOR(WPN_Y(WPN_N)) != FLOOR(HITY) GOTO @WPN_NEXT
0176 GOTO @WPN_HOK
0177 @WPN_NEXT
0178 NEXT WPN_N
0179 RETURN
0180 '
0181 @WPN_HOK
0182 HITF = 1
0183 '
0184 BEEP 13
0185 WPN_F(WPN_N) = 0
0186 RETURN
0187 '
0188 @WPN_GET
0189 FOR WPN_N=0 TO WPN_MAX-1
0190 IF WPN_F(WPN_N)==0 THEN RETURN
0191 NEXT WPN_N
0192 WPN_N = -1
0193 RETURN
0194 'End of line


プログラムの解説は今回は行いません。
変更部分によりうまく武器の発射ができるようになったと思います。
チェックポイント
同じ処理を行うプログラムを書く場合は必要な変数を配列にしておけばFORループなどの繰り返し処理で実行できます。
現在のプレイヤは武器が最高画面内1個しか使用できないので少々敵に当たりづらいので最高画面内3個まで増やしてあげましょう。


■プログラム
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 CLEAR
0019 SYS_CNT = 0
0020 RETURN
0021 '
0022 @SYS_MAIN
0023 VSYNC 1
0024 CLS
0025 SYS_CNT = SYS_CNT + 1
0026 SYS_KEY = BUTTON()
0027 RETURN
0028 '
0029 ' UFO task
0030 '
0031 @UFO_INI
0032 UFO_SX = 0.1
0033 UFO_F = 0
0034 UFO_CNT = 60
0035 RETURN
0036 '
0037 @UFO_TASK
0038 ON UFO_F GOTO @UFO_WAIT,@UFO_MVR,@UFO_MVL
0039 STOP
0040 '
0041 @UFO_WAIT
0042 UFO_CNT = UFO_CNT + 1
0043 IF UFO_CNT > 0 THEN RETURN
0044 GOTO @UFO_RSET
0045 ' move right
0046 @UFO_MVR
0047 LOCATE UFO_X, 0
0048 PRINT "A"
0049 UFO_X = UFO_X + UFO_SX
0050 IF UFO_X > 32 GOTO @UFO_LSET
0051 HITX = UFO_X
0052 HITY = 0
0053 GOSUB @WPN_HITC
0054 IF HITF GOTO @UFO_DETH
0055 RETURN
0056 '
0057 @UFO_LSET
0058 UFO_X = 31
0059 UFO_F = 2
0060 RETURN
0061 ' move left
0062 @UFO_MVL
0063 LOCATE UFO_X, 0
0064 PRINT "A"
0065 UFO_X = UFO_X - UFO_SX
0066 IF UFO_X < 0 GOTO @UFO_RSET
0067 HITX = UFO_X
0068 HITY = 0
0069 GOSUB @WPN_HITC
0070 IF HITF GOTO @UFO_DETH
0071 RETURN
0072 '
0073 @UFO_RSET
0074 UFO_X = 0
0075 UFO_F = 1
0076 RETURN
0077 '
0078 @UFO_DETH
0079 UFO_F = 0
0080 UFO_CNT = 60
0081 RETURN
0082 '
0083 ' player task
0084 '
0085 @PLY_INI
0086 PLY_X = 15
0087 PLY_SX = 0.5
0088 PLY_F = 0
0089 RETURN
0090 '
0091 @PLY_TASK
0092 LOCATE PLY_X,21
0093 PRINT "P"
0094 IF SYS_KEY AND 16 THEN GOSUB @WPN_FIRE
0095 ON PLY_F GOTO @PLY_STOP,@PLY_MVR,@PLY_MVL
0096 STOP
0097 ' stop
0098 @PLY_STOP
0099 IF SYS_KEY AND 8 GOTO @PLY1
0100 IF SYS_KEY AND 4 GOTO @PLY2
0101 RETURN
0102 @PLY1
0103 PLY_F = 1
0104 RETURN
0105 @PLY2
0106 PLY_F = 2
0107 RETURN
0108 ' move right
0109 @PLY_MVR
0110 PLY_X = PLY_X + PLY_SX
0111 IF PLY_X > 31 THEN PLY_X = 31
0112 IF SYS_KEY AND 8 THEN RETURN
0113 PLY_F = 0
0114 RETURN
0115 ' move left
0116 @PLY_MVL
0117 PLY_X = PLY_X - PLY_SX
0118 IF PLY_X < 0 THEN PLY_X = 0
0119 IF SYS_KEY AND 4 THEN RETURN
0120 PLY_F = 0
0121 RETURN
0122 '
0123 ' weapon task
0124 '
0125 @WPN_INI
0126 WPN_MAX = 3
0127 DIM WPN_F(WPN_MAX)
0128 DIM WPN_X(WPN_MAX)
0129 DIM WPN_Y(WPN_MAX)
0130 FOR I=0 TO WPN_MAX-1
0131 WPN_F(i) = 0
0132 NEXT I
0133 WPN_SY = 0.5
0134 RETURN
0135 '
0136 @WPN_TASK
0137 FOR WPN_I=0 TO WPN_MAX-1
0138 GOSUB @WPN_SUB
0139 NEXT WPN_I
0140 RETURN
0141 '
0142 @WPN_SUB
0143 ON WPN_F(WPN_I) GOTO @WPN_WAIT,@WPN_MOVE
0144 STOP
0145 '
0146 @WPN_WAIT
0147 RETURN
0148 '
0149 @WPN_MOVE
0150 LOCATE WPN_X(WPN_I),WPN_Y(WPN_I)
0151 PRINT "!"
0152 WPN_Y(WPN_I) = WPN_Y(WPN_I) + WPN_SY
0153 IF WPN_Y(WPN_I) >= 0 THEN RETURN
0154 WPN_F(WPN_I) = 0
0155 RETURN
0156 '
0157 @WPN_FIRE
0158 GOSUB @WPN_GET
0159 IF WPN_N < 0 THEN RETURN
0160 BEEP 10
0161 WPN_F(WPN_N) = 1
0162 WPN_X(WPN_N) = PLY_X
0163 WPN_Y(WPN_N) = 20
0164 RETURN
0165 '
0166 @WPN_HITC
0167 HITF = 0
0168 FOR WPN_N=0 TO WPN_MAX-1
0169 IF WPN_F(WPN_N) == 0 GOTO @WPN_NEXT
0170 IF FLOOR(WPN_X(WPN_N)) != FLOOR(HITX) GOTO @WPN_NEXT
0171 IF FLOOR(WPN_Y(WPN_N)) != FLOOR(HITY) GOTO @WPN_NEXT
0172 GOTO @WPN_HOK
0173 @WPN_NEXT
0174 NEXT WPN_N
0175 RETURN
0176 '
0177 @WPN_HOK
0178 HITF = 1
0179 '
0180 BEEP 13
0181 WPN_F(WPN_N) = 0
0182 RETURN
0183 '
0184 @WPN_GET
0185 FOR WPN_N=0 TO WPN_MAX-1
0186 IF WPN_F(WPN_N)==0 THEN RETURN
0187 NEXT WPN_N
0188 WPN_N = -1
0189 RETURN
0190 'End of line


では解説です。

0017 @SYS_INI
0018 CLEAR
0019 SYS_CNT = 0
0020 RETURN

これはプチコンの独特な仕様だと考えています。
本来はありえないことです。
通常のベーシックではRUNコマンドで実行すると変数は初期化されるのでこのCLEAR命令は必要ありません。
私の知っている限りのベーシックではこの必要はありません。
しかしプチコンはエラーがでるので仕方なくCLEARしておきます。
今後も必ずここでCLEARしておけばいいでしょう。

0125 @WPN_INI
0126 WPN_MAX = 3
0127 DIM WPN_F(WPN_MAX)
0128 DIM WPN_X(WPN_MAX)
0129 DIM WPN_Y(WPN_MAX)
0130 FOR I=0 TO WPN_MAX-1
0131 WPN_F(i) = 0
0132 NEXT I
0133 WPN_SY = 0.5
0134 RETURN

武器の複製を行います。
武器の使う変数を配列宣言します。
必要数をWPN_MAXに代入して使っているのには利点があるからです。
さてその利点とはなんでしょうか?
考えてみてくださいね!

配列宣言の後は武器の重要な変数であるフラグを0に初期化します。

0136 @WPN_TASK
0137 FOR WPN_I=0 TO WPN_MAX-1
0138 GOSUB @WPN_SUB
0139 NEXT WPN_I
0140 RETURN

本来の武器タスクのプログラムは@WPN_SUBへ移動します。
この部分は必要数分の武器タスクを実行するループに変更してあります。

0142 @WPN_SUB
0143 ON WPN_F(WPN_I) GOTO @WPN_WAIT,@WPN_MOVE
0144 STOP
0145 '
0146 @WPN_WAIT
0147 RETURN
0148 '
0149 @WPN_MOVE
0150 LOCATE WPN_X(WPN_I),WPN_Y(WPN_I)
0151 PRINT "!"
0152 WPN_Y(WPN_I) = WPN_Y(WPN_I) + WPN_SY
0153 IF WPN_Y(WPN_I) >= 0 THEN RETURN
0154 WPN_F(WPN_I) = 0
0155 RETURN

今までの武器プログラムですが変数が配列になっています。

0157 @WPN_FIRE
0158 GOSUB @WPN_GET
0159 IF WPN_N < 0 THEN RETURN
0160 BEEP 10
0161 WPN_F(WPN_N) = 1
0162 WPN_X(WPN_N) = PLY_X
0163 WPN_Y(WPN_N) = 20
0164 RETURN

武器の発射ルーチンですが@WPN_GETが発射可能な武器を探します。
WPN_Nに-1が入っている場合には発射できる武器が無かったことになります。
0~2の場合にはその番号の武器が発射できます。

0166 @WPN_HITC
0167 HITF = 0
0168 FOR WPN_N=0 TO WPN_MAX-1
0169 IF WPN_F(WPN_N) == 0 GOTO @WPN_NEXT
0170 IF FLOOR(WPN_X(WPN_N)) != FLOOR(HITX) GOTO @WPN_NEXT
0171 IF FLOOR(WPN_Y(WPN_N)) != FLOOR(HITY) GOTO @WPN_NEXT
0172 GOTO @WPN_HOK
0173 @WPN_NEXT
0174 NEXT WPN_N
0175 RETURN
0176 '
0177 @WPN_HOK
0178 HITF = 1
0179 '
0180 BEEP 13
0181 WPN_F(WPN_N) = 0
0182 RETURN

この部分が当たり判定ですが少し複雑になりましたね。
前回の場合は武器が1個だったんで判定が簡単でしたが今回は複数になりました。
今回はすべての判定をしてすべて当たっていないとき当たり判定が失敗となります。

0184 @WPN_GET
0185 FOR WPN_N=0 TO WPN_MAX-1
0186 IF WPN_F(WPN_N)==0 THEN RETURN
0187 NEXT WPN_N
0188 WPN_N = -1
0189 RETURN

発射可能な武器の検索です。

ではこれを実行してみてください。
武器は複数でるはずですが…どうでしょう?

変ですよね?
チェックポイント
同じ敵の複製(問題編)および配列とFORループを先に呼んでください。

では解決編です。


■プログラム
0001 ' initialize
0002 CLEAR
0003 GOSUB @TMR_INI
0004 GOSUB @TK1_INI
0005 @LOOP
0006 VSYNC 1
0007 CLS
0008 GOSUB @TMR_TASK
0009 GOSUB @TK1_TASK
0010 GOTO @LOOP
0011 '
0012 @TMR_INI
0013 TMR_INT = 2*60
0014 TMR_C = TMR_INT
0015 RETURN
0016 '
0017 @TMR_TASK
0018 TMR_C = TMR_C - 1
0019 IF TMR_C >= 0 THEN RETURN
0020 TMR_C = TMR_INT
0021 '
0022 GOSUB @TK1_GET
0023 IF TK1_N < 0 THEN RETURN
0024 TK1_F(TK1_N) = 1
0025 TK1_X(TK1_N) = 0
0026 TK1_Y(TK1_N) = 0
0027 RETURN
0028 '
0029 @TK1_INI
0030 DIM TK1_F(3),TK1_X(3),TK1_Y(3)
0031 FOR I=0 TO 2
0032 TK1_F(I) = 0
0033 NEXT I
0034 RETURN
0035 '
0036 @TK1_TASK
0037 FOR TK1_I=0 TO 2
0038 GOSUB @TK1_SUB
0039 NEXT TK1_II
0040 RETURN
0041 '
0042 @TK1_SUB
0043 ON TK1_F(TK1_I) GOTO @TK1_WAIT,@TK1_MOVE
0044 STOP
0045 '
0046 @TK1_WAIT
0047 RETURN
0048 '
0049 @TK1_MOVE
0050 LOCATE TK1_X(TK1_I),TK1_Y(TK1_I)
0051 PRINT "@"
0052 TK1_Y(TK1_I) = TK1_Y(TK1_I) + 0.1
0053 IF TK1_Y(TK1_I) < 23 THEN RETURN
0054 TK1_F(TK1_I) = 0
0055 RETURN
0056 '
0057 @TK1_GET
0058 FOR TK1_N=0 TO 2
0059 IF TK1_F(TK1_N)==0 THEN RETURN
0060 NEXT TK1_N
0061 TK1_N = -1
0062 RETURN
0063 'End of line


それでは解説です。

0001 ' initialize
0002 CLEAR
0003 GOSUB @TMR_INI
0004 GOSUB @TK1_INI
0005 @LOOP
0006 VSYNC 1
0007 CLS
0008 GOSUB @TMR_TASK
0009 GOSUB @TK1_TASK
0010 GOTO @LOOP


メインの変更は変数の初期化のために CLEAR 文を実行します。
それ以外は余り変更しないようにしました。

0012 @TMR_INI
0013 TMR_INT = 2*60
0014 TMR_C = TMR_INT
0015 RETURN
0016 '
0017 @TMR_TASK
0018 TMR_C = TMR_C - 1
0019 IF TMR_C >= 0 THEN RETURN
0020 TMR_C = TMR_INT
0021 '
0022 GOSUB @TK1_GET
0023 IF TK1_N < 0 THEN RETURN

0024 TK1_F(TK1_N) = 1
0025 TK1_X(TK1_N) = 0
0026 TK1_Y(TK1_N) = 0
0027 RETURN


タイマ部分ですが初期化では TMR_INT に2*60要するに2秒待つためのカウンタ値を代入してあります。
今後この変数の値を TMR_C に代入してカウントダウンします。
要するに2秒おきに0020行以降を実行します。
0022行以降は敵1のうちまだ動いていない待ち状態の敵を探しています。
敵1が合計3体いるのですがすでに画面内に3体いれば出せません。
もし1体でも待ち状態(要するにTK1_F(n)が0の敵)があればその敵を表示するのです。
ちなみに @TK1_GET は使用されていない敵がいなければ TK1_N に-1を返します。
そうでないときは何番目の敵が空いているかその番号を返します。
0024行以降は空いている敵の初期化を行っています。

0029 @TK1_INI
0030 DIM TK1_F(3),TK1_X(3),TK1_Y(3)
0031 FOR I=0 TO 2
0032 TK1_F(I) = 0
0033 NEXT I
0034 RETURN


敵の初期化部分です。
配列の宣言とフラグを0に初期化します。
この TK1_F(n) が0のときまだ敵が使用されていないということで、フラグが1のとき画面で移動中となります。

0036 @TK1_TASK
0037 FOR TK1_I=0 TO 2
0038 GOSUB @TK1_SUB
0039 NEXT TK1_II
0040 RETURN


前回のタスク実行のルーチンが3体分のループに変更されています。

0042 @TK1_SUB
0043 ON TK1_F(TK1_I) GOTO @TK1_WAIT,@TK1_MOVE
0044 STOP
0045 '
0046 @TK1_WAIT
0047 RETURN
0048 '
0049 @TK1_MOVE
0050 LOCATE TK1_X(TK1_I),TK1_Y(TK1_I)
0051 PRINT "@"
0052 TK1_Y(TK1_I) = TK1_Y(TK1_I) + 0.1
0053 IF TK1_Y(TK1_I) < 23 THEN RETURN
0054 TK1_F(TK1_I) = 0
0055 RETURN


このTK1_SUBは前回のTK1_TASKと同じです。
ただし敵のデータが配列になっています。
何番目の敵を操作しているかはTK1_Iに代入されています。

0057 @TK1_GET
0058 FOR TK1_N=0 TO 2
0059 IF TK1_F(TK1_N)==0 THEN RETURN
0060 NEXT TK1_N
0061 TK1_N = -1
0062 RETURN


最後に追加された部分ですがこれは空いている敵を探しています。
このアルゴリズムをサーチ(検索)といいます。
今後よく現れるアルゴリズムです。