|

|
|
 |
9.2 ステージクリアとゲームオーバー |
 |
|
|
|
|
|
というわけで、ステージクリア処理とゲームオーバー処理を作ります。ここも特にムズカシイ処理はありません。
ステージクリアは全ての敵が撃墜状態になったときに実行、ゲームオーバーは自機の残数が0になったときに実行します。
|
|
|
|
|
|
|
|
|
|
処理を追加する前に、いったん全体の流れを説明します。通常、ゲーム中(敵と戦っているとき)はプロシジャ「prcGameMain」の主処理を実行しています。
'主処理(ステージクリアかゲームオーバーになるまで実行)
'強制終了はEscキーを押す
Do Until lngGameStatus = cstStageClear Or lngGameStatus = cstGameOver
・
・
(省略)
・
・
Loop
'ステージクリア処理
lngStageClearB = GetTickCount() '処理開始時の時刻を取得
Do While lngGameStatus = cstStageClear
・
・
(この章で作ります)
・
・
Loop
|
この処理を実行中にステージクリアの条件「全ての敵が撃墜状態になったとき」が成り立ったらlngGameStatusにcstStageClear(ステージクリア)を設定し、とりあえずこのループを抜けます。
ループを抜けると、次に実行する処理がこれから作る「ステージクリア処理」です。ただし、ゲームオーバーになって抜けたのかもしれないので、「実行するのはゲームの状態lngGameStatusがステージクリア(cstStageClear)になっているときだけ」という制約を付けるようにします。
ステージクリア処理は一定時間メッセージを表示するのが主な仕事。メッセージを表示し一定時間が経過した後は次のステージを開始します。開始方法は、lngGameStatusにcstPlay(ゲーム中)を設定しプロシジャ「prcGameMain」を再度先頭から実行すればOK!
|
|
|
|
|
|
|
|
|
|
ゲームオーバーについても、最初に全体の流れを説明しておきます。と言っても、ステージクリアとほぼ同じです。
自機の残数が0になったときは、いちどプロシジャ「prcGameMain」の主処理を抜けます。もちろん、ゲームの状態(lngGameStatus)をcstGameOver」(ゲームオーバー)にして抜けます。
'主処理(ステージクリアかゲームオーバーになるまで実行)
'強制終了はEscキーを押す
Do Until lngGameStatus = cstStageClear Or lngGameStatus = cstGameOver
・
・
(省略)
・
・
Loop
'ステージクリア処理
lngStageClearB = GetTickCount() '処理開始時の時刻を取得
Do While lngGameStatus = cstStageClear
・
・
(この章で作ります)
・
・
Loop
'ゲームオーバー処理
lngGameOverB = GetTickCount() '処理開始時の時刻を取得
Do Until While lngGameStatus = cstGameOver
・
・
(この章で作ります)
・
・
Loop
|
ステージクリア処理もこのループの次に作りますが、ゲームオーバーもこのループの次に作ります。ただし、この処理も「実行するのはゲームの状態lngGameStatusがステージクリア(cstStageClear)になっているときだけ」という制約を付けるようにします。
ゲームオーバー処理も一定時間メッセージを表示するのが主な仕事。メッセージを表示し、一定時間が経過したらデモ画面に移ります。なお、ゲームオーバー処理を実行する条件は「ゲームオーバー処理もゲームの状態(lngGameStatus)がゲームオーバー(cstGameOver)のとき」です。
|
|
|
|
|
|
| Sheet1(ゲーム)のマクロ「(General)(Declarations)」への変数追加 |
|
|
|
|
(General)(Declarations)へ、ステージクリアメッセージとゲームオーバーメッセージのパターンを保存するための変数、メッセージを一定時間表示させるための変数を追加します。
各変数の使用目的については、ソースコードに記載しているコメントを参照してください。
★Sheet1(ゲーム)のマクロ「(General)(Declarations)」
Option Explicit
'エクセルスマイル★シューティングゲームを作ろう
'記述場所:ワークシート「ゲーム」
'記述箇所:(General)(Declarations)
Dim lngGameStatus as Long 'ゲームの状態
Dim lngMyShipX As Long '自機の横位置
Dim rngMyShip As Range '自機のパターン
Dim blnBeamF As Boolean 'ビーム砲発射フラグ
Dim lngBeamX As Long 'ビーム砲横位置
Dim lngBeamY As Long 'ビーム砲縦位置
Dim rngBeam As Range 'ビーム砲のパターン
Dim rngMyShipHit(1 To 5) As Range '自機の爆発パターン
Dim lngMyShipLeft As Long '自機の残機
Dim blnMyShipLock As Boolean '自機爆発中フラグ
Dim rngClear8 As Range '8x8の消去用パターン
Dim rngClear16 As Range '16x8の消去用パターン
Dim lngEnemyX(1 To 5) As Long '敵の横位置
Dim lngEnemyY(1 To 5) As Long '敵の縦位置
Dim lngEnemyPtn(1 To 5) As Long '敵待機飛行のパターン番号
Dim lngEnemySt(1 To 5) As Long '敵の状態
Dim rngEnemyWalk(1 To 4) As Range '敵待機飛行のパターン
Dim rngEnemyHit As Range '敵爆発のパターン
Dim blnEnemyAttackSt As Boolean '攻撃飛行中の敵有無
Dim lngEnemyAttackX As Long '攻撃飛行中の縦位置
Dim lngEnemyAttackY As Long '攻撃飛行中の横位置
Dim lngEnemyAttackDir As Long '攻撃飛行中の移動方向
Dim rngEnemyAttack As Range '攻撃飛行中のパターン
Dim lngStage As Long 'ステージ数
Dim lngScore As Long '得点
Dim lngEnemyCount As Long '敵撃墜数
Dim rngEnemyHit As Range '敵爆発のパターン
Dim lngMyShipS As Long '自機の移動速度
Dim lngMyShipB As Long '自機の前回処理時刻
Dim lngBeamS As Long 'ビーム砲の移動速度
Dim lngBeamB As Long 'ビーム砲の前回処理時刻
Dim lngMyShipHitS As Long '自機の爆発描画速度
Dim lngMyShipHitB As Long '自機の爆発描画前回時刻
Dim lngMyShipHitC As Long '自機の爆発パターンの描画回数
Dim lngEnemyWalkS As Long '敵待機飛行の移動速度
Dim lngEnemyWalkB As Long '敵待機飛行の前回処理時刻
Dim lngEnemyAttackS As Long '敵攻撃飛行の移動速度
Dim lngEnemyAttackB As Long '敵攻撃飛行の前回処理時刻
Dim lngEnemyHitS As Long '敵爆発パターン描画時間
Dim lngEnemyHitB(1 To 5) As Long '敵爆発パターンの前回処理時刻
Dim lngEnemyHitC(1 To 5) As Long '敵爆発パターンの描画回数
Dim lngStageClearS As Long 'ステージクリアメッセージの表示秒数
Dim lngStageClearB As Long 'ステージクリアメッセージの前回描画時刻
Dim lngGameOverS As Long 'ゲームオーバーメッセージの表示秒数
Dim lngGameOverB As Long 'ゲームオーバーメッセージの前回描画時刻
Dim rngNumbers As Range '数字パターン
Dim rngMyShipLeft As Range '自機残機パターン
Dim rngStageClear As Range 'ステージクリアメッセージ
Dim rngGameOver As Range 'ゲームオーバーメッセージ
Const cstFKeyLeft As Long = 37 '左ボタン
Const cstFKeyRight As Long = 39 '右ボタン
Const cstFKeyV As Long = 86 'Vボタン
Const cstPlay As Long = 1 'ゲーム中
Const cstStageClear As Long = 2 'ステージクリア
Const cstGameOver As Long = 4 ' ゲームオーバー
Const cstDemo As Long = 8 ' デモ画面
Const cstEnemyWalk As Long = 1 '敵待機飛行
Const cstEnemyAttack As Long = 2 '敵攻撃飛行
Const cstEnemyHit As Long = 8 '敵爆発中
Const cstEnemyDown As Long = 16 '敵撃墜
|
※このマクロは、ワークシート「ゲーム」へ記述してください。
灰色は既に作成済みの部分です。黒の部分を追加してください。
|
|
|
|
|
|
| Sheet1(ゲーム)のマクロ「prcAllInit」への処理追加 |
|
|
|
|
全体の初期設定(起動時の初期設定)では、各メッセージのパターン格納と表示時間の設定を行います。表示時間は、ワークシート「コンフィグ」から取得します。
★Sheet1(ゲーム)のマクロ「prcAllInit」
Sub prcAllInit()
'エクセルスマイル★シューティングゲームを作ろう
'処理概要:アプリケーション全体の初期設定を行う
'記述場所:ワークシート「ゲーム」
'乱数を初期化
Randomize
'ゲームの状態を設定
lngGameStatus = cstPlay
'自機のパターンを設定
Set rngMyShip = Range(Cells(201, 1), Cells(208, 16))
'自機の爆発パターンを設定
Set rngMyShipHit(1) = Range(Cells(201, 17), Cells(208, 32))
Set rngMyShipHit(2) = Range(Cells(201, 33), Cells(208, 48))
Set rngMyShipHit(3) = Range(Cells(201, 49), Cells(208, 64))
Set rngMyShipHit(4) = Range(Cells(201, 65), Cells(208, 80))
Set rngMyShipHit(5) = Range(Cells(217, 33), Cells(224, 48))
'ビーム砲のパターンを設定
Set rngBeam = Range(Cells(217, 1), Cells(224, 8))
'8×8のパターン消去用
Set rngClear8 = Range(Cells(217, 33), Cells(224, 40))
'16x8のパターン消去用
Set rngClear16 = Range(Cells(217, 33), Cells(224, 48))
'敵待機飛行のパターン
Set rngEnemyWalk(1) = Range(Cells(209, 1), Cells(216, 16))
Set rngEnemyWalk(2) = Range(Cells(209, 17), Cells(216, 32))
Set rngEnemyWalk(3) = Range(Cells(209, 33), Cells(216, 48))
Set rngEnemyWalk(4) = Range(Cells(209, 49), Cells(216, 64))
'敵攻撃飛行のパターン
Set rngEnemyAttack = Range(Cells(209, 65), Cells(216, 80))
'敵の爆発パターン
Set rngEnemyHit = Range(Cells(209, 81), Cells(216, 96))
'数字パターン
Set rngNumbers = Range(Cells(225, 1), Cells(229, 5))
'自機の残数表示パターン
Set rngMyShipLeft = Range(Cells(217, 9), Cells(224, 16))
'ステージクリアメッセージ
Set rngStageClear = Range(Cells(235, 1), Cells(239, 27))
'ゲームオーバーメッセージ
Set rngGameOver = Range(Cells(230, 1), Cells(234, 42))
'移動速度取得
lngMyShipS = Worksheets("コンフィグ").Range("B3") '自機
lngBeamS = Worksheets("コンフィグ").Range("B5") 'ビーム砲
lngEnemyAttackS = Worksheets("コンフィグ").Range("B7") '敵攻撃飛行
lngEnemyHitS = Worksheets("コンフィグ").Range("B7") '敵爆発時間
lngMyShipHitS = Worksheets("コンフィグ").Range("B4") '自機爆破時間
lngStageClearS = Worksheets("コンフィグ").Range("B14") 'ステージクリア表示秒数
lngGameOverS = Worksheets("コンフィグ").Range("B15") 'ゲームオーバー表示秒数
'ゲーム画面の初期化
Call prcScreenInit
End Sub
|
※このマクロは、ワークシート「ゲーム」へ記述してください。
灰色は既に作成済みの部分です。黒の部分を追加してください。
|
|
|
|
|
|
| Sheet1(ゲーム)のマクロ「prcEnemyMove」への処理追加 |
|
|
|
|
敵の移動処理に追加するのはステージクリアの判定と設定処理で、撃墜した敵の数が5匹かつ全ての敵が撃墜状態(cstEnemyDown)になったとき、ゲームの状態(lngGameStatus)にステージクリア(cstStageClear)を設定します。
敵を何匹撃墜したかはlngEnemyCountという変数に入っているので、この変数の値が5なったらステージクリア成立。条件が成立したら、ゲームの状態をステージクリア(cstStageClear)に設定すると、その後はステージクリア処理(プロシジャprcGameMainの所)が動きます。
ちなみに、lngEnemyCountのカウントアップを敵のステータスを撃墜状態にするときに行うのは理由があります。この場所でカウントアップすると、爆発している敵も含め全ての敵が画面上から消えたときにステージクリアになるからです。
撃墜した数のカウントは、例えばビーム砲と敵が衝突したときに行っても良いのですが、ビーム砲と衝突後、敵は爆発しますよね。もしも、ビーム砲と衝突した瞬間にカウントすると、爆発のアニメーションが終わる前にステー時クリア処理が動いてしまい、なんとなくカッコ悪いです。
★Sheet1(ゲーム)のマクロ「prcEnemyMove」
長くなったので別ウィンドウで表示するようにしました。ここをクリックしてくださいな。
|
|
|
|
|
|
| Sheet1(ゲーム)のマクロ「prcMyShipDown」への処理追加 |
|
|
|
|
自機の爆発処理には、ゲームオーバーの判定とゲームオーバーの設定を行います。
自機の残数を引いている処理がありますが、その後に「残数が0であればゲームの状態(lngGameStatus)をゲームオーバー(cstGameOver)に設定」という処理を追加します。
★Sheet1(ゲーム)のマクロ「prcMyShipDown」
Sub prcMyShipDown()
'エクセルスマイル★シューティングゲームを作ろう
'処理概要:自機の爆発
'記述場所:ワークシート「ゲーム」
'lngMyShipHitCが6未満のときは爆発パターンの描画
If lngMyShipHitC < 6 Then
rngMyShipHit(lngMyShipHitC).Copy _
Destination:=Range(Cells(135, lngMyShipX), _
Cells(142, lngMyShipX + 15))
End If
'lngMyShipHitCが20以上で攻撃飛行中の敵がいないときは
'自機の残数を減らし、自機の位置を初期位置へ戻し、自機を復活
If lngMyShipHitC >= 20 And _
blnEnemyAttackSt = False Then
lngMyShipLeft = lngMyShipLeft - 1 '自機の残数を減らす
lngMyShipX = 1 '自機の初期表示位置
blnMyShipLock = False '自機の復活
'自機の残数を表示
Call prcMyShipLeft
'ゲームオーバー判定
If lngMyShipLeft = 0 Then
lngGameStatus = cstGameOver
End If
End If
'キー入力を溜め込まないためのダミー呼び出し
Call GetAsyncKeyState(cstFKeyV)
'爆発制御カウンタのカウントアップ
lngMyShipHitC = lngMyShipHitC + 1
End Sub
|
※このマクロは、ワークシート「ゲーム」へ記述してください。
灰色は既に作成済みの部分です。黒の部分を追加してください。
|
|
|
|
|
|
| Sheet1(ゲーム)のマクロ「prcGameMain」への処理追加 |
|
|
|
|
ゲームの主制御処理には、ステージクリア処理とゲームオーバー処理を追加します。内容は、どちらもメッセージの表示と一定時間が経過するまでの待機です。
ステージクリア処理でもループを行っていますが、このループに入る条件は「ゲームの状態(lngGameStatus)がステージクリア(cstStageClear)のとき」です。なぜかというと、ゲームオーバーになったときにこの部分(ステージクリア処理)を実行しないようにするためです。ステージクリア処理では、一定時間が経過したらゲームの状態(lngGameStatus)を「プレイ(cstPlay)」、ステージ数(lngStage)へ1を加算してこのループを終了し、このプロシジャ(prcGameMain)を先頭から再度実行するのですが、再度実行するための処理はプロシジャprcGameMain側で制御します。
同じように「ゲームオーバー処理」でもループを行っています。こちらのループに入る条件は「ゲームの状態(lngGameStatus)がゲームオーバー(cstGameOver)のとき」です。ただし、こっち(ゲームオーバー)のループを抜けるときは、ゲームの状態(lngGameStatus)を「ステージクリアcstStageClear」にして抜けます。
ちなみに「Call GetAsyncKeyState(cstFKeyV)」は、キー入力を溜めないようにするためのダミー呼び出しです。これをやっておかないと、「デモ画面に戻った瞬間にゲームがスタートしてしまう」という不具合が出ます。ダミー呼び出しついての詳細は「8.2 自機と敵の衝突/Sheet1(ゲーム)のマクロ「prcMyShipDown」の作成」を参考にどうぞ。
★Sheet1(ゲーム)のマクロ「prcGameMain」
Sub prcGameMain()
'エクセルスマイル★シューティングゲームを作ろう
'処理概要:ゲームの主制御処理
'記述場所:ワークシート「ゲーム」
'ゲームの初期設定
Call prcGameInit
'主処理(ステージクリアかゲームオーバーになるまで実行)
'強制終了はEscキーを押す
Do Until lngGameStatus = cstStageClear or lngGameStatus = cstGameOver
'自機の移動。自機が爆発していないとき、かつ
'前回処理時刻から一定の時間が過ぎたとき処理を行う
If blnMyShipLock = False And _
GetTickCount() - lngMyShipB > lngMyShipS Then
'自機の移動処理を呼び出す
Call prcMyShipMove
'自機の移動処理を行った時刻を保存(次回処理のため)
lngMyShipB = GetTickCount()
End If
'自機の爆発処理。敵と衝突するとblnMyShipLockがTrueになる。
'前回処理時刻から一定の時間が過ぎたときだけ処理
If blnMyShipLock = True And _
GetTickCount() - lngMyShipHitB > lngMyShipHitS Then
Call prcMyShipDown
lngMyShipHitB = GetTickCount()
End If
'ビーム砲/前回処理時刻から一定の時間が過ぎたときだけ処理
If GetTickCount() - lngBeamB > lngBeamS Then
'ビーム砲フラグが発射状態(ture)のときはビーム砲処理を呼び出す
If blnBeamF Then
Call prcBeamMove
End If
'ビーム砲の移動処理を行った時刻を保存(次回処理のため)
lngBeamB = GetTickCount()
End If
'敵の移動処理を呼び出す
Call prcEnemyMove
Loop
'ステージクリア処理
lngStageClearB = GetTickCount() '処理開始時の時刻を取得
Do While lngGameStatus = cstStageClear
'メッセージの描画
'本当は1回だけ描けばいいけど、シンプルに毎回描画しています
rngStageClear.Copy _
Destination:=Range(Cells(66, 61), Cells(70, 87))
'キー入力を溜め込まないためのダミー呼び出し
Call GetAsyncKeyState(cstFKeyV)
'描画時間の制御
If GetTickCount() - lngStageClearB > lngStageClearS Then
'ゲームの状態をプレイ中に
lngGameStatus = cstPlay
'ステージ数へ1加算
lngStage = lngStage + 1
End If
Loop
'ゲームオーバー処理
lngGameOverB = GetTickCount() '処理開始時の時刻を取得
Do While lngGameStatus = cstGameOver
'メッセージの描画
'本当は1回だけ描けばいいけど、シンプルに毎回描画しています
rngGameOver.Copy _
Destination:=Range(Cells(52, 61), Cells(56, 102))
'キー入力を溜め込まないためのダミー呼び出し
Call GetAsyncKeyState(cstFKeyV)
'描画時間の制御
If GetTickCount() - lngGameOverB > lngGameOverS Then
'ゲームの状態をデモに
lngGameStatus = cstDemo
End If
Loop
End Sub
|
※このマクロは、ワークシート「ゲーム」へ記述してください。
灰色は既に作成済みの部分です。黒の部分を追加してください。
|
|
|
|
|
|
| Sheet1(ゲーム)のマクロ「prcGame」への処理追加 |
|
|
|
|
全体を制御するための処理では、主制御処理「prcGameMain」の呼び出しに「ゲームオーバー以上(=デモ)になるまで実行する」という制限を付けます。
ステージクリアすると、プロシジャ「prcGameMain」が終了しこの処理へ制御が戻りますが、ステージクリア処理を終えたときにゲームの状態へ「プレイ(cstPlay)」を設定しているので再度プロシジャ「prcGameMain」を実行するようになり、これで次のステージへGO!というわけです。
ゲームオーバー後のデモ画面の処理は…次の章で作りますので、とりあえず忘れてくださいな。
★Sheet1(ゲーム)のマクロ「prcGame」
Sub prcGame()
'エクセルスマイル★シューティングゲームを作ろう
'処理概要:全体の主制御処理
'記述場所:ワークシート「ゲーム」
'変数の初期設定
Call prcAllInit
'主処理(永久にprcGameMainを呼び続ける)
'終了はEscキーを押す
Do Until False
'ステージ数の初期設定
lngStage = 1
'得点の初期化
lngScore = 0
'自機の残数
lngMyShipLeft = Worksheets("コンフィグ").Range("B1")
'ゲームの主制御処理の呼び出し
'ゲームの状態がゲームオーバーになるまで繰り返す
Do Until lngGameStatus > cstGameOver
Call prcGameMain
Loop
Loop
End Sub
|
※このマクロは、ワークシート「ゲーム」へ記述してください。
灰色は既に作成済みの部分です。黒の部分を追加してください。
|
|
|
|
|
|
|
|
|
|
サンプルのコードを全て定義したら、Sheet1のプロシジャ「prcGame」を実行しましょう。実行する際は、VBエディターからの実行ではなく、エクセルのツールバーにある (マクロの実行)ボタンを押し、マクロの一覧で「Sheet1.prcGame」を選択しましょう。
今度は敵を全て倒すと「CLEAR!」のメッセージが表示され次のステージへ進みますが、ステージが進むにつれて敵の位置が下がってきます。いったい、どこまで下がるのでしょうか…(笑)
そういえば、自機が全部無くなるとゲームオーバーのメッセージが誇らしげに(?)出ます。でも、ゲームオーバーになるとその先に進みません(泣)。これは、デモ画面の処理を作ってないためで決してバグではありません(汗)。というわけで、次はデモ画面を作ります。
なお、実行すると永久ループと同じような状態になるのでタダでは停止できません。 (マクロの停止)ボタンが押せないのです。
でも、安心してください。Escキーを押すことで処理を止めることができます。
|
|
|
|
|