このページでは、ScriptableObjectをゲームコードのロジックとデータを分離するデータコンテナとして使用する方法を説明します。
これは、Unity開発者を支援するために作成された6つのミニガイドシリーズの第2弾です。 デモこのミニガイドは、電子書籍「Create modular game architecture in Unity with ScriptableObjects」に付属するデモでUnity開発者を支援するために作成された、6つのミニガイドシリーズの第2弾です。
このデモは、古典的なボールとパドルを使ったアーケード・ゲームのメカニズムにインスパイアされたもので、ScriptableObjectが、テスト可能でスケーラブル、かつデザイナーフレンドリーなコンポーネントの作成にどのように役立つかを示しています。
電子書籍、デモプロジェクト、そしてこれらのミニガイドを組み合わせることで、プログラミング・デザイン・パターンを使用するためのベストプラクティスを提供します。 プログラミングデザインパターンを使用するためのベストプラクティスを提供します。これらのヒントは、コードを単純化し、メモリ使用量を減らし、コードの再利用性を促進するのに役立つ。
このシリーズには以下の記事が含まれる:
ScriptableObjectのデモ・プロジェクトやこのミニ・ガイドのシリーズに飛び込む前に、デザイン・パターンの核心は単なるアイデアに過ぎないということを覚えておいてほしい。すべての状況に当てはまるわけではない。これらのテクニックは、UnityとScriptableObjectの新しい使い方を学ぶのに役立ちます。
それぞれのパターンには長所と短所がある。特定のプロジェクトに有益なものだけを選ぶ。デザイナーはUnity Editorを多用していますか?ScriptableObjectベースのパターンは、開発者とのコラボレーションを助ける良い選択かもしれない。
結局のところ、最良のコード・アーキテクチャとは、あなたのプロジェクトとチームに合ったものなのだ。
ソフトウェア開発者は、アプリケーションをより小さく、自己完結的なユニットに分解するモジュール性に関心を持つことが多い。各モジュールは、アプリケーションの機能の特定の側面を担当するようになる。
Unityでは、ScriptableObjectがデータとロジックの分離に役立ちます。
ScriptableObjectは、特に静的なデータの保存に優れている。そのため、ゲームの統計、アイテムやNPCの設定値、キャラクターのダイアログなどに最適です。
ゲームプレイ・データをビヘイビア・ロジックから分離することで、プロジェクトの各独立した部分のテストと保守が容易になる。この "懸念の分離 "は、必要な変更を行う際に、意図しない望ましくない副作用を減らすことができる。
ScriptableObjectのワークフローについて復習したい場合は、Unity Learnの記事が役立ちます。そうでなければ、ここで簡単に説明しよう:
ScriptableObject を定義します:作成するには、ScriptableObject基底クラスを継承するC#クラスを定義し、格納したいデータのフィールドとプロパティを持たせます。ScriptableObject は MonoBehaviours と同じデータ型を保存できるため、汎用性の高いデータコンテナです。EditorからCreateAssetMenuAttributeを追加して、プロジェクトでのアセット作成を簡単にします。
アセットを作る:ScriptableObjectクラスを定義したら、プロジェクト内でそのScriptableObjectのインスタンスを作成することができます。これはディスクに保存されたアセットとして表示され、異なるGameObjectやシーンで再利用することができます。
設定値:アセットを作成したら、インスペクタでフィールドとプロパティの値を設定して、アセットにデータを入力します。
資産を使う:アセットがデータを保持したら、変数やフィールドからそれを参照します。ScriptableObject アセットに加えられた変更は、プロジェクト全体に反映されます。
ScriptableObjectは、ゲームのさまざまな部分でデータコンテナとして再利用できます。例えば、ScriptableObject内で武器やキャラクターのプロパティを定義し、プロジェクト内のどこからでもそのアセットを参照することができます。
注:CreateInstanceメソッドを使えば、実行時にScriptableObjectを生成することもできる。しかし、データを保存する場合は、CreateAssetMenuAttributeを使用して、事前にScriptableObjectアセットを作成するのが一般的です。
ScriptableObjectがMonoBehavioursよりもデータストレージに適している理由を理解するために、それぞれの空のバージョンを比較してみましょう。アセット・シリアライゼーションを モードに設定してください:YAML マークアップをテキストとして表示するには、プロジェクトの設定でテキスト を強制します。
空のMonoBehaviourを持つ新しいGameObjectを作成する。次に、空のScriptableObjectアセットと比較します。並べてみると、上の画像のようになるはずだ。
ScriptableObjectはMonoBehavioursに比べて軽量で、Transformコンポーネントのような後者に関連するオーバーヘッドを持ちません。これにより、ScriptableObjectのメモリ使用量が少なくなり、データ保存に最適化されます。
ScriptableObjectはアセットとして保存されるため、Playモード以外でも保持され、便利です。例えば、ScriptableObjectのデータは、新しいシーンをロードしても、どこからでも利用できます。
Patternsのデモの例では、基本的なクレジット画面が用意されており、自分でテストすることができる。Credits_Data ScriptableObjectを変更し、Updateを押して保存されたテキストが表示されるのを確認します。
膨大な台詞のあるRPGや、あらかじめ台本が用意されているチュートリアル・シーンの場合、これは大量のデータを保存するための一般的な方法です。
ScriptableObject内のデータは変更されると即座に更新されるが、私たちのプロジェクトでは、手動で画面を更新するための更新ボタンが必要である。UIツールキット・ベースのスクリーンは、それ自体を一度だけ構築し、データが変更されたときに通知される必要がある。
更新を自動的に同期させたい場合は、ScriptableObject内にイベントを作成します。例えば、このExampleSOスクリプトは、ExampleValueが変更されるたびにOnValueChangedイベントを呼び出します。以下のコード例を見てほしい。
そして、リスニングしているUIオブジェクトにOnValueChangedをサブスクライブさせ、それに従って更新させる。
ScriptableObjectは、多くのオブジェクトが同じデータを共有するときに輝く。例えば、多数のユニットが同じ攻撃速度と最大体力を持つストラテジーゲームを作る場合、すべてのGameObjectにそれらの値を個別に保存するのは非効率的だ。
その代わりに、共有データを中央の場所に統合し、各オブジェクトがその共有場所を参照するようにすることができる。ソフトウェア設計では、これはフライウェイト・パターンと呼ばれる最適化である。このようにコードを再構築することで、多くの値のコピーを避け、メモリフットプリントを減らすことができる。
PaddleBallSOでは、GameDataSO ScriptableObjectが共有データストレージとして機能する。
共通の設定(スピード、質量、物理的な弾みなど)のコピーを別々に保持するのではなく、パドルとボールのスクリプトは、可能な限り同じGameDataSOインスタンスを参照します。各ゲーム要素は、位置や入力イベントなどの固有のデータを保持するが、可能な限り共有データをデフォルトとする。
わずか2、3個のオブジェクトではメモリの節約は目立たないかもしれないが、共有データの編集は、1個1個手作業で編集するよりも速く、エラーの発生も少ない。
例えば、パドルのスピードを変更する必要がある場合、1箇所でそれを調整すると、すべてのシーンで両方のパドルが更新されます。MonoBehavioursにユニークなフィールドとして保存した場合、1回のクリックミスで簡単に2つの値が同期されなくなります。
データをScriptableObjectにオフロードすることで、バージョン管理にも役立ち、チームメイトが同じシーンやPrefabで作業する際のマージの衝突を防ぐことができます。
GameDataSOは、ScriptableObjectをデータコンテナとして使用する方法を示しています。PaddleBallSOでは、ゲームプレイを構成するための様々な設定が含まれます:
- パドルのデータパドルの速度、抗力、質量などの属性が、ゲームプレイ中のパドルの動きと物理を決定する。
- ボールのデータボールの現在速度、最高速度、バウンス倍率を保持し、シミュレーションと相互作用したときのボールの挙動を制御します。
- 試合データGameDataSOには、試合中のポイント間の遅延に関する情報が含まれており、ゲームのテンポをコントロールするのに役立ちます。
- 選手IDPlayerIDSO ScriptableObjectは、各選手(例えば、Player1とPlayer2)のチーム識別として機能する。
- 選手のスプライト:これらのオプションのスプライトは、プレイヤーアバターのカスタマイズを可能にします。
- レベルレイアウト:LevelLayoutSOオブジェクトは、プレイヤーやゴールや壁のようなゲーム要素の開始位置を定義します。
GameDataSOは、これらの設定やデータをすべて一元管理しているため、どのオブジェクトでもこの共有データを利用することができます。これにより、オブジェクトの管理方法が簡素化され、プロジェクト全体の一貫性が高まります。パドルの物理を変える?複数のスクリプトを調整する代わりに、ここで1つの変更を加える。
時には、ケーキを食べながら、それを食べることもできる。二重シリアライゼーションを使用すると、データを ScriptableObject に格納すると同時に、別の形式で保持することができます。
LevelLayoutSOスクリプトはこの概念を示している。パドルとボールのスタート位置を保持するだけでなく、壁とゴールのトランスフォームデータをカスタム構造体に格納します。
これらの値は、ExportToJsonメソッドを使ってディスクに書き出すことができる。JSONファイルは人間が読めるテキストなので、Unityの外で簡単に変更できます。これにより、エディターでScriptableObjectを操作し、そのデータをJSONやXMLファイルのような別の場所に保存することができます。
JSONやXMLのようなファイル形式はエディターで扱うのは難しいかもしれませんが、Unityの外でテキストエディタで修正するのは簡単です。これにより、カスタムレベルやユーザー改造レベルの可能性が広がる。
GameSetupスクリプトは、LevelLayout ScriptableObjectまたは外部JSONファイルを使用してゲームレベルを生成できます。
カスタムモディファイされたレベルをロードするには、セットアップスクリプトは、CreateInstance を使用して実行時に ScriptableObject を生成します。そして、JSONファイルからテキストを読み込み、ScriptableObjectに入力する。
カスタムデータは ScriptableObject の内容を置き換え、この外部改造レベルを他のレベルのように使用できるようにします。残りのアプリケーションはスイッチに気づかず、通常通り機能する。
このパドル・ボールのミニゲームでは、ScriptableObjectデータ・コンテナのすべての使用例を示すことはできませんが、あなた自身のアプリケーションでは、次のように考えてみてください:
- ゲーム構成:定数やゲームルールなど、ゲームプレイ中に変更する必要のない設定パラメータについて考えてみましょう。他のコンポーネントは、ハードコードされた値を使うことなく、このコンフィギュレーション・データを参照することができる。
- キャラクターと敵の属性:ScriptableObjectを使用して、ヘルス、攻撃力、スピードなどの属性を定義します。これにより、開発者がいなくても、デザイナーがゲームプレイ要素のバランスや調整を行うことができます。
- 在庫と品目システム:名前、説明、アイコンなどのアイテム定義やプロパティは、ScriptableObjectに最適です。また、プレイヤーが収集、使用、装備したアイテムを追跡するインベントリ管理システムの一部として使用することもできます。
- 対話と物語のシステム:ScriptableObjectには、ダイアログテキスト、キャラクター名、ダイアログパスの分岐、その他のシナリオ関連データを格納できます。複雑な対話システムの基礎を築くことができる。
- レベルおよびプログレッションのデータ:ScriptableObject を使用して、レベル レイアウト、敵のスポーン ポイント、目標、およびその他のレベル関連情報を定義できます。
- オーディオクリップPaddleBallSOプロジェクトにあるように、ScriptableObjectには1つ以上のオーディオクリップを格納することができます。ゲームの複数のパートにまたがるオーディオエフェクトや音楽を定義できます。
- アニメーションクリップ:ScriptableObjectは、アニメーションクリップを保存するために使用することができます。これは、複数のGameObjectやキャラクタ間で共有される共通のアニメーションを定義するのに便利です。
ScriptableObjectsをより深く理解し、自分のプロジェクトに合わせてカスタマイズしていけば、さらに多くの用途が見えてくるでしょう。特にデータの管理には便利で、さまざまなゲーム要素で一貫性を維持しやすくなる。
ScriptableObjectsを使ったデザインパターンについて、電子書籍で詳しく読む UnityでScriptableObjectsを使用してモジュラーゲームアーキテクチャを作成する.また、一般的なUnity開発のデザインパターンについては、以下をご覧ください。 ゲームプログラミングパターンでコードをレベルアップ.