Unityのアセット
概要
Quantum は Unity で利用可能な各アセットタイプに対して、 ScriptableObject
ベースのラッパーパーシャルクラスを生成します。このようなラッパーのベースとなるクラスが AssetBase
です。AssetBase
のインスタンスを管理するメインクラスは UnityDB
と呼ばれます。QuantumはUnityで利用できる各アセットに対して、ScriptableObject
ベースのラッパーパーシャルクラスを生成します。このラッパーのベースクラスはAssetBase
です。AssetBase
インスタンスを管理するメインクラスは、UnityDB
という名前です。
起動時に、シミュレーションに全ての利用可能なアセットのAssetGuids
を知らせる必要があります。
知らせる方法は以下の通りです。
エディター内:
QuantumEditorSettings.AssetSearchPaths
で定義された場所(デフォルトではAssets/Resources/DB
)から収集したすべてのAssetBase
アセットです。- 各
AssetBase
には、AssetGuid
とAssetBase
を実行時に読み込むために必要な情報を含む生成されたエントリーがあります。 - エントリーは
QuantumEditorSettings.AssetResourcePath
で定義されたAssetResourceContainer
アセットに保存されます(デフォルトではAssets/Resources/AssetResources.asset
です)。
ランタイム時:
- 最初に
UnityDB
のいずれかのメンバーが使用されると、Resources.Load
を使ってAssetResourceContainer
が 読み込まれます (QuantumEditorSettings.AssetResourcePath
に基づいています)。 - エントリーのリストは、各アセットを動的に読み込むために必要な情報とともに、シミュレーションの
IResourceManager
を初期化するために使用されます。
- 最初に
現在データベースの一部となっているAsset Objectのリストを参照するには、Quantum/Show AssetDB Inspector
およびWindow/Quantum/AssetDB Inspector
メニュー項目からアクセス可能なAssetDB Inspectorを使用します。
Finding Quantum Assets in Unity scripts
ユーザーが作成したすべてのコンクリートクラスには、Unity側で作成された対応するクラスが与えられ、実際のUnityスクリプタブルオブジェクトとしてインスタンス化を有効にすることができるようになります。
例:CharacterData
と名前のついてQuantumアセットはUnityではCharacterDataAsset
という名前の付いたクラスが与えられます。ここには必ずAsset
という言葉がサフィックスとして追加されます。Unityクラスには常に、シミュレーション固有のフィールドにアクセスするためQuantumクラスにキャストされうるAssetObject
というプロパティが含まれます。
Unity側のアセットを探すには、UnityDB
クラスを使用します。以下では、Quantumアセット宣言の完全なスニペットとUnityでそのフィールドにアクセスする方法をご紹介します。
Quantum側:
C#
// in any .qtn file:
asset CharacterData;
// in a .cs file:
public unsafe partial class CharacterData
{
public FP MaximumHealth;
}
Unity側:
C#
var characterDataAsset = UnityDB.FindAsset<CharacterDataAsset>(myAssetRef.Id);
var characterData = characterDataAsset.Settings;
FP maximumHealth = characterData.MaximumHealth;
リソース、アドレサブル、アセットバンドル
Quantum は決して AssetBase
アセットへのハードリファレンスを形成しません。これにより、あらゆる動的コンテンツ配信を利用することができます。以下のアセット読み込み方法が、そのままサポートされています。
- リソース
- アドレサブル(明示的に有効化する必要があります)
- アセットバンドル (実証実験として、アセットバンドルには高度にカスタマイズされた、プロジェクトごとのアプローチが必要なため)
AssetBase
を上記のいずれかの方法で動的に読み込むために、特別な手順は必要ありません。各アセットの読み込み方法の詳細は AssetResourceContainer
に格納されています。この情報は、シミュレーションが Frame.FindAsset
を呼び出したとき、または UnityDB.FindAsset
を呼び出したときにアクセスされ、適切な読み込み方法が使用されるように導かれます。
上記のメソッドを使用すれば、AssetBase
を動的に読み込めるようにするためにその他の手順は不要です。各アセットを読み込む際の詳細は、AssetResourceContainer
に保存されています。この情報は、シミュレーションがFrame.FindAsset
を呼んだ場合、またはUnityDB.FindAsset
が呼ばれた場合に、適切な読み込みメソッドを使用する必要が生じた際にアクセスされます。
- アセットが
Resource
フォルダにある場合、Resources
API を使用して読み込まれます。
アセットがResource
フォルダ内にある場合、アセットはResources
APIを使用して読み込まれます。 - アセットにアドレスがある場合(明示的または暗黙的)、
Addressables
API を使用して読み込まれます。
アセットにアドレスがある場合(明示的または暗黙的)、アセットはAddressables
APIを使用して読み込まれます。 - アセットがアセットバンドルに属している場合(明示的または暗黙的に)、
AssetBundle
API を使用してアセットを読み込もうと試みます。
アセットがアセットバンドルに属する場合(明示的または暗黙的)、AssetBundle
APIを使用したアセットの読み込みが試行されます。
アセットのリスト(AssetResourceContainer
)自体を動的にするためには、いくつかの追加コードが必要です。詳細については、Updating Quantum Assets At Runtime の項を参照してください。
アセットのリスト(AssetResourceContainer
)を動的にするには、コードの追加が必要です。詳細な情報は、ランタイム時にQuantumアセットを更新セクションを参照してください。
ユーザースクリプトは、Quantumアセットを参照する際に、AssetBase
参照(例:SimulationConfig
)の代わりに AssetRef
型(例:AssetRefSimulationConfig
)を用いて、ハードリファレンスを回避することができます。
Quantumアセットを参照する際、AssetBase
参照(たとえばSimulationConfig
)の代わりにAssetRef
型(たとえばAssetRefSimulationConfig
)を使用すれば、ユーザースクリプトはハード参照を回避できます。
C#
public class TestScript : MonoBehaviour {
// hard reference
public SimulationConfigAsset HardRef;
// soft reference
public AssetRefSimulationConfig SoftRef;
void Start() {
// depending on the target asset's settings, this call may result in
// any of the supported loading methods being used
SimulationConfigAsset config = UnityDB.FindAsset<SimulationConfigAsset>(SoftRef.Id);
}
}
Unityでアセットをドラッグアンドドロップ
アセットインスタンスを追加し、シミュレーション内からFrameクラスで検索しても、システムはそれ以上のことはできません。このため、アセットインスタンスにデータベースへの参照を持たせ、Unity エディター内でそれらの参照をドラッグアンドドロップできるようにすることで、利便性の高いソリューションが生まれました。
よくある使い方としては、ビルド前の RuntimePlayer
クラスを拡張して、プレイヤーが選択した特定の CharacterSpec
アセットへの AssetRef
を含めることです。生成されたタイプセーフな asset_ref
型は、アセットや他の設定オブジェクト間の参照をリンクするために使用されます。
よくある利用法としては、ビルド前のRuntimePlayer
クラスを拡張し、プレイヤーが選択した特定のCharacterSpec
アセットにAssetRef
を含めることです。生成された型セーフなasset_ref
型はアセットや他の設定オブジェクト間の参照のリンクに使用されます。
C#
// this is added to the RuntimePlayer.User.cs file
namespace Quantum {
partial class RuntimePlayer {
public AssetRefCharacterSpec CharacterSpec;
partial void SerializeUserData(BitStream stream) {
stream.Serialize(ref CharacterSpec);
}
}
}
このスニペットでは、asset_ref
フィールドを生成し、CharacterSpec
タイプのアセットへのリンクのみを受け付けることができます。このフィールドはUnityインスペクタに表示され、スロットにアセットをドラッグ&ドロップすることで入力することができます。
このスニペットでは、asset_ref
フィールドを生成し、このフィールドはCharacterSpec
型のアセットへのリンクのみを受容します。このフィールドはUnityインスペクタ―に表示され、アセットをスロットにドラッグアンドドロップすることで入力できます。
マップアセット・ベイキングパイプライン
Quantumでカスタムデータを生成するためのもう一つのエントリーポイントは、マップベーキングパイプラインです。MapAsset
は Map
アセットの AssetBase
ベースラッパーです。
Map
アセットは Quantum シミュレーションに必要なもので、Navmeshes や静的コライダーなどの基本情報が含まれています。追加のカスタムデータは、カスタムアセットスロットに置かれたアセットの一部として保存することができます。カスタムアセットには、初期化時や 実行時に使用する静的データを格納することができます。典型的な例としては、位置、スポーンタイプなどのスポーンポイントデータの配列があります。
Unity のシーンに MapAsset
を関連付けるには、MapData
MonoBehaviour
コンポーネントがシーン内の GameObject
に存在する必要があります。MapData.Asset
が有効な MapAsset
を指すようになると、ベイク処理を行うことができるようになります。デフォルトでは、Quantumはシーンの保存時やプレイモードに入った時に、ナビメッシュ、静的コルディア、シーンのプロトタイプを自動的にベイクしますが、この動作はQuantumEditorSettings
で変更することができます。
ベイクが行われるたびに呼び出されるカスタムコードを割り当てるには、抽象的な MapDataBakerCallback
クラスを継承したクラスを作成します。
C#
public abstract class MapDataBakerCallback {
public abstract void OnBake(MapData data);
public abstract void OnBeforeBake(MapData data);
public virtual void OnBakeNavMesh(MapData data) { }
public virtual void OnBeforeBakeNavMesh(MapData data) { }
}
そして、必須の OnBake(MapData data)
と OnBakeBefore(MapData data)
メソッドをオーバーライドしてください。
C#
public class MyCustomDataBaker: MapDataBakerCallback {
public void OnBake(MapData data) {
// any custom code to live-load data from scene to be baked into a custom asset
// generated custom asset can then be assigned to data.Asset.Settings.UserAsset
}
public void OnBeforeBake(MapData data) {
}
}
Addressable アセットのプリロード
Quantum では、アセットが同期的に読み込み可能である必要があります。
v1.16以降
1.17以前のAddressablesバージョンでは、シミュレーション開始前にプリロードするか、UnityのSyncAddressables
サンプルを使用する以外に、Addressableアセットを同期的に読み込む手段はありませんでした。
v1.17以前
WaitForCompletion
は Addressables 1.17 で追加され、アセットを同期して読み込む機能が追加されました。Quantum でこれを有効にするには、 QUANTUM_ADDRESSABLES_USE_WAIT_FOR_COMPLETION
を定義するか、 QuantumEditorSettings
アセットの Build Features
セクションのトグル を使用します。
同期読み込みも可能ですが、プリロードの方が望ましい場合もあります。 QuantumRunnerLocalDebug.cs
スクリプトは、これを実現する方法を示しています。
AssetBase の読み込み情報をベイクする
AssetResourceContainer
は ScriptableObject
で、各 AssetBase
を読み込んで AssetGuids
にマッピングするための情報を含んでいます。
メニューオプション Quantum > Generate Asset Resources
を使用するか、QuantumEditorSettings.AssetSearchPaths
のいずれかにあるアセットをインポートするたびに、AssetResourceContainer
は QuantumEditorSettings.AssetResourcePath
で指定した場所に完全に再作成されます。
AssetResourceContainer
の作成時に、任意の QuantumEditorSettings.AssetSearchPaths
にある各 AssetBase
はグループに割り当てられます。デフォルトでは、次の3つのグループが存在します。
ResourcesGroup
AssetBundlesGroup
AddressablesGroup
アセットをどのグループに割り当てるかを決定するプロセスは、下図のとおりです。
以下の場合、アセットはAddressableであるとみなされます。
- アドレスが割り当てられている。
- その親フォルダのいずれかがAddressableである。
- 他のAddressableアセットの中にネストされている場合。
同じロジックが、アセットが Asset Bundle の一部であるかどうかを決定する際にも適用されます。
アセットをインポートするときに AssetBase
をベイクしないようにするには、QuantumEditorSettings.UseAssetBasePostprocessor
のチェックボックスをオフにします。
Quantum Assets をビルドで更新する
外部CMSからデータアセットを提供することは可能です。これは、既にリリースされたゲームに対して、プレイヤーがアップデートしなければならない新しいビルドを作成することなく、バランス調整のアップデートを提供する場合に特に有用です。
このアプローチでは、キャラクターのスペック、マップ、NPCのスペックなど、データ駆動型の側面に関する情報を含むバランス調整シートを、ゲームのビルド自体から独立して更新することができます。この場合、ゲームクライアントは常にCMSサービスに接続し、アップデートがあるかどうかを確認し、(必要であれば)オンラインマッチを開始または参加する前にゲームデータを最新バージョンにアップグレードしようとします。
既存アセットの更新
Addressable または Asset Bundle を使用することをお勧めします。Addressable または Asset Bundle の一部である AssetBase
は、適切なメソッドを使用して実行時に読み込まれます。
ゲームシミュレーション中にアセットをダウンロードすることによる、予測できない遅延スパイクを避けるために、アセットをダウンロードし、プリロードすることを検討してください。Addressable Assetsのプリロード](#preloading_addressable_assets)。
新しいアセットを追加
エディタで生成される AssetResourceContainer
には、その作成時に存在したすべてのアセットのリストが含まれます。もし、プロジェクトの動的コンテンツに、新しいビルドを作成せずに新しい Quantum アセットを追加する場合、リストを更新する方法を実装する必要があります。
これを達成するために推奨される方法は、部分的な UnityDB.LoadAssetResourceContainerUser
メソッドの拡張を使用することです。最初のシミュレーションを開始するとき、あるいは UnityDB
メソッドを呼び出すとき、Quantum は AssetResourceContainer
を読み込もうと試みます。デフォルトでは、AssetResourcesContainer
は QuantumEditorSettings.AssetResourcePath
にある Resource であると仮定されます。この挙動をオーバーライドするには、UnityDB.LoadAssetResourceContainerUser
を実装する必要があります。
実装例
まず、AssetResourceContainer
を Resources
フォルダから移動する必要があります。これは、QuantumEditorSettings.AssetResourcePath
を設定することで行います。
次に、新しい AssetResourceContainer
は Addressable にする必要があります。
最後に、パーシャルメソッドを実装したスニペットです。
C#
partial class UnityDB {
static partial void LoadAssetResourceContainerUser(ref AssetResourceContainer container) {
var path = QuantumEditorSettings.Instance.AssetResourcesPath;
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying) {
container = UnityEditor.AssetDatabase.LoadAssetAtPath<AssetResourceContainer>(path);
Debug.Assert(container != null);
return;
}
#endif
var op = Addressables.LoadAssetAsync<AssetResourceContainer>(path);
container = op.WaitForCompletion();
Debug.Assert(container != null);
}
}
DynamicAssetDB による新しいアセットの追加
新しいアセットを決定論的な方法で作成できる場合、DynamicAssetDB
を使用することができます。ダイナミックアセット](./assets-simulation#dynamic_assets)。