【プログラミング中級者向け】ExcelVBAでスネークゲームを自作してみよう!応用編

難易度変更 PC

どうも、室井(@muroiwataru)です。
今回は以前作成したスネークゲームに機能を追加していきます。

前回の記事を読んでいない方は、先にそちらを読んでください。

VBA-gif-eyecatch

【プログラミング初心者向け】ExcelVBAでスネークゲームを自作してみよう!基礎編

難易度選択機能の追加

ゲームの難易度は自機の移動速度を変更すれば調節できます。
sleep関数による待機時間を短くすれば難易度が上昇しますね。

やるべきことをリストアップすると以下のようになります。

  • 難易度を切り替えるボタンの追加
  • 難易度を確認できる表示の追加
  • 難易度ごとにSleep関数の時間を変更
難易度選択ボタン

UIはこのようになります。
順番に実装していきましょう。

難易度を切り替えるボタンの追加

難易度を選択するためのコマンドボタンを作成します。
EASY・NORMAL・HARDの3つのコマンドボタンを追加しました。

コマンドボタンの作成方法は方向キーを作成したときと同様です。
忘れてしまった方は前回の記事の外部設計を確認してください。

作成したコマンドボタンに以下のプログラムを追加します。
levelの値は難易度ごとに変更します。
今回はEASYを0、NORMALを1、HARDを2としてください。

Private Sub CommandButton2_Click()
    If play = 0 Then
        level = 0
    End If
End Sub

levelという変数はまだ宣言していないので、標準モジュールにPublicで宣言しておきましょう。
型はInteger型を推奨します。

lebel変数は何に使うの?

Sleep関数を実行する前にlebel変数の値で分岐させることで、難易度を変更するために使います。
また、後に実装するランキング機能でも利用します。

If play = 0 Thenの意味は?

ゲームプレイ中には難易度の変更ができないようにするための処理です。
プレイ中に難易度を変更されると、ランキングが正常に機能しないためです。

難易度を確認できる表示の追加

VBAでは Cells(行, 列) = “文字列” でセルに任意の文字列を入力できます。
難易度を表示させるウィンドウを、セルを結合して表現しましょう。

今回はAA2~AE3を結合して中央揃えにしているので、Cells(2, 27) に文字列を入力します。
先程書いたコマンドボタンのプログラムに1行追加します。

Private Sub CommandButton2_Click()
    If play = 0 Then
        level = 0
        Cells(2, 27) = "EASY"
    End If
End Sub

難易度変更

難易度ごとにSleep関数の時間を変更

Select Case文を使って、難易度ごとに自機の移動速度を変更しましょう。
前回、Sleep 90‘待ち時間の調節  と記述した部分を以下のように書き換えます。

Select Case level
    Case 0
        Sleep 120
    Case 1
        Sleep 90
    Case 2
        Sleep 60
End Select

キーボードからの操作

コマンドボタンのプロパティからAcceleratorを設定すると、キーボードを押下した際にクリックしたときと同様の処理が走ります。
ただし、Altキーと同時押しする必要があります。

キーボード操作

上下左右にwasdなどを割り当てておくといいでしょう。

ランキング機能の実装

CSVファイルの作成

複数人でデータを共有できるように、csvファイルとしてユーザー名と得点を保持します。

csvファイル

1~3行目をEASY、4~6行目をNORMAL7~9行目をHARDの記録として使います。
1行ごとに 得点,”ユーザー名” となっているので、最初は0と適当な文字列を入れておきましょう。

Public Sub ランキング初期化()
'ランキングを保存しているCSVファイルを初期化
'製作者用、シートからは呼び出せないようにする
    Dim score As Integer, user As String, i As Integer
    score = 0: user = "Null"
    
    Open ThisWorkbook.Path & "\score.csv" For Output As #1
    
    For i = 0 To 8
        Write #1, score, user
    Next i
    
    Close #1
    
End Sub

上記のプロシージャを実行すれば、自動でscore.csvファイルが作成されます。
デバッグ中にファイルを初期化したい状況が出てくるので、書いておくといいでしょう。

外部設計

ユーザー名&ランキング

ユーザー名の入力フォームの作成

テキストボックスでユーザー名の入力フォームを作成します。

プロパティから入力できる文字数を制限できます。
ランキングの表示が崩れないように、MaxLengthを設定しておきましょう。

入力文字数制限

ランキングを表示するラベルの作成

ランキングを表示するためのラベルを作成します。
プロパティからフォントを等幅フォントに変更しておいてください。

プロポーショナルフォントだと、下の画像のように表示が崩れてしまいます。

プロポーショナルフォント

コマンドボタンを1つ作成しておいてください。

ランキングを読み込む処理

Public Sub ランキング読み込み()

    Dim score(8) As Integer, user(8) As String
'既存のランキングデータを読み込み
    Open ThisWorkbook.Path & "\score.csv" For Input As #1
    
    For i = 0 To 8
        Input #1, score(i), user(i)
    Next i
    
    Close #1
'ランキングを表示
    For i = 0 To 6 Step 3
        
        Select Case i
            Case 0
                Sheet1.Label1.Caption = "EASY" & vbCrLf
            Case 3
                Sheet1.Label1.Caption = Sheet1.Label1.Caption & "NORMAL" & vbCrLf
            Case 6
                Sheet1.Label1.Caption = Sheet1.Label1.Caption & "HARD" & vbCrLf
        End Select
        
        For j = 0 To 2
            Sheet1.Label1.Caption = Sheet1.Label1.Caption & j + 1 & "位" & "  " & Format(user(i + j), "!@@@@@@@@") & Format(score(i + j), "@@") & vbCrLf
        Next j
    Next i
    
End Sub

csvファイルを読み込んで、ラベルに出力する処理です。
リアルタイムで更新することは難しいので、コマンドボタンを押したときに呼び出します。

先程作成したコマンドボタンにCall文を追加しましょう。

Private Sub CommandButton5_Click()
    Call ランキング読み込み
End Sub

ランキングを書き出す処理

Public Sub ランキング書き込み()
'ランキングに関する処理
'排他制御していないため、同時に複数人プレイすると正常に書き込まれない可能性がある
    
    Dim score(8) As Integer, user(8) As String
'既存のランキングデータを読み込み
    Open ThisWorkbook.Path & "\score.csv" For Input As #1
    
    For i = 0 To 8
        Input #1, score(i), user(i)
    Next i
    
    Close #1
'難易度を判定
    Select Case level
        Case 0
            i = 0
        Case 1
            i = 3
        Case 2
            i = 6
    End Select
'難易度ごとに1位から3位までを保存
'配列の0~2を"EASY",3~5を"NORMAL",6~8を"HARD"の保存領域にする
    If cnt > score(i) Then
        score(i + 2) = score(i + 1): user(i + 2) = user(i + 1)
        score(i + 1) = score(i + 0): user(i + 1) = user(i + 0)
        score(i + 0) = cnt: user(i + 0) = player
    ElseIf cnt > score(i + 1) Then
        score(i + 2) = score(i + 1): user(i + 2) = user(i + 1)
        score(i + 1) = cnt: user(i + 1) = player
    ElseIf cnt > score(i + 2) Then
        score(i + 2) = cnt: user(i + 2) = player
    End If

    Open ThisWorkbook.Path & "\score.csv" For Output As #1
    
    For i = 0 To 8
        Write #1, score(i), user(i)
    Next i
    
    Close #1

End Sub

記録を更新した際に、csvファイルを更新するための処理です。
排他制御していないので、複数人が同時にゲームオーバーすると正常に更新されない可能性があります。

このプロシージャを、ゲームオーバーの判定をしているIf文の中から呼び出せるようにしましょう。

'ゲームオーバーの判定
If Y < 3 Or Y > 17 Or X < 3 Or X > 17 Or Cells(Y, X).Interior.ColorIndex = 5 Then
    fin = True
    play = False
    cnt = cnt - 1
    Call ランキング書き込み
End If

ユーザー名を記録できるように、ゲーム開始時にplayer変数にテキストボックスの値を格納できるようにします。
player変数は標準モジュールにPublicで宣言しておいてください。
型はString型です。

僕の場合はSheet1.TextBox1を使っているので、player = Sheet1.TextBox1.Text をスタートボタンに記述します。

Private Sub CommandButton1_Click()
    If play = False Then
        Call 初期化
        Select Case lebel
            Case 0
                Cells(2, 27) = "EASY"
            Case 1
                Cells(2, 27) = "NORMAL"
            Case 2
                Cells(2, 27) = "HARD"
        End Select
        Call ランダム配置
        fin = False
        play = True
        X = 10
        Y = 10
        muki = 2
        player = Sheet1.TextBox1.Text
        Call ブロック
    End If
End Sub

最終的にスタートボタンのプログラムはこのようになりました。
Excel起動時に難易度の表示と変数の値がズレている場合があるので、難易度の表示を確認する処理も追加しています。

プレイ中の得点の表示

スコア

赤いブロックを取得するたびに、カウントアップしていくようにします。
難易度の表示と同様に、結合したセルに表示させています。
今回はU2のセルを使用しています。

赤いブロックを取得したかどうかのIf文に、下記の1行を追加します。
cntをインクリメントする前に記述してください。

Cells(2, 21) = "記録" & "  " & Format(cnt, "@@")

また、初期化プロシージャに表示をリセットするための1行も追加します。
Cells(2, 21) = "記録" & "  " & Format(0, "@@")

あとがき

ランキング機能はうまく実装できましたか?
csvファイルなので簡単に改ざんされますが、そこはVBAの限界ということで妥協してください。

室井
csvファイルをゲームに活用する手法は他にも使い道がありそうな気がする。