7.3 速度調整
敵は移動速度の調整を行っていないので常に全開バリバリ(←死語)で動いています。ビーム砲でやっつける処理をいれるまえに、速度の調整をしておきましょう。

方法は、「6.3 自機とビーム砲の速度調整」と全く同じなので練習問題にしようかなと思ったのですが、サクっと流す程度にやります…(汗)。なお、移動速度の調整については、エクセルマクロTips〜ゲーム篇〜5.1 速度調整もどうぞ。
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 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 blnEnemyAttackSt     As Boolean '攻撃飛行中の敵有無
Dim lngEnemyAttackX      As Long    '攻撃飛行中の縦位置
Dim lngEnemyAttackY      As Long    '攻撃飛行中の横位置
Dim lngEnemyAttackDir    As Long    '攻撃飛行中の移動方向
Dim rngEnemyAttack       As Range   '攻撃飛行中のパターン

Dim lngStage      As Long 'ステージ数

Dim lngMyShipS As Long    '自機の移動速度
Dim lngMyShipB As Long    '自機の前回処理時刻
Dim lngBeamS   As Long    'ビーム砲の移動速度
Dim lngBeamB   As Long    'ビーム砲の前回処理時刻

Dim lngEnemyWalkS   As Long '敵待機飛行の移動速度
Dim lngEnemyWalkB   As Long '敵待機飛行の前回処理時刻
Dim lngEnemyAttackS As Long '敵攻撃飛行の移動速度
Dim lngEnemyAttackB As Long '敵攻撃飛行の前回処理時刻

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」への処理追加
全体の初期設定(起動時の初期設定)では、敵の移動速度のうち「攻撃飛行」の移動速度設定を行います。攻撃飛行の移動速度は変化しない(=ゲームの起動時に1度だけ行っておけばよい)ため、この処理で初期化してます。

なお、待機飛行の移動速度(lngEnemyWalkSの値)は、ゲーム中に変化する(敵の数が減る毎に移動速度を速める)ため、ステージクリア毎に最初の値に戻さなければなりません。そのため、ゲームの初期処理「prcGameInit」で行います。
※「prcGameInit」は、最終的にはステージクリア毎に1回動作する処理になります


 ★Sheet1(ゲーム)のマクロ「prcAllInit」
Sub prcAllInit()

    'エクセルスマイル★シューティングゲームを作ろう
    '処理概要:アプリケーション全体の初期設定を行う
    '記述場所:ワークシート「ゲーム」

    '乱数を初期化
    Randomize

    'ゲームの状態を設定
    lngGameStatus = cstPlay

    '自機のパターンを設定
    Set rngMyShip = Range(Cells(201, 1), Cells(208, 16))

    'ビーム砲のパターンを設定
    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))

    '移動速度取得
    lngMyShipS = Worksheets("コンフィグ").Range("B3") '自機
    lngBeamS = Worksheets("コンフィグ").Range("B5") 'ビーム砲
    lngEnemyAttackS = Worksheets("コンフィグ").Range("B7") '敵攻撃飛行

    'ゲーム画面の初期化
    Call prcScreenInit

End Sub
 ※このマクロは、ワークシート「ゲーム」へ記述してください。
  灰色は既に作成済みの部分です。黒の部分を追加してください。
Sheet1(ゲーム)のマクロ「prcGameInit」への処理追加
ステージ毎の初期設定処理には、待機飛行の移動速度(lngEnemyWalkS)の設定と、敵の前回処理時刻(lngEnemyWalkBlngEnemyAttackB)へ現在時刻を設定する処理を追加します。ちなみに、現在時刻はAPI/GetTickCountで取得した時刻を設定します。


 ★Sheet1(ゲーム)のマクロ「prcGameInit」
Sub prcGameInit ()

    'エクセルスマイル★シューティングゲームを作ろう
    '処理概要:ステージ毎の初期設定を行う
    '記述場所:ワークシート「ゲーム」

    Dim i As Long '汎用変数
    Dim lngTmpY As Long ' 敵の縦位置調整用

    '自機の横位置設定
    lngMyShipX = 1

    'ビーム砲フラグをOFF
    blnBeamF = False

    '敵の基準縦位置設定
    'ステージ数を元に基準位置を算出
    '104を超えたときはそれ以上、下へ表示しない
    lngTmpY = lngStage * 8
    If lngTmpY > 104 Then
       lngTmpY = 104
    End If

    '敵の待機飛行の移動速度再設定
    lngEnemyWalkS = Worksheets("コンフィグ").Range("B6")

    '敵の位置設定
    For i = 1 To 5
        lngEnemyX(i) = Worksheets("コンフィグ").Cells(11, i + 1) '敵の横位置
        lngEnemyY(i) = lngTmpY '敵の縦位置
        lngEnemyPtn(i) = Worksheets("コンフィグ").Cells(13, i + 1) '敵待機のパターン番号
        lngEnemySt(i) = cstEnemyWalk '敵の状態
    Next

    '攻撃飛行中の敵はナシ
    blnEnemyAttackSt = False

    '前回処理時刻初期化
    lngMyShipB = GetTickCount() '自機
    lngBeamB = GetTickCount()   'ビーム砲
    lngEnemyWalkB = GetTickCount()   '敵待機飛行
    lngEnemyAttackB = GetTickCount() '敵攻撃飛行

    'ゲーム画面の初期化
    Call prcScreenInit

End Sub
 ※このマクロは、ワークシート「ゲーム」へ記述してください。
  灰色は既に作成済みの部分です。黒の部分を追加してください。
Sheet1(ゲーム)のマクロ「prcEnemyMove」への処理追加
敵の移動処理「prcEnemyMove」へ速度調整処理を追加した内容です。待機飛行と攻撃飛行の各処理はGosubで呼び出しているので、この呼び出しを「前回の処理から移動速度で指定された時間を経過した場合のみ」呼び出すようにします。
Gosubで行ったのはここで分かりやすいようにするためで、プログラムの処理効率を考えた場合Gosubは使わない方がいい場合があります。

しかし、Gosubを呼び出す条件が「時間が経過しているか」としていないのがポイント。というのも、敵の移動処理はFor〜Nextで5回ループしているので、Gosubを呼び出す都度判定した場合は「前の敵を動かしてからn秒後」(待機飛行の場合)ということになり少々意味が変わってしまいます。(ループしている間に時間が過ぎるからです)

そのため、For〜Nextに入る前に状態毎の移動可否判定を行い、移動OKのときはフラグをTrueFor〜Next内では「フラグがTrueなら移動処理を実行」という流れにしています。


 ★Sheet1(ゲーム)のマクロ「prcEnemyMove」
Sub prcEnemyMove ()

    'エクセルスマイル★シューティングゲームを作ろう
    '処理概要:敵の制御を行う
    '記述場所:ワークシート「ゲーム」

    Dim i As Long
    Dim blnWalk As Boolean
    Dim blnAttack As Boolean

    '待機飛行を行う時間になっているかを判定
    If GetTickCount() - lngEnemyAttackB > lngEnemyAttackS Then
       blnWalk = True '経過した
       lngEnemyAttackB = GetTickCount()
    Else
       blnWalk = False 'まだ
    End If

    '攻撃飛行を行う時間になっているかを判定
    If GetTickCount() - lngEnemyWalkB > lngEnemyWalkS Then
       blnAttack = True '経過した
       lngEnemyWalkB = GetTickCount()
    Else
       blnAttack = False 'まだ
    End If

    For i = 1 To 5

        '攻撃飛行処理の呼び出し
        'ただし、一定時間が経過している場合のみ
        If blnWalk = True Then
           GoSub EnemyAttack
        End If

        '待機飛行処理の呼び出し
        'ただし、一定時間が経過している場合のみ
        If blnAttack = True Then
           GoSub EnemyWalk
        End If

    Next

    Exit Sub

'-------------------------------------------------------
EnemyWalk: '待機飛行処理
'-------------------------------------------------------

    '敵が待機飛行のとき
    If lngEnemySt(i) = cstEnemyWalk Then
       '敵を表示
       rngEnemyWalk(lngEnemyPtn(i)).Copy _
           Destination:=Range(Cells(lngEnemyY(i), lngEnemyX(i)), _
                              Cells(lngEnemyY(i) + 7, lngEnemyX(i) + 15))
        
    End If

    '敵の移動とパターンの切り替え (@)
    '待機飛行中と攻撃飛行中の場合のみ行う
    If lngEnemySt(i) = cstEnemyWalk Or _
       lngEnemySt(i) = cstEnemyAttack Then

       '敵を左へ移動する
       lngEnemyX(i) = lngEnemyX(i) - 1

       'rangeの指定でマイナスなどを指定すると400のエラーが出る
       '右端まで達したら、左端へ移動
       If lngEnemyX(i) < 1 Then
          rngClear16.Copy _
              Destination:=Range(Cells(lngEnemyY(i), 1), _
                                 Cells(lngEnemyY(i) + 7, 16))
          lngEnemyX(i) = 130
       End If

       'パターン番号の切り替え
       '4以上になったら1から
       lngEnemyPtn(i) = lngEnemyPtn(i) + 1
       If lngEnemyPtn(i) > 4 Then
          lngEnemyPtn(i) = 1
       End If

    End If

    Return

'-------------------------------------------------------
EnemyAttack: '攻撃飛行処理
'-------------------------------------------------------

    '攻撃飛行に移る判定1「通常」
    '攻撃飛行中の敵がナシ、Rndの結果が5以下、待機飛行中なら攻撃飛行へ移る
    '攻撃飛行の開始位置は待機飛行中の位置
    '横移動の方向は最初は0(横移動ナシ)
    If blnEnemyAttackSt = False And Int(Rnd * 1000) < 5 And _
       lngEnemySt(i) = cstEnemyWalk Then
       lngEnemySt(i) = cstEnemyAttack '攻撃飛行中
       blnEnemyAttackSt = True        '攻撃飛行中あり
       lngEnemyAttackX = lngEnemyX(i) '攻撃飛行開始横位置設定
       lngEnemyAttackY = lngEnemyY(i) '攻撃飛行開始縦位置設定
       lngEnemyAttackDir = 0 '横の移動方向 この値を移行中の横位置へ加算
    End If

    '攻撃飛行処理
    If lngEnemySt(i) = cstEnemyAttack Then
       '攻撃飛行の敵を表示
       rngEnemyAttack.Copy _
           Destination:=Range(Cells(lngEnemyAttackY, lngEnemyAttackX), _
                              Cells(lngEnemyAttackY + 7, lngEnemyAttackX + 15))

       '25%の確立で横移動方向を変化
       If Int(Rnd * 100) < 25 Then
          '-1 , 0, 1の結果が得られる
          lngEnemyAttackDir = Int(Rnd * 3) - 1
       End If

       '縦位置へ1加算(下へ移動)
       lngEnemyAttackY = lngEnemyAttackY + 1

       '縦位置が143を超えたら最下段到達
       '攻撃飛行を終了し待機飛行へ戻る
       If lngEnemyAttackY > 143 Then
          rngClear16.Copy _
              Destination:=Range(Cells(143, lngEnemyAttackX), _
                                 Cells(150, lngEnemyAttackX + 15))
          lngEnemySt(i) = cstEnemyWalk
          blnEnemyAttackSt = False
       End If

       '横位置へlngEnemyAttackDirを加算
       'lngEnemyAttackDirの内容は -1 , 0, 1
       lngEnemyAttackX = lngEnemyAttackX + lngEnemyAttackDir
          
       '横位置が1未満になったらそれ以上左へ移動しない
       If lngEnemyAttackX < 1 Then
          lngEnemyAttackX = 1
       End If

       '横位置が130以上になったらそれ以上右へ移動しない
       If lngEnemyAttackX > 130 Then
          lngEnemyAttackX = 130
       End If

    End If

    Return

End Sub
 ※このマクロは、ワークシート「ゲーム」へ記述してください。
  灰色は既に作成済みの部分です。黒の部分を追加してください。
というわけで実験!
サンプルのコードを全て定義したら、Sheet1のプロシジャ「prcGame」を実行しましょう。実行する際は、VBエディターからの実行ではなく、エクセルのツールバーにある(マクロの実行)ボタンを押し、マクロの一覧で「Sheet1.prcGame」を選択しましょう。

ここまで作ると、かな〜りゲームっぽくなったと思いませんか?待機飛行の敵はのんびり歩き、攻撃飛行の敵は猛スピード(?)で飛んでくるでしょう。ここまで来ると、ビーム砲が当たっても敵を撃墜できないのがツライですね(汗)。というわけで、次は敵を撃墜する部分を作ってみましょう。

なお、実行すると永久ループと同じような状態になるのでタダでは停止できません。(マクロの停止)ボタンが押せないのです。

でも、安心してください。Escキーを押すことで処理を止めることができます。
Copyright(C) 1999-2006 結城圭介。 All rights reserved