どうも、ねへほもんです。
Unreal engine 5でコマンドRPGを作ってみました!
— ねへほもん (@nehehomon) October 2, 2025
有りモノの寄せ集めなので見た目がしょぼいのはご容赦ください pic.twitter.com/AUuwhu1x7e
以前の記事でもご紹介しましたが、ゲーム紹介の話です。
正直見た目は前回の記事の時よりしょぼいですが、なんとこのゲーム、
自作
です。
本に書かれた手順に従って、淡々と作業するだけ・・・ではなく、ゲームの仕様を考え、それをUnreal engine 5 で実装する過程まで、全て自分でこなしました。
さすがにキャラクターの3Dモデルやアニメーションは作れないので、そこだけは以前買った本でダウンロードできるデータを使用しましたが、プログラム周りは完全自作です。
土日で始めるゲームづくり for UE5 | 株式会社ボーンデジタル
簡単な開発の手順は経験していても、アクションとRPGでは仕様が全く異なります。
「フィールドとバトルで画面切り替えどうすんの?」「ダメージ計算どうすんの?」「HPとかどうやって表示すんの?」と分からないことだらけです。
が、今の時代強い味方が居ます。
ChatGPT
マジでビビりました。
仕事でVBAやPythonのプログラムを書いてもらったことはありますが、Unreal engineまで対応しているとは驚きです。
Unreal engineはC++言語で開発するモードもあるそうですが、今回は使わず、視覚的に分かりやすいブループリントで開発しました。
ChatGPT的にはC++の方がやりやすいだろうに、わざわざ人間に合わせてブループリントにも対応してくれるのは嬉しいです。
1.言語化能力が一番大事
前回は本に沿って作業するだけなので、1週間も掛からず完成しましたが、今回は1ヶ月半ほど掛かりました。
まぁ、作業が重いというより、シンクロカップとか空の軌跡とか浮気要因が多すぎて中断していた理由の方が大きいですが、それでもなかなか苦労しましたし、振り返ると良い経験になりました。
今回の経験を通じ、一番重要だと感じたのが「言語化能力」です。
普段ゲームをプレイしていて、意識しないような簡単な処理でも、実は複数の処理が組み合わされていることがあるのだと実感しました。
(例)HP100のキャラが30のダメージを受けて、残りHPが70になる
上記の例だと、直感的にはただ引き算をして70という計算結果を導くだけ、暗算と称するまでも無く一瞬です。
が、ゲーム開発においては複数の過程が存在するのです。
①データ取得
まずは初期状態の変数の情報を集める必要があります。
今回必要なのは、
HP:100
ダメージ:30
の2つです。
実際のゲームだと、攻撃側の攻撃力や攻撃対象の守備力といった変数を参照してダメージを計算する過程もありますが、ただ手順が長くなるだけなので省略します。
僕は面倒だったので、攻撃側の攻撃力がそのままダメージになるものとして実装しました。
②残りHPの計算
100-30=70という計算をするだけ。
③HP変数の上書き
70というのはただ計算されただけの値で、そのままでは何もゲームに反映されません。
元々100という値を持っていたHP変数を、70という値に上書きする必要があります。
④画面に表示されるHPの数値更新
ゲーム内では確かにHPが70に更新されましたが、そのままではプレイヤーはHPが変化したことを把握できません。
そこで、画面に現在のHPがいくつかを表示させる処理が必要となります。
このように、プレイヤーは単に「100-30=70」という引き算で片付ける処理も、裏では4つの過程に分けて処理されています。
実際には他の処理(例:攻撃後にHPが0を下回ったら戦闘終了にする)が含まれるので、綺麗に整理することを考えずに開発しているとぐっちゃぐちゃになってしまいますが、僕の作った中だとHPの回復処理が見やすい感じなのでブループリントをご紹介します。
①左端のPlayerHPとMax Player HP:現在HPと最大HPの変数を取得
②+50の部分:現在HPに回復量(50)を加算
③Clamp(Integer):回復後のHPがMax Player HPを超えないように上限を設定
④Set Player HP:回復後のHPの値を、PlayerHPという変数にセット
⑤右端のUpdateHP:回復後のHPを画面に表示させる処理(一部の値を更新する関数を複数作るのは面倒なので、プレイヤーのHP,MPと敵HPを全て更新する関数にしています)
今時は小学校でもプログラミングを習うらしいですが、プログラミング言語を習うというより、こういう感じでやりたい処理を複数の工程に細分化してみよう!って思考法を学んでいるんですかね。
今の時代、ノーコードのプログラミングツールとか、ChatGPTみたくプログラムを質問する手段が増えているので、初学者的には思考法を学ぶ方が大事だと思います。
で、あと言語化能力が問われるのがChatGPTへの質問です。
一番最初は、
「Unreal engine 5の初心者に対し、初歩的なコマンドRPGゲームの製作方法を教えてください」
というふわっとした質問でしたが、徐々に開発が進んでくると、「今〇〇まで作ってて、次は〇〇を作りたい!」みたいな要望を具体的に伝えないと、的外れな回答が返ってきてしまいます。
ChatGPTはチャットが長く続くと、過去のチャット履歴まで全て記憶するのに処理能力を食うので、1つのチャットで粘り続けると有料プランへの移行を勧められてしまいます。
そこで、何度かチャット履歴をリセットしながら開発を進めたのですが、リセットする上で重要なのが、「過去のチャットでどこまで開発が進んだか?」を記録することです。最初は1行で終わったプロンプトも、最終的にはここまで長くなりました。
(プロンプト)
別のチャットで、Unreal Engine 5を用いたコマンドRPG製作を教えてもらいました。 メッセージの上限に達したので書き直しますが、概要は以下の通りです。
------------------------------------------------------------------------
## 1. 基本の流れ(最初の説明)
- 以下2つのレベルに分かれる
1. フィールド探索(レベル名「LV_Field」)
2. ターン制コマンドバトル画面(レベル名「BattleLevel」)
**LV_Fieldの構成要素**\
- **BP_EncounterManager**(ランダムエンカウントの管理)\
- **BP_PlayerCharacter**(フィールド画面での操作キャラクター)
**BattleLevelの構成要素**\
- **BP_BattleManager**(戦闘管理)\
- **BP_BattlePlayer**(バトル画面での操作キャラクター)\
- **BP_EnemyBase及び子クラス**(敵の処理の管理、子クラスで種別の敵のステータスを管理)
**Gamemode及びInstanceの構成要素**\
- **BP_MyGameInstance**(エンカウント時にプレイヤーと位置と向きを保存)\
- **BP_GM_Field**(LV_FieldのDefault Pawn ClassをBP_PlayerCharacterに定める等のゲーム設定)\
- **BP_GM_Battle**(BattleLevelのゲーム設定)
**UIの構成要素**\
- **WBP_CommandMenuUI**(プレイヤーのコマンドとしてAttack,Heal,Defendの3つのボタンを配置, 敵味方のHPとしてPlayerHP / EnemyHPというテキストを表示)\
- **WBP_BattleWin**(バトル勝利時に表示される画面)\
- **WBP_BattleLose**(バトル敗北時に表示される画面)
------------------------------------------------------------------------
## 2. フィールド探索の詳細
### BP_EncounterManagerの詳細\
- 関数Check Encounter(ランダムエンカウントの管理、一定距離を歩く毎にエンカウント率が上昇し一定の乱数を超えるかを判定し、超えたらBattleLevelへ移行)\
- 戦闘直前にプレイヤーの位置と向きをBP_MyGameInstanceに記憶させる、戦闘後「LV_Field」へ戻った時にその場所から再開
### BP_PlayerCharacterの詳細
- キャラクターのSkeltal Mesh Assetを指定\
- Event Begin play(ゲーム開始時や戦闘終了後にBP_MyGameInstanceからプレイヤーの位置と向きを読み込む)\
- Event Tick(前回Check Encounter関数を起動した時からの歩行距離を計測し、所定の距離を超えた時にCheck Encounter関数を起動)
### 未作成の要素\
- ボス戦の仕様(フィールド上にボスのシンボルを配置し、プレイヤーが接触すると戦闘開始)\
------------------------------------------------------------------------
## 3. ターン制コマンドバトル画面の詳細
### BP_BattleManager の詳細
- **BeginPlay**\
- Create Widget → Add to Viewport\
- SpawnActorで敵を出現させる\
- プレイヤーと敵のHPを最大HPに設定
- ターンをプレイヤー側に設定
- **PlayerAttack(関数)**\
- プレイヤー側の攻撃力の分だけ敵HPを削る
- 敵HPが0以下になった場合にOnBattleWin(カスタムイベント)を起動
- **EnemyAttack(カスタムイベント)**\
- 雑魚敵は通常攻撃のみを繰り返し、敵の攻撃力の分だけプレイヤーのHPを削る\
- プレイヤー側が防御状態(DefendTurns変数が1以上)の場合、敵の攻撃力の4分の1だけプレイヤーのHPを削る\
- ボス戦の場合、通常攻撃→通常攻撃→チャージ→大攻撃のローテーションで行動する。大攻撃は攻撃力の4倍のダメージを与える。
- プレイヤーのHPが0以下になった場合にOnBattleLose(カスタムイベント)を起動
- **PlayerHeal(カスタムイベント)**\
- 所定の回復値だけプレイヤー側のHPを回復\
- 回復後のHPは最大HPを上限に設定
- **PlayerDefend(カスタムイベント)**\
- プレイヤー側を防御状態(DefendTurns変数が1)にする
- **OnBattleWin(カスタムイベント)**\
- WBP_BattleWinを表示させ、戦闘勝利時の画面とアニメーションを表示\
- BP_MyGameInstanceを参照し、戦闘開始前の位置と向きを再現した上でOpenLevel"LV_Field"を実行
- **OnBattleLose(カスタムイベント)**\
- WBP_BattleLoseを表示させ、戦闘敗北時の画面とアニメーションを表示\
- ゲーム開始時の位置に戻すため、単にOpenLevel"LV_Field"を実行
### BP_BattlePlayer の詳細
- **アニメーション設定**\
- 待機中、攻撃時、被ダメージ時、勝利時のアニメーションに関する変数を設定(型:Anim Sequence)\
- 各変数に対応するアニメーションを設定
- **各種カスタムイベントの設定**\
- BeginPlay:Play Animationで待機中のアニメーションを再生 \
- Event Attack:Play Animationで攻撃のアニメーションを再生し、その後待機中のアニメーションを作成 \
- Event Damage:Play Animationで被ダメージのアニメーションを再生し、その後待機中のアニメーションを作成 \
- Event Victory:Play Animationで勝利時のアニメーションを再生
### BP_EnemyBase及び子クラスの詳細
- Take Damage関数として、敵が受けたダメージの値だけHPを減少させる処理を実装 \
- 変数としてMAX Enemy HP(敵の最大HP)とAttack Power(攻撃力)を設定 \
- 子クラスとしてBP_Enemy_Zako(雑魚敵)とBP_Enemy_BOSS(ボス)を作成し、それぞれにSkeltal Mesh AssetとMAX Enemy HP(敵の最大HP)とAttack Power(攻撃力)を設定
### 未作成の要素
- 現状、BeginPlayでプレイヤーのHPが最大HPと同じに設定されるが、戦闘後にHPは回復せず、減った状態で次の戦闘を開始するようにしたい \
- PlayerHealをMPを消費する回数制にしたい \
- 敵の行動時のエフェクト(アニメーションを用意していないため、敵キャラを点滅させる等、古いゲームの演出を参考に実装したい)
------------------------------------------------------------------------
## 4. 次に開発したい要素
- 現状、BeginPlayでプレイヤーのHPが最大HPと同じに設定されるが、戦闘後にHPは回復せず、減った状態で次の戦闘を開始するようにしたい \
- BP_BattleManagerでは、BeginPlayでプレイヤーのHPが固定値(100)に設定されるが、最初の戦闘は100からスタート、次の戦闘以降は直前の戦闘の最終HPからスタートとしたい \
- また、LV_Field上でも現在のHPを表示させたい
(プロンプトおわり)
最初のプロンプト:48字
最後のプロンプト:3256字
ながすぎぃぃぃ
2.苦労した所、工夫した所
さすがUnreal engineというべきか、ヴィジュアル面は非常にリッチに作れます。
無料で拾えるアセットを検索していると、前に作った明るい感じではなく、ダークな雰囲気のものが入手できたので使ってみました。
1個ずつ壁を設置するのは若干手間でしたが、等間隔で設置するだけなので特に悩みませんでした。
やはり裏側で走る処理の実装に苦労したので、アピールしたいポイントを3点ご紹介します。
①エンカウント率
日頃ゲームをプレイしていて、HPが100から30減ったら70になる!みたいな単純計算の処理に注目する人は少ないでしょうが、低レベルプレイヤー的にはダメージ計算の処理は非常に興味があります。
今回はダメージ計算は諦めたのですが、次にゲームの仕様で気になっていたエンカウント率を自分で実装してみました。
使う変数は以下の3つ。
・Encount rate(累積エンカ率)→例:初期値10%
・Encount step(1歩ごとに上がるエンカ率)→例:2%
・Max Encount rate(最大エンカ率)→例:50%
例の数値設定だと、1歩目のEncount rateは10%で、2歩目は10+2=12%, 3歩目は12+2=14%と徐々に上昇します。
で、21歩目で50%に到達した後は、上限に達してずっと50%になります。
歩数が増えるほどエンカしやすくなるという、実際のゲームにありそうな仕様を実装できて良かったです。
確率に基づきエンカするかどうかという部分については、0~100%までの乱数を発生させ、Encount rateがその乱数を上回ったらエンカウントする、という処理にしました。
②GameInstanceって何やねん
同じくエンカ処理の話ですが、エンカウントしたならバトル画面に移行や!と言いたくなりますが、その前に1つやっておくべき処理があります。
プレイヤーの位置の記憶
これもゲーム開発をしていて気づかされたのですが、戦闘終了後に戦闘直前の場所に戻るという処理は当然のものではありません。何もしなかったら振り出しに戻されます。
ゲームでは異なる画面のそれぞれを「レベル」と呼び、僕のRPGだと「フィールド」「バトル」という2つのレベルを作成しました。
普通に考えると、「プレイヤーの位置」という変数はフィールドでしか使わないので、フィールドのレベルだけで変数を設定すれば良さそうですが、そうするとフィールド→バトル→フィールドとレベルを遷移した後に、「プレイヤーの位置」という変数は初期値にリセットされてしまいます。
そこで、上の図では「Cast to BP_MyGameInstance」という小難しそうな処理が入っていますが、このGameInstanceの中の変数はレベル間を遷移してもリセットされないので、プレイヤーの位置を記憶することができます。
具体的な手順としては、
①フィールドからバトル画面へ遷移する前に、プレイヤーの位置をGameInstance内の変数に登録
②バトル画面で雑魚敵を倒して勝利し、フィールドへ戻る
③フィールドへ戻る際に、GameInstance内のプレイヤーの位置に関する変数を読み込み、それをプレイヤーの位置に設定する
という順序です。
エンカ率が徐々に増えるとか、具体的な計算をする処理は理解しやすいのですが、GameInstance内の変数に登録して、その後呼び出すといった目に見えづらい部分は理解するのに苦労しました。
他にはHPの処理もGameInstanceを使っており、敵HPは戦闘する度に最大HPまで回復して良いのですが、プレイヤーのHPは次の戦闘に引き継ぐ必要があります。
ここでもGameInstance内のプレイヤーの現在HPという変数に書き込まないと、戦闘の度にプレイヤーのHPが最大まで回復してしまいます。
③古き良きFFの記憶「点滅処理」
これが一番懐かしくて、実装できた時にウルっと来た処理です。
今回は買った本のおまけでダウンロードできたキャラの3Dモデルを利用したのですが、プレイヤーキャラはアニメーションまであった一方で、敵キャラの方は付いていませんでした。
(そもそも買った本で作ったアクションゲームだとぬいぐるみの置物扱いです)
とはいえ、戦闘中に敵が行動していることが分からないと、プレイヤーがダメージを受ける動作をするだけで、「幽霊と戦っているのか?」という気分になってしまいます。
これはどうしたものか・・・?と思った所、ふと古き良きFFの記憶が蘇りました。
「せや、行動時にキャラを点滅させればええんや!!!」
これならアニメ―ションは不要です。
僕自身、最新のフルボイスでアニメーションが付いているゲームに染まってきてしまいましたが、自分でゲームを制作するとなると、「キャラのアニメーションが無い!」といった制約に悩まされます。
そういう時に昔のゲームが参考になるのだと気づくと、技術的制約を乗り越えて名作を世に送りだした偉大なるレジェンドゲームクリエイターの方々に敬意を表したくなりました。
で、具体的な点滅処理ですが、
初期状態:キャラが見えている
①Set Visibility=FALSE(キャラが見えない状態に変更)
②Delay 0.1(0.1秒待機)
③Set Visibility=TRUE(キャラが見える状態に変更)
④Delay 0.1(0.1秒待機)
(以下、①へ戻る)
「見えない→0.1秒後→見える→0.1秒後・・・」を繰り返すというパワープレイで実現しました。
点滅回数という変数を作って、その回数だけ①~④の処理を繰り返すというプログラミングっぽい処理も可能でしたが、2回しか点滅させないですし、一目瞭然でシンプルな処理の方が初心者的にはやりやすいので、単純に①~④のセットを2個繋げました。
最後にUnreal engineで使える物の中から視覚エフェクトやサウンドエフェクトを付けましたが、正直あまりしっくり来ませんでした。
が、唯一点滅だけは古のFFの記憶を呼び戻す位にバッチリ再現できたので、個人的には大満足でした。
3.次回作
今回はお試しとして、基本的なロジックを組んだだけで、公開してみんなに遊んでもらおう!などとは考えていません。
RPGって、完成品に仕上げるにはフィールドやら敵やら、とにかく要素を追加しないといけないので、最後はボリューム足すだけの作業ゲーになってしまうんですよね。
と考えると、次はボリュームが少なくとも遊べるジャンルにしようと思いました。
(プロンプト)
Unreal Engine 5を用いて、初歩的な格闘ゲームの製作方法を教えてください。
以下の仕様を考えています。
各レベルの構成要素
1. スタート画面:ゲーム起動後に表示される画面。何かボタンを押すとキャラクター選択画面へ遷移する。
2. キャラクター選択画面:対戦に使用するキャラクターを選択する。2人がキャラクターを選択したらバトル画面へ遷移する。
3. バトル画面:2人のキャラクターが対戦する画面。上部にお互いの体力ゲージが表示され、下部に必殺技ゲージが表示される。いずれかのキャラクターの体力がゼロになると、もう一方のキャラクターの勝利を宣言する画面が表示された後、キャラクター選択画面へ遷移する。
バトル時のキャラクター操作\
- ゲーム機のコントローラーを接続してボタンを押すか、キーボード操作で動く仕様\
- ボタンかキー別に以下の技を割り当てる\
- ストリートファイター同様、2人を左右に分けて配置し、キャラクターは左右と上下の2方向にのみ移動する\
- 弱攻撃:ダメージは小さいものの、発動までの時間や発動後の硬直は小さい技\
- 遠距離攻撃:ストリートファイターの波動拳のように、飛び道具を放ち、ヒットした場合に小ダメージを与える技\
- 強攻撃:弱攻撃よりダメージは大きいものの、発動までの時間や発動後の硬直が大きい技\
- 必殺技:バトル中の攻撃や被ダメージに応じて必殺技ゲージが増加し、ゲージが溜まると発動できる。強攻撃よりもダメージが大きい技。
開発計画\
- バトル画面に1人のキャラクターを置き、移動や各攻撃のモーションを実装\
- バトル画面に対戦相手を棒立ちさせ、攻撃被弾時のダメージや吹き飛び等の仕様を実装\
- 2つのコントローラー接続等で2人で対戦する仕様を実装\
- 見た目や技の異なる他のキャラクターを追加\
- キャラクター選択画面やスタート画面を追加し、レベル間を遷移する仕様を実装
最初の質問\
- Unreal engine 5では、最初から利用可能なブループリントと、Visual Studioを導入して使用可能なC++の2種類の開発方法を選べます。
- いずれの方法でも、実装方法をChat GPTに質問予定ですが、どちらの方がやりやすいですか?
という訳で、次は
格闘ゲーム
を作ります!
格ゲーなら1つの戦闘画面をしっかり作り込めば、後は上達を目指して延々と遊び続けられるので、少ないボリュームで完成品っぽさを出せます。
ChatGPT様が居れば処理の実装は自信があります。
後は、キャラの3Dモデルとアニメーションをどうするか・・・?
そこがちゃんとできれば、Unreal engineの本領発揮、素人作とは思えない本格的な格ゲーに仕上がるはずだと思います。
また数か月、下手すると年単位で空くかもしれませんが、上手く進捗したら時々ご報告します。
では(^^)/