【Unity】簡単なRPGを作る場合に必要な作業を考えてみよう
私はもともとRPGツクールで遊んでいて「ゲーム作り楽しいなー」とUnityの世界に足を踏み入れたわけですが、いわゆる王道系RPGについてはそんなに作らず、アクションやシミュレーションだったりと、他のジャンルについても手を出しています。一旦初心にかえって考えた時に、いわゆる初期のドラクエ的なRPGを作ってみたいと思い立ち、それに必要な作業を考えてみたいと思います。
最終的には超シンプルRPGを作る過程について、時間を見つけてうまくチュートリアル的にまとめたいと目論んでいるものの、しばらくブログを書くのをサボっていたせいで筆の進みが自分でもびっくりするほど遅いため、ゆっくりペースでまとめていきたいと思います。
ざっくりと箇条書き
本当にざっくりと考えてみた時に、以下の部分から決めていきたいと思います。あくまで超シンプル、規模も小さく、の想定でいるため、昨今のRPGに含まれている便利機能などは入れずにいきましょう。
- 制作環境の選定
- 規模の想定
- 必要なリソースのリストアップ
- 実装が必要な部分
- 最小限にとどめる勇気を持つ
制作環境の選定
「UnityでRPGを作りたい」と冒頭で言ってしまっているので、これはもうUnityを使う方向で行きます。これを書いている2024年11月はUnity6がリリースされて1ヶ月程度なので、リリースされてほやほやのUnity6で作っていきたいと思います。多分作っているうちにUnity6.1にアップデートされそうな気もしますが、そこは臨機応変にいきましょう。
私のメイン環境はmacで、VSCodeを使って開発していきます。もしチュートリアル化するなら画面のスクショはmacのものになるかと思いますが、環境に合わせて読み替えていただければと思います。書いた流れで再現できるかな? という点はWindows機を使って確認予定です。その時間が取れるかはわかりませんが、未来の私に頑張ってもらうことにしましょう。
規模の想定
超シンプル、超小規模。
今回のコンセプトはこれでいきます。ストーリーも、勇者の旅立ちの前日譚みたいな感じで、村の少年が洞窟に住み着いたゴブリン一族を懲らしめて帰ってきたらお城に呼ばれて終わり、僕の戦いはこれからだ! くらいにしておきましょう。
RPGを作る時には、ついつい妄想を膨らませて規模が大きくなってしまうのが常。なので自分に言い聞かせるように規模の小さいものを作るのが完成させるためのコツです。RPGツクールの攻略本を昔読んだことがあるのですが、その中に「最初は5分くらいで終わるものでいいから簡単なものを作って、それができてから広げていこう!」のようなことが書いてありました。この点は今でも心の中に残っていて、企画段階の無秩序な妄想の広がりはモチベーションとして残しつつ、現実的な一歩目として小規模な部分から作り始めましょう。
特にRPGツクールを使うならRPGに必要な機能が大体揃っているのですが、Unityにおいてはそこから作り始めることになります。ここで心が折れる方も多いので、必要最低限な機能を作るのにとどめましょう。必要最低限と言いつつ、必要な機能は多いので泣きそうですがなるべくシンプルにいきます。大事なことなので何回も自分に言い聞かせましょう。
必要なリソースのリストアップ
今回のコンセプトは初期のドラクエ的なRPGなので、2Dのトップビューで進めます。必要になりそうなリソースは以下の通りです。
- フィールド上のキャラクターのスプライト
- 最低限のアニメーションで
- 32*32くらいのサイズを想定
- フィールド上のマップパーツ
- キャラクターと同じく32*32を想定
- UnityのTileMapを使用
- 戦闘画面の敵キャラのスプライト
- 3キャラくらい(スライム、ゴブリン、ボスゴブリン)
- アニメーションはしません
- メッセージウィンドウやUIの背景
- シンプルな単色背景
- 選択用のカーソル
- メニュー画面で項目を選択したり、戦闘画面で選択したり
- ゲーム内で使用するフォント
- フリーフォントを使いましょう
- 効果音
- 主に戦闘シーンで
- BGM
- 村、フィールド、洞窟、戦闘など
とりあえずこの辺りは必要になりそうかなと思います。チュートリアルの中ではグラフィック関係は自作、それ以外はフリー素材を使っていく想定です。
システム関係の素材に関してはゲームの規模によらず必要になってくるので、規模が小さくても素材の準備の流れを知ることができるかと思います。
実装が必要な部分
問題はここなんよ……。
必要最小限とは言いつつ、規模によらずに実装しなければならない機能は多いんですよね。
- ゲーム開始の処理
- キャラクターの移動処理
- マップの作成
- メニュー画面の処理
- 戦闘関連の処理
- パラメータ設定
- 宿屋の処理
- お店の処理
- 会話の処理
- イベントフラグの処理
- セーブの処理
- 音楽関係の処理
ざっと思いつくだけでもこの辺りがあります。必要最低限にするなら音楽関係の処理をなしにすれば素材の準備でも楽ができるのですが、個人的に音楽があった方がテンションが上がるので頑張って入れちゃいましょう。
上記の項目について、それぞれ深掘りしていきます。
ゲーム開始の処理
ゲーム全体の開始処理ですね。
初期化処理の中で必要なリソースをロードしたり、タイトル画面として
- ゲーム開始
- 続きから
- ゲーム終了
あたりを選択できるようにしましょう。
私がいつもやるシーン構成だと、
- Init
- Title
- Game用のシーンいくつか
なんて感じで初期化用のシーン(リソースをロードしつつロゴを出したり)を挟んだりするのですが、今回はタイトルシーンでゲーム全体の初期化処理もやってしまいましょう。フィールドマップや戦闘シーンもひとつのシーン内で処理します。おそらく、シーン構成としては、
- Title
- Game
の2つになるかと思います。
キャラクターの移動処理
移動処理に関してはUnityで入力検知のシステムがあるのでそれで検知します。キャラクターを自由に動き回れるようにするか、マスに沿って移動させるかは悩みどころですが、初期のドラクエ風味であることを見据えてマスに沿った移動をさせたいと思います。
キャラクターの位置情報はVector2Intで保持して、移動先に該当するタイルの中心位置を計算しつつ、最終的にUnityのTransformで位置を移動させる、なんていう若干の計算が入りますが、イベントの発生位置などが分かりやすくなる利点もあるので、今回はこの方式で頑張ってみたいと思います。
今回はチュートリアルの意味合いもあるので自力実装しますが、アセットのTopDownEngineを使うことで簡単に移動を実装することもできるので、実際に作るときにはそちらを使うと楽ができます。
マップの作成
マップはUnityのTileMapを使用して作成します。マスに沿った移動と相性が良いので多少分かりやすく実装できるのではと考えています。
作成するマップは、
- 村
- フィールド
- 洞窟
の3種類でいきましょう。
フィールドも村と洞窟が表示されるだけの簡単なものにします。なるべくタイルを作らなくて済むように進めます。ちゃんと最後まで作れたら、アセットの作り込みを行なっていきましょう。最初に細かく作りすぎると力尽きる可能性がグッと上がります。
メニュー画面の処理
これも地味に大変だったりします。
メニューボタンを押したらキャラクターたちの移動は止めて、必要な項目を選択できるようにします。
- アイテムの使用
- 装備の選択
- ステータスの確認
- ゲームの終了
あたりを実装しておきましょうかね。それぞれの項目について画面を作って、動作をつけてとやることも地味に多いですが頑張りましょう。規模が小さくても大きくてもこの辺りの項目は必要になってくるので、拡張時に使いまわせるのは利点です。
戦闘関連の処理
多分これが一番大変だと思います。ここもさらに分割して考えてみましょう。
- エンカウントの方式
- 戦闘の流れの作成
- 戦闘中の行動選択の処理
- 戦闘中の行動処理
- ダメージ計算
- 勝利時の処理
- 敗北時の処理
エンカウントの方式
エンカウントについては初期のドラクエ風味に合わせてランダムエンカウントにします。フィールドや洞窟を歩いている場合に、移動後にエンカウントの判定を行い、戦闘に入るようにします。ファミコン初期の鬼エンカ率にすると移動も大変なので、村から洞窟に移動するまでに1回か2回くらいのエンカウントが良いかもしれません。この辺はテストプレイ時に調整していきます。
ボス戦はイベント戦闘として扱い、会話後に戦闘に入るようにします。これを応用すればロマサガのようなシンボルエンカウントも実現できるので、両方のエンカウント方式について触れることになります。
戦闘の流れの作成
戦闘はターン制にします。今回は素早さのパラメータを導入するので、それを使ってターン内の行動順を選択します。行動順決定の乱数幅は大体20%程度を見込んでいます。
戦闘が開始されたら、行動を選択するようにします。この超シンプルRPGでは1vs1の戦闘なので、ターゲット選択はなしにして、攻撃、魔法、アイテム、逃げる、あたりから選択して、選択フェーズを終了します。1vs1なので防御コマンドは無しにしておきましょうか。複数人バトルなら防御する意味も出てくるため、全体の方針決定フェーズ(戦うか逃げるか)、キャラクター個別の選択フェーズ(攻撃、魔法、防御、アイテム)と分けていくと、ユーザとしても選択しやすくなるかと思います。
行動を選択したらその結果を処理します。行動順の決定、先攻側の行動処理、後攻側の行動処理、次のターンの処理に進みます。敵を倒せたら経験値やゴールドの入手処理、こちらのHPが0になったらゲームオーバーのメッセージと画面を出してタイトルに戻します。
また、遭遇時におけるプレイヤー側の先制攻撃、敵による不意打ちの判定は今回は行いません。入れるとしたらエンカウント後に判定を行うイメージですね。ドラクエ2だとラスボスすら先制攻撃してくることがある鬼難易度になっていましたが、ボス戦の場合はこちらの先制攻撃も敵の不意打ちもなしにしておくのが良いかと思います。
戦闘中の行動選択の処理
上記の方針で進めるため、行動選択用のUIを作成しましょう。選択肢のテキスト、現在選択中のカーソルの表示、キー入力に対する処理などを実装していきます。
行動選択に関しては、現在どのウィンドウで選択を行なっているのかの状態を定義して、どのカーソルを動かすのか切り分けていきましょう。
今回は1vs1の戦いのため、「攻撃」を選択したらそのまま行動選択フェーズを終了して行動順の計算に入ります。
「魔法」や「アイテム」を選択した場合には、どの魔法またはアイテムを使用するのかのウィンドウに遷移します。魔法の場合は一覧を表示する際にキャラクターの現在MPを確認して、消費MPが足りていない項目をグレーアウトします。アイテムは所持数が0のものを表示しないようにしましょう。また、装備品などの戦闘中に使用できないアイテムについても表示しないようにします。
「逃げる」を選択した場合は逃走判定に移ります。ボス戦の場合には「逃げる」をグレーアウトして選択できないようにするなど、フラグを用意しておきます。
戦闘中の行動処理
行動を選択したらターン内の処理を実行します。
まずは「逃げる」を選択した時の逃走成功判定を行います。この場合、プレイヤー側の行動順を先にして、逃げるの判定で成功すれば逃走して戦闘終了、経験値やゴールドの獲得はなしで移動画面に戻ります。失敗したら、敵側のみが行動してターン終了となります。
続いてプレイヤー側のキャラクターと敵キャラで行動順の計算を行い、早い方から行動させます。基本的には素早さを元に計算していきますが、多少のランダム要素を入れるために、上述の通り20%程度の乱数幅で計算するようにします。
攻撃の場合には、ダメージ計算を行なった上でダメージを受けた側のHPを減らします。今回は命中判定は入れずにいきますが、入れる場合はダメージ計算を行う前に命中したかどうかを計算するのが良いかと思います。
魔法やアイテムの場合には、定義値を確認して効果量の計算を行います。こちらもダメージ計算と同様に、計算式を準備しておきましょう。
行動可能なキャラクターが全員行動したら次のターンに移ります。
ダメージ計算
攻撃時のダメージ計算
ダメージ計算については初期のドラクエに倣って、
(攻撃する側の攻撃力) / 2 – (攻撃を受ける側の防御力) / 4 = ダメージ
にしましょう。実際には左辺の計算時に乱数も含めて計算予定のため、
((攻撃する側の攻撃力) / 2 – (攻撃を受ける側の防御力) / 4) * (乱数) = ダメージ
になる想定です。乱数幅は20%程度の想定で、Randomクラスを使って0.8から1.2の間の乱数を取得します。攻撃力の割り算、防御力の割り算についてはfloatで計算し、乱数と掛け算した後の最終結果でintに切り上げます。攻撃を受ける側の防御力が高いと計算結果がマイナスになることから、最低ダメージ保証として1を設定します。
最低ダメージ保証を入れるかどうかは好みにもよるところで、ストーリー的に考えた時に、格上を殴ってダメージが入るかどうかを検討してみたり、ゲーム的に考えて少しでもユーザの行動を反映させたいと検討してみたりと、多角的に考えてみると良いかと思います。格上を殴るなら、そもそも命中するかどうかのところで判定させる方が良いかもしれません。強い人なら相手の攻撃を受けないように立ち回るでしょうし、強くてもガッチガチに防具を固めた人なら動きにくくて命中はするけど、ほとんどダメージが入らないから最低保証のダメージだけ入る、みたいな感じです。お互いに命のやり取りをしている状態での攻撃と考えれば、それはもう死に物狂いで全力で行くでしょうから、当たりさえすればダメージが入る、と考えるのも良いかと思います。
クリティカルについては今回は導入しないこととします。発生率を固定にしたり、特定のパラメータ(運など)に依存させたりなど、導入するパラメータに応じて決定するとグッド。クリティカルの発生率については、期待値を計算して設定できるとより調整しやすくなります。クリティカル発生時のダメージ倍率も影響してくるので、ExcelやNumbers、Googleスプレッドシートなどを使ってそれぞれのパラメータを変えながら計算してみて、ちょうど良い値に調整していきましょう。
魔法やアイテムの回復量の計算
また、今回は回復魔法、回復アイテムも導入します。そのため、回復用の計算式も想定しておきましょう。
(基礎回復量) * (乱数) = 回復量
とシンプルにしておくのが良さそうです。パラメータの種類がたくさんある場合は、魔法は魔力などを加味してより回復量が大きくなるようにするのも良いかもしれません。今回は魔力に相当するパラメータは入れないので、上記の式で進めます。アイテムの場合は誰が使っても似たような量で回復させるため、魔力などは計算に含めない形にすると良いかと思います。
魔法でもクリティカルが発生するゲームもあるので、その場合は上述のダメージ計算にあるように、期待値を計算して調整するとグッド。
逃走成功率の計算
さらに、逃げる時の成功率計算の式も用意します。
(基礎成功率) + ((逃げる側の素早さ) – (逃げられる側の素早さ)) = 成功確率
としておきましょう。ここで、基礎成功率は割合ではなく%表示の値を使います。成功率80%なら80を入れます。ここでも逃走率の最低保証として50を設定しましょう。格上から逃げる場合、簡単には逃げられないと考えるのが現実的ですが、ゲーム的には格上と戦うと高確率でゲームオーバーになるので、全く逃げられないとプレイ時のストレスがマッハとなることから運次第で逃げられるようにした方が良さそうです。
今回は運のパラメータはありませんが、逃げる際に運のパラメータを使うのもユーザ側からイメージしやすくなるかと思います。
行動順の計算
素早さを使う計算として、戦闘中の行動順決定に関しても式を用意します。
(素早さ) * (乱数) = 行動優先度
として、行動優先度の大きいキャラクターから行動させます。同値の場合は差がつくまで乱数で判定します。今回は導入しませんが、防御を選択した場合は、行動開始前に防御状態にするなどの処理を入れます。自分の番が回ってきてから防御、だと素早い敵と戦う際には防御前に攻撃を受けたりしますからね。
また、行動優先度を無視するターン内最速行動、ターン内最遅行動の魔法やスキルなどを導入する場合は、行動速度の計算によらず処理させるとグッド。味方と敵で同時にターン内最速行動をする場合は、魔法側の優先度や素早さの計算によって順番を決めることになるかと思います。
勝利時の処理
戦闘に勝利した場合は、経験値、ゴールドの獲得処理を行います。今回はとことんシンプルに進めたいのでアイテムのドロップ判定は行いません。もし導入する場合は、敵キャラの定義データの中に、ドロップするアイテムのIDやその確率などを入れておきましょう。
経験値を獲得したらレベルアップするかどうかの確認を行います。レベルアップの定義データを作成しておいて、経験値が該当のレベルに達していたらレベルアップの処理を行います。レベルと経験値の対応は累計経験値で確認するようにします。
通常の敵のエンカウントでは、上記の処理後に移動画面に戻ります。ボス戦の場合は、戦闘が終わったら後続のイベント処理を呼び出すようにします。
敗北時の処理
こちらのHPが0になって敗北した場合は、ゲームオーバー画面に遷移します。ゲームオーバー画面では何らかのキーを入力したら、村の長老の前から再スタートするようにします。負けた後にタイトル画面に戻すか、特定の場面からやり直すかはゲームの方針に合わせて選択するとグッド。今回はドラクエ風味なので、長老の前に戻します。
ペナルティに関してはゲームの難易度と相談して決めるのが良いですね。ドラクエだと所持金の半分が失われる形、ファイナルファンタジーシリーズだとゲームオーバーでタイトルに戻される、ネトゲだったら同じレベル内で獲得した経験値の没収、といった形でいろんな形式があります。セーブポイントが近いならゲームオーバーでタイトルに戻した方が、むしろペナルティとしては軽い気もしています。今回はシンプルにいくのでペナルティなしにしましょう。
パラメータ設定
戦闘で使うパラメータを考えてみます。シンプルに作りたいので、
- HP
- MP
- 力
- 身のまもり
- 素早さ
でいきたいと思います。HPがなくなったらゲームオーバーで、村の長老に励まされる感じにしましょう。それぞれのパラメータの値の範囲は小さめで、レベルが上がる時にも値を増やしすぎないようにします。レベル1でこの値、レベル2でこの値、というように、事前に定義を作っておくようにします。必要な経験値についても定義します。
値の定義についてはScriptableObjectに持たせるようにします。シンプルに進めたいので、CSVからのインポートツールなどは作らず、インスペクターウィンドウで値を設定できるようにしておきます。操作キャラに加えて、敵キャラについてもパラメータの設定をするようにしましょう。敵キャラについては経験値とゴールドも定義します。
装備品による補正については、力+武器の攻撃力で総合攻撃力、身のまもり+防具の防御力で総合防御力として表示します。
また、どくやまひといったステータス異常などは実装しません。
宿屋の処理
いわゆる全回復の処理です。宿屋とは言いましたが、今回のストーリーでは操作キャラは自分の村から出発しており、自分の家があるはずなので自宅で無料で全回復するようにします。
ゲームの序盤は少ない金額で、ゲームの後半になるにつれて大きな金額にしていくのがよくある手法です。魔王の城に近くなる程危険度が増すから宿代も高くなる、だったり、他国と緊張状態にあるから宿代も高くなる、といった感じにストーリーや地理的な理由もあるとグッド。
また、回復するHPやMPの量に応じて金額が増えるケースでも、パラメータの値が増える後半では回復の金額が増えることになるので、成長を感じることができます。
お店の処理
回復アイテムの薬草やポーションのようなものを用意して、HPを回復する手段を用意します。装備品も上位品がひとつあるとRPGで装備の更新をする楽しさが出るので、こちらも実装します。
装備品については、武器と防具という括りにしておきます。もし装備箇所を増やしていくなら、それに応じてメニュー画面の項目も増やしていくことになります。
会話の処理
RPGで大切な会話、メッセージ表示の処理も実装します。ここも地味に大変かもしれません。
シンプルにいきたいので、1文字ずつの表示などは入れず、1ページ分一気に表示させるようにします。はい、いいえ、で分岐する処理くらいは入れておきたいと思っています。
戦闘中のメッセージ表示もあるので、UIの素材については使い回していきます。あ、戦闘中は「〇〇の攻撃!」で一旦止まって、効果音とエフェクトを出してから続きに「〇〇に30のダメージ!」のように表示しましょうか。システムを確認する最初の段階では1ページ分一気に表示するようにして、ブラッシュアップしていく段階になったら上記の改善を入れていきます。
RPGではテキスト量も多くなるため、実際に作るときにはテキストファイルから読み込む仕組みもあると便利です。キャラクターの名前を置き換えたり、文章の途中でキャラクターの立ち絵の表情を変えたりなど、特殊な動作に対応するタグなどを作っておくと表現できることも多くなります。この点は大昔に吉里吉里でノベルゲームを作ろうとしたこともあって、文章表示系の機能に関しては勉強になりました。
Unityで文章表示機能を使うならアセットの宴を導入する手もあります。ノベルゲームの動作全体ではなく、文章表示機能だけ使う、というややトリッキーな使い方ですが、自力実装するよりは早く使えそうな気がしています。
私がずっと前に自力実装した時は、テラシュールブログさんの以下の記事を参考にした記憶があります。一度自力実装しちゃえばそのあとは使いまわせるので、ゲームの雰囲気に合わせて好きなように改造していく楽しみもあります。
TextMeshProに対応させる形だと、ねこじゃらシティさんの記事や、ロジカルビートさんの記事がわかりやすいかなと思います。
イベントフラグの処理
RPGではイベントのフラグのON、OFFで処理を切り替える場面が多いので、こちらも導入したいと思います。
といっても使う場面は限られていて、洞窟の宝箱を取ったかどうか、洞窟のボスゴブリンを倒して村に戻ったら長老のセリフが変わってエンディングになる、くらいの使い道になります。
場面が限られているので単純なbool値のフィールドでも良いのですが、RPGを拡張していく際にはどのフラグがどんな値になっているかを管理するのも大切なので、structでID、名前、フラグの状態、の3つくらいを持たせて、List形式で保持したいと思います。DictionaryではなくListにしているのは、この後のセーブの部分とも関連しますがJSONUtilityでJSON化する関係で、Dictionaryが対応していないためです。
アセットのEasySaveならDictionaryの保存も対応していたかと思いますので、そちらを使う場合はDictionary形式でも良いかもしれません。
セーブの処理
データの保存も重要な点です。
セーブした時と同じ状況を再現できるように、必要な値を決めて保存していきましょう。シンプルに、と言いつつPlayerPrefsではなくJSONを使って保存するようにします。PlayerPrefsは環境によってはレジストリに値を保存しちゃったりするので、基本使わない方が良いかと思います。
今回のチュートリアルでは自前でセーブ機能を実装しますが、実際にゲームを作る時にはEasySaveなどのアセットを使うと楽です。
また、セーブポイントについては初期のドラクエ風味として村に配置予定です。現代的なゲームを考えると、どこでもセーブやオートセーブなどもあると良いのですが、セーブの仕組みがあればあとはそれを呼び出すだけなので今回は対象外にします(UIを作ったりはありますが)。
音楽関係の処理
個人的にはある程度ちゃんとチャンネルに分けて音楽関係を管理するSourdManagerのようなものを作りたいのですが、そこまでやると作業が増えてしまうので、シンプルに再生、停止、くらいの機能を持たせるようにします。AudioSourceのコンポーネントを使って、場面に応じたBGMや効果音を設定していきます。
最小限にとどめる勇気を持つ
超シンプル、超小規模。
そんなことを言いながらやることをリストアップしたら結構な長さになりました。そう、UnityでRPGを作るのは結構大変なんです。正直に言えばRPG Maker Uniteだったり、なんなら本家のRPGツクールMZなどを使った方がはるかに簡単です。そして心折れる確率も少ないかと思います(白目)
それでもUnityで自分でRPGを作りながら必要な実装を学んでいきたいのも開発者のサガなので、作っている途中の「これも追加したいなー」という悪魔の囁きを退けつつ、最小限のストーリーを完成させることを目標に進めたいと思います。
まとめ
超シンプルなRPGを作る上で必要になりそうな作業や考えておきたいことをリストアップしてみました。超シンプルと言いつつも、たくさんの項目を挙げてしまった気がしています。作っているうちに検討項目も増えそうな気がするので、都度更新するかもしれません。
項目のリストアップが終わったので、次はどこから手をつけていくかを考えていきたいと思います。どの部分から着手するとテンションが上がるか、モチベーションが維持できるか、といった点は人によって異なるので正解はないのですが、今回私はこう進めていきますというのを一例としてお出しできればと思います。
Unityで1からRPGを作るのってかなりめんど……大変なので、みんな苦労しているよね、とこの気持ちを共有できたら幸いです。完成までは遠そうですが、頑張って進めていきたいと思うので、どうぞよろしくお願い申し上げます。
ゲーム開発の攻略チャートを作りました!
-
前の記事
【Unity/C#】DictionaryでCapacityを指定して初期化する場合の速度比較 2024.11.15
-
次の記事
記事がありません
コメントを書く