ゲームコンテンツをデータに:HydroのScriptableObject
セリフ、アイテム、クエスト、敵をC++から、インスペクターで編集できるリフレクション付きの定義ファイルとテーブルへ移しました。
10月末には、RPGにNPC、アイテム、ヒーロー、敵、クエストが揃っていましたが、すべてC++で書かれていました。NPCのセリフはキャラクターごとにif (name == "Farmer")のブロックが1つ。セリフを1行足すたびに、コードを編集し、ビルドし、再起動する必要がありました。室内マップや村が増えればそれは倍増し、セーブデータには長く使えるIDを持つコンテンツ(「ポーションを2つ持っている、長老と話した」)が必要です。
UnityにはScriptableObject、UnrealにはData AssetとData Tableがあります。Hydroには定義とデータテーブルができました。
モデル
- 型はC++の構造体で、
HY_DATAを付け、フィールドにはHY_PROPERTY、列挙型にはHY_ENUMを付けます。ジェネレーターがビルド時にリフレクションを生成します。 - 定義(
.hydef)は1レコード:ヒーロー1人、パーティー構成など。 - テーブル(
.hytable)はID付きの行:敵、ドロップ、クエスト、NPCのセリフ。 - どちらもディスク上はYAMLで、アセットマネージャーのアセットとして扱われ、ネストした構造体、リスト、列挙型も含めて、コンポーネントと同じようにインスペクターで編集できます。
ゲームコードはData::Get<T>(handle)やData::GetRow<T>(rowRef)で読みます。ファイルや行がなければ、型のデフォルト値と1回の警告。クラッシュはしません。
シーンに触れずにコンテンツを移す
NPCのセリフをデータから取るために、すべてのシーンのすべてのNPCにスクリプトを追加するのは大変な作業です。代わりに、NPCのセリフはタグ内の名前でNpcs.hytableから探し、載っていないNPCにはDefaultの行を使います。シーンファイルは1つも変わりませんでした。看板、クエストのセリフ、各シーンのライティングのプリセットも同じ方法でデータになりました。
エンジンなしでテスト
ジェネレーター、シリアライザー、検索、編集操作には、ウィンドウもGPUも使わないヘッドレステストがあり、CIでもエージェントのサンドボックスでも動きます。データのバグは、プレイ中の空のダイアログボックスではなく、失敗したテストとして現れます。
それで可能になったこと
ヒーローの追加はテーブルに1行足すだけになりました。バトルのバランス調整はインスペクターで行います。そして、すべてのアイテム、クエスト、敵が長く使えるIDを持つので、セーブデータはIDで状態を保存できます。それが翌日のゲームセッションとセーブを可能にしました。