当ページのリンクには広告が含まれています。

ゲーム開発で挫折した人必見!生成AI ChatGPT&VBAでプチゲーム開発しませんか?(第3話)

当ページのリンクには広告が含まれています。
  • URLをコピーしました!

※2023/5/4追記 タイトル変更しました。

生成AI ChatGPTを使ってVBAゲームを開発の第3話です。

今回はプロシージャ単位の解説の一部を説明します。

前回予告でプロシージャの説明とちょっと動作させてみる…なんて調子のよいことを申し上げたんですが、ボリューム膨らみすぎて無理でした。次回にご期待ください。

目次

第2話のおさらい

前回のおさらいですが、主にゲームの完成予想図とかんたんな画面の作成を行い、最後にメインのソースコードの構造の説明を行いました。

ChatGPTが出力したソースコードはこちら
Option Explicit
' グリッドの幅と高さを定義
Const GRID_WIDTH As Integer = 10
Const GRID_HEIGHT As Integer = 10
' グリッドのセルごとに定義する内容
Enum GridCell
    Empty
    Enemy
    Item
    Pitfall
    Door
End Enum
' キャラクターの初期位置
Dim playerX As Integer
Dim playerY As Integer
' キャラクターの状態
Dim playerHP As Integer
Dim playerHasKey As Boolean
Dim playerAttackPower As Integer
' グリッドの内容を保持する配列
Dim grid(GRID_WIDTH, GRID_HEIGHT) As GridCell
' 初期化処理
Sub Init()
    ' グリッドの内容をランダムに設定
    Dim x As Integer, y As Integer
    For x = 1 To GRID_WIDTH
        For y = 1 To GRID_HEIGHT
            Dim randValue As Integer
            randValue = Int(Rnd() * 4) ' 0-3のランダムな整数を生成
            grid(x, y) = randValue
        Next y
    Next x
    
    ' キャラクターの初期位置を設定
    playerX = 1
    playerY = 1
    
    ' キャラクターの初期状態を設定
    playerHP = 10
    playerHasKey = False
    playerAttackPower = 1
End Sub
' キャラクターの移動
Sub MovePlayer(dx As Integer, dy As Integer)
    ' 移動先の座標を計算
    Dim newX As Integer
    newX = playerX + dx
    If newX < 1 Or newX > GRID_WIDTH Then
        ' 移動先がグリッド外なら何もしない
        Exit Sub
    End If
    Dim newY As Integer
    newY = playerY + dy
    If newY < 1 Or newY > GRID_HEIGHT Then
        ' 移動先がグリッド外なら何もしない
        Exit Sub
    End If
    
    ' 移動先のセルの内容によって処理を分岐
    Select Case grid(newX, newY)
        Case GridCell.Empty
            ' 空のセルなら何もしない
        Case GridCell.Enemy
            ' 敵がいるセルなら攻撃する
            AttackEnemy()
        Case GridCell.Item
            ' アイテムがあるセルなら取得する
            PickupItem()
        Case GridCell.Pitfall
            ' 落とし穴があるセルならダメージを受ける
            TakeDamage(3)
        Case GridCell.Door
            ' 扉があるセルなら鍵を持っているか確認して開ける
            If playerHasKey Then
                ' 扉を開けてクリア
         MsgBox "クリア!"
          Else
              MsgBox "鍵が必要です。"
        End If
End Select
' 移動先に移動する
playerX = newX
playerY = newY
End Sub
' 敵と戦う
Sub AttackEnemy()
' 敵にダメージを与える
' この部分は実装してください
End Sub
' アイテムを取得する
Sub PickupItem()
' アイテムの種類に応じて効果を適用する
' この部分は実装してください
End Sub
' ダメージを受ける
Sub TakeDamage(damage As Integer)
' ダメージを受けてHPを減らす
playerHP = playerHP - damage
If playerHP <= 0 Then
' HPが0以下になったらゲームオーバー
MsgBox "ゲームオーバー"
End If
End Sub
' 初期化
Randomize ' 乱数の初期化
Init()
' キャラクターを右に移動
MovePlayer(1, 0)
' キャラクターを下に移動
MovePlayer(0, 1)

ChatGPTが出力したソースコードをプロシージャ単位に分けて、どんな処理をしているのか概略図で説明しました。

今回はこのプロシージャ毎に順番に解説をしていきたいと思います。

1.定数・変数の宣言をするべし!

変数宣言部分
Option Explicit
' グリッドの幅と高さを定義
Const GRID_WIDTH As Integer = 10
Const GRID_HEIGHT As Integer = 10
' グリッドのセルごとに定義する内容
Enum GridCell
    Emp ' なにもなし Empty→Empに変更
    Enemy ' 敵
    Item  ' アイテム
    Pitfall ' 落とし穴
    Door  ' ドア
  Key ' ドア 無かったので追加
End Enum
' キャラクターの初期位置
Dim playerX As Integer ' 横軸の座標
Dim playerY As Integer ' 縦軸の座標
' キャラクターの状態
Dim playerHP As Integer ' プレイヤーHP
Dim playerHasKey As Boolean ' 鍵の所持判定
Dim playerAttackPower As Integer ' プレイヤー攻撃力
' グリッドの内容を保持する配列
Dim grid(GRID_WIDTH, GRID_HEIGHT) As GridCell

定数・変数の宣言(※1) しているだけなので、詳細はコード後ろのコメントをご覧いただければと思いますが、このプログラムで利用する変数などの設定をしています。

因みにEnum GridCellの中の「Empty」は「Emp」に変更しました。

VBAだとEmptyというキーワードはプログラムで使うので変数名として使えません

詳細は後述しますが、GridCellにkeyという変数名を追加しています。

鍵もアイテムの一種ではあるんですが、本RPGの都合上、他のアイテムとは別物として扱うので追記しました。

ここで宣言した変数は同一モジュール(※プロシージャをまとめた大きなプログラムの塊)内で共通利用できます。

(※1)変数の宣言とは…
「この変数名に数値を入れます!」「変数名に文字列を入れます!」…と変数名と使い方を宣言しておかないとVBAから怒られます。ばんばんエラー出します。

2.初期化 init で初期状態を設定しよう!

初期化処理
' 初期化処理
Sub Init()
    ' グリッドの内容をランダムに設定
    Dim x As Integer, y As Integer
    For x = 1 To GRID_WIDTH
        For y = 1 To GRID_HEIGHT
            Dim randValue As Integer
            randValue = Int(Rnd() * 5) ' 0-4のランダムな整数を生成 ※修正
            grid(x, y) = randValue
        Next x
    Next y
    
    ' キャラクターの初期位置を設定
    playerX = 1
    playerY = 1
    
    ' キャラクターの初期状態を設定
    playerHP = 10
    playerHasKey = False
    playerAttackPower = 1
End Sub

Forループでは10×10の配列(※変数をまとめて管理する入れ物のこと)に0~3のランダムな整数を入れる処理をしています。

宣言した変数、Enum GridCellの中身を数値にしたもので、空・敵・アイテム・落とし穴を全てのマス(配列)に設定しています。

あれ、ドアと鍵(4、5)は?

残念パパ

このループにドアと鍵を混ぜるとドアと鍵もいっぱいできちゃうので
このままにします。

鍵と扉がいっぱいあると簡単にクリアできちゃいますからね…。

ということで、For ループでセルにイベントを埋め尽くした後で、扉と鍵をアトランダムに一つだけ配置すれば問題ないわけです。

なので、ループの後にこんなソースを追加してみました。

  '鍵の配置
    randValue = Int(Rnd() * 9 + 1)  ' 1-10のランダムな整数を生成
    x = randValue
    randValue = Int(Rnd() * 9 + 1)  ' 1-10のランダムな整数を生成
    y = randValue
    grid(x, y) = 5
    
    
   '扉の配置
    randValue = Int(Rnd() * 9 + 1) ' 1-10のランダムな整数を生成
    x = randValue
    randValue = Int(Rnd() * 9 + 1) ' 1-10のランダムな整数を生成
    y = randValue
    grid(x, y) = 4

配列のイベントが埋まりきった後で、鍵と扉はを上書きする形にして1つだけ配置するようにしてみました。

でも、これ実は問題あるソースコードなんです。

鋭い方は気が付いたかもしませんが、100分の1の確率で鍵と扉の配置がかぶるんですよね。

扉はあるけど鍵が見当たらないとか、永遠にクリアできないとか、とんだクローズド・サークルです。

完全犯罪ですよw

ということで、扉と鍵がかぶらない方法をChatGPT先生に聞いてみました。

以下のVBAのソースですが、2番目の扉のフラグを配列にセットすると、前の鍵のフラグと配列番号がかぶってしまうことがあります。扉のセット時に鍵のフラグと配列番号がかぶったら、やり直すソースを生成してください。

‘鍵の配置 ~ ’扉の配置 のソースを貼り付ける

ということでChatGPTが出力したソースがこちら。

'鍵の配置
Do
    randValue = Int(Rnd() * 9 + 1)  ' 1-10のランダムな整数を生成
    x = randValue
    randValue = Int(Rnd() * 9 + 1)  ' 1-10のランダムな整数を生成
    y = randValue
Loop While grid(y, x) <> 0 ' すでに別のフラグがある場合はやり直す
grid(y, x) = 5
'扉の配置
Do
    randValue = Int(Rnd() * 9 + 1) ' 1-10のランダムな整数を生成
    x = randValue
    randValue = Int(Rnd() * 9 + 1) ' 1-10のランダムな整数を生成
    y = randValue
Loop While grid(y, x) <> 0 Or grid(y, x) = 5 ' すでに別のフラグがあるか、鍵の位置と重なる場合はやり直す
grid(y, x) = 4

・・・扉の配置はギリギリOKなんですが、鍵の方はDo Whileループは不要です。

鍵の配置のLoop Whileの条件式が「grid が0以外なら上書きしない」という記述になっています。完全な間違いではありませんが、鍵はどの配列に上書きしてもとりあえずOKなので、Do Whileは削除します。

'鍵の配置
    randValue = Int(Rnd() * 9 + 1)  ' 1-10のランダムな整数を生成
    x = randValue
    randValue = Int(Rnd() * 9 + 1)  ' 1-10のランダムな整数を生成
    y = randValue
grid(y, x) = 5
'扉の配置
Do
    randValue = Int(Rnd() * 9 + 1) ' 1-10のランダムな整数を生成
    x = randValue
    randValue = Int(Rnd() * 9 + 1) ' 1-10のランダムな整数を生成
    y = randValue
Loop While grid(y, x) = 5 ' 鍵の位置と重なる場合はやり直す
grid(y, x) = 4

この方がすっきりしていますね。

話は戻りますが、Forループの後は宣言した変数に対して、プレーヤーの初期HPや攻撃力、開始位置など。敵やアイテムの配置などの値をセットしています。

この辺はソースを素直に見ていただければわかると思うので、詳細は割愛します。

解説・修正したソースを統合

今回のChatGPTでVBAゲームはここまで!

本当は「3.移動処理」まで進みたかったんですが、あまりに長文になってしまい、泣く泣く分割しました。

分割した移動処理もその先の処理があるから分割していて、どんどん話数が増えるという事態に…。

プログラム初心者~中級者手前くらいの人が分かるように書くのって難しいと痛感しました。

さて、以下が修正したソースです。

修正済みのソースはこちら
Option Explicit
' グリッドの幅と高さを定義
Const GRID_WIDTH As Integer = 10
Const GRID_HEIGHT As Integer = 10
' グリッドのセルごとに定義する内容
Enum GridCell
    Emp
    Enemy
    Item
    Pitfall
    Door
  Key 
End Enum
' キャラクターの初期位置
Dim playerX As Integer
Dim playerY As Integer
' キャラクターの状態
Dim playerHP As Integer
Dim playerHasKey As Boolean
Dim playerAttackPower As Integer
' グリッドの内容を保持する配列
Dim grid(GRID_WIDTH, GRID_HEIGHT) As GridCell
' 初期化処理
Sub Init()
    ' グリッドの内容をランダムに設定
    Dim x As Integer, y As Integer
    For x = 1 To GRID_WIDTH
        For y = 1 To GRID_HEIGHT
            Dim randValue As Integer
            randValue = Int(Rnd() * 4) ' 0-3のランダムな整数を生成
            grid(x, y) = randValue
        Next y
    Next x

'鍵の配置
    randValue = Int(Rnd() * 9 + 1)  ' 1-10のランダムな整数を生成
    x = randValue
    randValue = Int(Rnd() * 9 + 1)  ' 1-10のランダムな整数を生成
    y = randValue
  grid(y, x) = 5

'扉の配置
  Do
      randValue = Int(Rnd() * 9 + 1) ' 1-10のランダムな整数を生成
      x = randValue
      randValue = Int(Rnd() * 9 + 1) ' 1-10のランダムな整数を生成
      y = randValue
  Loop While grid(y, x) = 5 ' 鍵の位置と重なる場合はやり直す
  grid(y, x) = 4

    ' キャラクターの初期位置を設定
    playerX = 1
    playerY = 1
    
    ' キャラクターの初期状態を設定
    playerHP = 10
    playerHasKey = False
    playerAttackPower = 1
End Sub
' キャラクターの移動
Sub MovePlayer(dx As Integer, dy As Integer)
    ' 移動先の座標を計算
    Dim newX As Integer
    newX = playerX + dx
    If newX < 1 Or newX > GRID_WIDTH Then
        ' 移動先がグリッド外なら何もしない
        Exit Sub
    End If
    Dim newY As Integer
    newY = playerY + dy
    If newY < 1 Or newY > GRID_HEIGHT Then
        ' 移動先がグリッド外なら何もしない
        Exit Sub
    End If
    
    ' 移動先のセルの内容によって処理を分岐
    Select Case grid(newX, newY)
        Case GridCell.Empty
            ' 空のセルなら何もしない
        Case GridCell.Enemy
            ' 敵がいるセルなら攻撃する
            AttackEnemy()
        Case GridCell.Item
            ' アイテムがあるセルなら取得する
            PickupItem()
        Case GridCell.Pitfall
            ' 落とし穴があるセルならダメージを受ける
            TakeDamage(3)
        Case GridCell.Door
            ' 扉があるセルなら鍵を持っているか確認して開ける
            If playerHasKey Then
                ' 扉を開けてクリア
         MsgBox "クリア!"
          Else
              MsgBox "鍵が必要です。"
        End If
End Select
' 移動先に移動する
playerX = newX
playerY = newY
End Sub
' 敵と戦う
Sub AttackEnemy()
' 敵にダメージを与える
' この部分は実装してください
End Sub
' アイテムを取得する
Sub PickupItem()
' アイテムの種類に応じて効果を適用する
' この部分は実装してください
End Sub
' ダメージを受ける
Sub TakeDamage(damage As Integer)
' ダメージを受けてHPを減らす
playerHP = playerHP - damage
If playerHP <= 0 Then
' HPが0以下になったらゲームオーバー
MsgBox "ゲームオーバー"
End If
End Sub
' 初期化
Randomize ' 乱数の初期化
Init()
' キャラクターを右に移動
MovePlayer(1, 0)
' キャラクターを下に移動
MovePlayer(0, 1)

ということで、今回はここまで!

残念パパこといのっちでした。

では、また!

この記事が気に入ったら
フォローしてね!

シェアお願いします!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメント一覧 (1件)

コメントする

目次