※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の都合上、他のアイテムとは別物として扱うので追記しました。
ここで宣言した変数は同一モジュール(※プロシージャをまとめた大きなプログラムの塊)内で共通利用できます。
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先生に聞いてみました。
ということで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)
ということで、今回はここまで!
残念パパこといのっちでした。
では、また!
コメント
コメント一覧 (1件)
I think that is one of the so much vital info for
me. And i am glad reading your article. However want to commentary on few common issues, The website taste is ideal, the articles is in point of fact excellent : D.
Good activity, cheers