ScriptableObjectがあなたのチームとコードに利益をもたらす6つの方法

新しい技術電子書籍の発売をお知らせします、 UnityでScriptableObjectsを使用したモジュラーゲームアーキテクチャを作成するプロの開発者によるScriptableObjectsをプロダクションに導入するためのベストプラクティスを紹介しています。
電子書籍と一緒に、古典的なボールとパドルのアーケードゲームのメカニズムにインスパイアされたデモ・プロジェクトをGitHubからダウンロードできる。このデモでは、ScriptableObjectが、テスト可能でスケーラブルでありながら、デザイナーに優しいコンポーネントの作成にどのように役立つかを紹介している。このようなゲームは、もっと少ないコード行数で作ることができるが、このデモはScriptableObjectsの動作を示している。

この記事では、ScriptableObjectsの利点について説明しますが、Unityの基本や一般的なコーディングについては説明しません。Unityでのプログラミングが初めての方は、Unity Learnへどうぞ。Unity Learnでは、役立つ入門チュートリアルを提供しています。電子書籍の第1章も、しっかりとした入門書を提供している。
あなたのプロジェクトでScriptableObjectを使うことで得られる6つの方法を見てみましょう。もっと知りたい方へこれらの例はすべて、電子書籍とデモ・プロジェクトでさらに掘り下げられている。
ここで紹介するテクニックの多くは、C#のクラスを使っても実現できるが、ScriptableObjectsの主な利点のひとつは、アーティストやデザイナーが利用しやすいことだ。ScriptableObjectを使用することで、コードを編集することなく、プロジェクト内のゲームロジックを設定し、適用することができます。
エディターは、ScriptableObjectの表示と編集を便利にし、デザイナーが開発チームからの重いサポートを受けずにゲームプレイデータを設定できるようにします。これはゲームロジックにも当てはまり、例えばScriptableObjectを追加してNPCに動作を適用させることができます(以下のパターンで説明します)。
データとロジックを1つのMonoBehaviourに保存すると、2人が同じPrefabやシーンの異なる部分を変更した場合、時間のかかるマージコンフリクトが発生します。ScriptableObjectsを使用して共有データを小さなファイルやアセットに分割することで、デザイナーは開発者と並行してゲームプレイを構築することができます。
異なる役割を持つ同僚がゲームコードやアセットに同時にアクセスすると、問題が発生する可能性がある。ScriptableObjectsを使えば、プログラマーはプロジェクトのどの部分をエディターで編集可能にするかをコントロールできる。さらに、ScriptableObjectを使用してコードを整理すると、コードベースがモジュール化され、テストが効率的になります。
システムゲームデザインとUnity(C#)を専門とするシニアテクニカルゲームデザイナー、クリスト・ノッブスが寄稿しました。 Unityゲームデザイナー・プレイブックに寄稿し、Unityでのゲームシステム設計に関するブログ記事シリーズのメイン執筆者でもある。彼の投稿「生態系を創造するシステム」:創発的ゲームデザイン" と "予測できない面白さ:ゲームデザインにおけるランダム化の価値" は、デザイナーがScriptableObjectsをどのように使うことができるかという興味深い例を示している。
モジュール性は、ScriptableObjectsを使わなくてもC#で実装できる一般的なソフトウェアの原則である。しかし、前述したように、ScriptableObjectはデータとロジックを分離することで、クリーンなコーディングの実践を促進し、モジュール化されたゲームコードへの第一歩となります。この分離は、意図しない副作用を引き起こすことなく変更を加えやすくし、テスト容易性を向上させることを意味する。
ScriptableObjectは静的なデータを保存するのが得意なので、アイテムやNPCのステータス、キャラクターのダイアログなど、静的なゲームプレイ値を設定するのに便利です。ScriptableObjectはアセットとして保存されるため、ゲームモード以外でも持続し、実行時に動的に変更される静的な設定でのロードに使用することができます。
ScriptableObjectデータへの変更はエディター内に残りますが、ゲームデータを保存するためのものではないことに注意してください。その場合は、JSONやXMLなどのシリアライゼーション・システムを使うか、パフォーマンスが重要な場合はバイナリー・ソリューションを使う方がよい。
MonoBehavioursは、GameObject(デフォルトではTransform)をホストとして動作させる必要があるため、余分なオーバーヘッドが発生します。つまり、1つの値を保存する前に、多くの未使用データを作成する必要がある。ScriptableObjectは、このメモリフットプリントを削減し、GameObjectとTransformを削除します。また、データをプロジェクト・レベルで保存するため、複数のシーンから同じデータにアクセスする必要がある場合にも便利だ。
多くのGameObjectが、実行時に変更する必要のない重複したデータに依存しているのはよくあることだ。このようにGameObjectごとに重複したローカルデータを持つのではなく、ScriptableObjectに流すことができます。各オブジェクトは、データそのものをコピーするのではなく、共有データ資産への参照を保存する。これにより、何千ものオブジェクトを扱うプロジェクトにおいて、大幅なパフォーマンス向上が期待できる。


ソフトウェア設計では、これはフライウェイト・パターンと呼ばれる最適化である。このようにScriptableObjectを使ってコードを再構築すると、値のコピーが避けられ、メモリ・フットプリントが削減される。電子書籍をご覧ください、 ゲームプログラミングパターンでコードをレベルアップをご覧ください。
ScriptableObjectがコードを簡素化する良い例は、比較演算のための列挙型として使用することである。ScriptableObjectは、特殊なダメージ効果(冷気、熱、電気、魔法など)のようなカテゴリーやアイテムの種類を表すことができます。
ゲームアイテムを装備するためのインベントリシステムが必要なアプリケーションの場合、ScriptableObjectはアイテムタイプや武器スロットを表すことができます。インスペクタのフィールドは、ドラッグ・アンド・ドロップで設定できるインターフェースとして機能します。

ScriptableObjectをenumとして使うのは、それを拡張してさらにデータを追加したいときに面白くなる。通常の列挙型とは異なり、ScriptableObjectは余分なフィールドやメソッドを持つことができる。ルックアップテーブルを別に用意したり、新しいデータ配列と関連付けたりする必要はない。
従来の列挙型は値のセットが固定されているのに対し、ScriptableObjectの列挙型は実行時に作成・変更できるため、必要に応じて値を追加したり削除したりできる。
明示的な番号付けのない列挙値の長いリストがある場合、列挙値を挿入したり削除したりすると、それらの順序が変わってしまうことがある。この並べ替えは、微妙なバグや意図しない動作を引き起こす可能性がある。ScriptableObjectベースの列挙型にはこのような問題はない。毎回コードを変更することなく、プロジェクトの削除や追加ができる。
RPGで装備可能なアイテムを作りたいとする。そのためには、ScriptableObjectにブーリアン・フィールドを追加すればよい。特定のキャラクターは特定のアイテムを持つことができないのですか?魔法のようなアイテムもあれば、特別な能力を持つアイテムもある?ScriptableObjectベースの列挙型ならそれができる。
ScriptableObjectにはメソッドを作成できるので、データを保持するのと同様に、ロジックやアクションを格納するのにも便利です。ロジックをMonoBehaviourからScriptableObjectに移動すると、後者をデリゲートオブジェクトとして使用できるようになり、ビヘイビアがよりモジュール化されます。
特定のタスクを実行する必要がある場合は、そのアルゴリズムを独自のオブジェクトにカプセル化することができる。オリジナルのギャング・オブ・フォーは、この一般的なデザインを戦略パターンと呼んでいる。以下の例では、抽象クラスを使ってEnemyAIを実装することで、Strategyパターンをより便利なものにする方法を示している。その結果、動作の異なる複数のScriptableObjectが派生し、各アセットが交換可能であるため、プラグイン可能な動作となる。選択したScriptableObjectをMonoBehaviourにドラッグ&ドロップするだけです。

ScriptableObjectsを使用して動作を駆動する方法を示す詳細な例については、ビデオシリーズ「Pluggable AI with ScriptableObjects」をご覧ください。これらのセッションは、状態、アクション、および状態間の遷移をスクリプト可能なオブジェクトを使って設定できる、有限状態機械をベースとしたAIシステムのデモである。
大規模なプロジェクトでよくある課題は、複数のGameObjectがデータやステートを共有する必要がある場合、これらのオブジェクト間の直接参照を避けることです。このような依存関係を大規模に管理することは、多大な労力を必要とし、しばしばバグの原因となる。多くの開発者はシングルトンを使っている。シングルトンとは、シーンのローディングに耐えうる、クラスの1つのグローバルなインスタンスである。しかし、シングルトンはグローバルな状態をもたらし、単体テストを困難にする。シングルトンを参照するPrefabで作業している場合、孤立した関数をテストするためだけにその依存関係をすべてインポートすることになります。これではコードがモジュール化されず、デバッグの効率も悪くなる。
一つの解決策は、GameObjectの通信を助けるためにScriptableObjectベースのイベントを使うことです。この場合、ScriptableObjectを使って、オブザーバー・デザイン・パターンの一種を実装していることになる。オブザーバー・デザイン・パターンでは、サブジェクトが1つ以上の疎結合オブザーバーにメッセージをブロードキャストする。各観測対象は、対象から独立して反応することができるが、他の観察者には気づかない。主体は "パブリッシャー "または "ブロードキャスター "と呼ばれ、オブザーバーは "サブスクライバー "または "リスナー "と呼ばれることもある。
オブザーバーパターンは、MonoBehaviours またはC# オブジェクトで実装できます。これはUnity開発ではすでに一般的な手法ですが、スクリプトのみのアプローチでは、ゲームプレイ中に必要なすべてのイベントをデザイナーがプログラミングチームに頼ることになります。

一見すると、オブザーバー・パターンにオーバーヘッドのレイヤーを追加したように見えるが、この構造にはいくつかの利点がある。ScriptableObjectはアセットなので、階層内のすべてのオブジェクトからアクセスでき、シーンのロード時に消えることはありません。
特定のリソースに簡単に永続的にアクセスできることが、多くの開発者がシングルトンを使う理由だ。ScriptableObjectは、不必要な依存関係を増やすことなく、同じ利点を提供できることが多い。
ScriptableObject ベースのイベントでは、任意のオブジェクトがパブリッシャー(イベントをブロードキャストする)となり、任意のオブジェクトがサブスクライバー(イベントをリッスンする)となることができます。ScriptableObjectはその中間に位置し、信号の中継を助け、2つの間の中央集権的な仲介者のような役割を果たす。
これを "イベント・チャンネル "として考える方法もある。ScriptableObjectを電波塔に見立て、いくつものオブジェクトがその電波を受信していると想像してほしい。興味のあるMonoBehaviourはイベントチャンネルを購読し、何かが起こったときに応答することができます。
このデモでは、オブザーバーパターンが、UI、サウンド、得点などのゲームイベントの設定にどのように役立つかを紹介する。
実行時に、シーン内のGameObjectやコンポーネントのリストを追跡する必要があることがよくあります。例えば、敵のリストは頻繁にアクセスする必要があるものだが、敵が増えたり倒されたりすると変化する動的なリストでもある。シングルトンはグローバル・アクセスを容易にするが、いくつかの欠点がある。シングルトンを使用する代わりに、"ランタイム・セット "としてScriptableObjectにデータを保存することを検討してください。ScriptableObjectインスタンスはプロジェクト・レベルで表示されるため、どのシーンのどのオブジェクトでも利用可能なデータを保存でき、同様のグローバル・アクセスを提供します。データは資産にあるので、その公開項目リストにはいつでもアクセスできる。
この使用例では、要素のパブリック・コレクションを維持しながら、コレクションに追加したり、コレクションから削除したりする基本的なメソッドも提供する、特別なデータ・コンテナが得られます。これにより、シングルトンの必要性を減らし、テスタビリティとモジュール性を向上させることができる。

ScriptableObject から直接データを読み込むことは、Object.FindObjectOfTypeやGameObject.FindWithTag のような検索操作でシーン階層を検索するよりも最適です。ユースケースや階層のサイズにもよりますが、これらの方法は比較的高価で、フレームごとの更新には非効率的です。
ScriptableObjectsフレームワークには、これら6つのシナリオ以上の使用例を提供するものがいくつかある。ScriptableObjectを広範囲に使用するチームもあれば、静的データのロードとロジックとデータの分離に限定して使用するチームもある。最終的には、プロジェクトのニーズによって使い方が決まる。
スクリプタブルオブジェクトを使ってUnityでモジュラーゲームアーキテクチャを作成するは、中級から上級のUnityプログラマー向けのシリーズの第3弾ガイドです。経験豊富なプログラマーが執筆した各ガイドは、開発チームにとって重要なトピックに関するベストプラクティスを提供している。
C#スタイルガイドを作成する:スケールアップする、よりクリーンなコードを書く スタイルガイドの開発を支援し、よりまとまりのあるコードベースを作成するためのアプローチの統一を支援します。
ゲームプログラミングパターンでコードをレベルアップSOLIDの原則と一般的なプログラミングパターンを使用して、Unityプロジェクトでスケーラブルなゲームコードアーキテクチャを作成するためのベストプラクティスを紹介します。
このシリーズは、経験豊富なクリエイターに実用的なヒントやインスピレーションを提供するために作りましたが、ルールブックではありません。Unityプロジェクトの構成にはさまざまな方法があり、あるアプリケーションには自然に適合すると思われるものでも、別のアプリケーションには適合しないことがあります。各推奨、ヒント、パターンの利点と欠点を同僚と評価してから導入する。
Unityのベストプラクティスハブで、より高度なガイドと記事をご覧ください。

