マップベイク
概要
Quantumでは、マップはUnityシーンとQuantumMapData
の2つで構成されます。
Unityシーンはマップとあわせてロードされ、EntityView
のようなマップの視覚要素を含みます。Unityのゲームオブジェクトは、ゲームのエンティティのレンダリングを担当します。
一方QuantumMapData
は、決定論的シミュレーションでゲームプレイを進行するために使用されるマップの情報を含みます。
マップベイク
QuantumSampleGame
シーンには、SampleMap
アセットを参照するQuantumMapData
コンポーネントが付いています。SampleMap
はシーンのResources
フォルダーにあります。
新しいゲームシーンを作成する際に、新しいQuantumMapData
コンポーネントを作成/設定する方法はいくつかあります。
- 上部ツールバー(
Quantum/Setup/Create New Quantum Scene
)から、新しいQuantumシーンを作成する。 - または、既に作成されたシーンをQuantumシーンに変換する(
Quantum/Setup/Add Quantum To Current Scene
)。 - または、マップを手動で設定する。
QuantumMapData
コンポーネントのゲームオブジェクトを作成し、コンテキストメニュー(Create/Quantum/Assets/Map
)から新しいMap
アセットを作成します。
プロジェクトのQuantumEditorSettings
からは、シーン保存時・再生時・アプリビルド時などでの自動マップベイクの有効/無効を切り替えることができます。
自動マップベイクを無効にすることが便利な場面もありますが、手動マップベイクは時間がかかりパイプラインでヒューマンエラーが起こる可能性に注意することが重要です。そのため一般的なほとんどのプロジェクトでは、自動マップベイクを有効にしておくことを推奨します。

Quantumでシーンをベイクするには、シーン内のゲームオブジェクトにQuantumMapData
コンポーネントが存在する必要があります。さらに、ナビメッシュが存在する場合は、QuantumMapNavMeshUnity
コンポーネントも必要です。Quantum SDKで提供されているQuantumSAmpleGame
シーンには、必要なセットアップが行われたQuantumMapData
が用意されています。
QuantumMapData
コンポーネントには、必要に応じて手動でベイクプロセスをトリガーできるボタンが含まれています。Bake All Mode
は、ベイクプロセスの特定のステップをスキップするように調整できます。

QuantumMapData
Quantumがマップをベイクすると、Resources/DB/Configs
以下にMap
アセットが生成されます。必要に応じて、このアセットを他の場所に移動させることも可能です。
マップが再ベイクされると値は上書きされてしまうため、Map
のフィールド値は通常、手動で変更しないようにすることが重要になります。
Map
アセットのUser Asset
フィールドを使用すると、任意のアセットをマップに注入して、シミュレーション側で取得できるようにすることが可能です。これを行うには、インスペクターからアセットを手動でリンクするか、カスタムマップベイクコールバックからアセットを割り当ててください。以下に例を示します。
Quantum Editor Settings BakeMapData
Quantumのマップベイクコールバックによって、QuantumMapData
ベイクプロセスに独自処理を差し込むことができます。マップベイクコールバックを実装するには、MapDataBakerCallback
を継承したクラスを作成します。また、アセンブリをカスタム属性でマークする必要があるため、ファイルに[assembly: Quantum.QuantumMapBakeAssemblyAttribute]
を追加してください。これはアセンブリ全体をマークするため、複数のファイルに属性を追加する必要はありません。以下に実装例を示します。
C#
[assembly: Quantum.QuantumMapBakeAssemblyAttribute]
namespace Quantum
{
public class ExampleMapDataBaker : MapDataBakerCallback
{
public override void OnBeforeBake(QuantumMapData data)
{
}
public override void OnBake(QuantumMapData data)
{
}
}
}
QuantumのMapDataBakerCallback
属性を使用すると、複数のカスタムマップベイクコールバックの実行順序を指定できます。カスタムマップベイクコールバッククラスに属性を追加して、invokeOrder
値を指定してください。マップベイクプロセスにおいて、値が小さいコールバックが先に実行されます。
次に例を示します。
C#
[MapDataBakerCallback(invokeOrder:5)]
public class ExampleMapDataBaker : MapDataBakerCallback
OnBeforeBake
は、他のMapData
ベイクが実行される前に呼び出されます。これによって例えば、Unityシーンにコンポーネントを追加/削除したりする調整を行うことができます。
OnBake
は、組み込みのマップベイクのステップが実行された後、Map
アセットが保存される前に呼び出されます。これによって、Map
にベイクされたすべてのデータにアクセスして調整できます。
必要に応じて、仮想コールバックをオーバーライドできます。OnBeforeBakeNavmesh
・OnCollectNavMeshBakeData
・OnCollectNavMeshes
・OnBakeNavMesh
、そしてベイクフラグとベイクトリガーを提供するOnBeforeBake
のオーバーロードがあります。
独自データをマップに追加
ゲームマップには、コライダーやナビメッシュ以外にもゲームプレイに重要な要素が含まれることがあります。含める必要があるデータが不変である場合は、マップ上のエンティティに追加することは必須ではなく、UnityコンポーネントMapData
のUser Asset
フィールドを使用して、任意のデータをシミュレーションに渡すことができます。これによって、オブジェクトの位置やゲームルールなどをマップに含めることが可能です。User Asset
フィールドに割り当てられたデータは、シミュレーション内でアクセスし使用することができます。
例:スポーンポイント
マップベイク時に独自データを利用する例として、ベイクされたマップにスポーンポイントを含めます。デザイナーはその後、Unityシーン上にスポーンポイントを設置して自由に移動させることができます。
まず、DSLファイルにアセット定義を追加します。
C#
asset MapCustomData;
次に、Quantum
プロジェクトにスポーンポイントのデータを保存する新しいクラスを作成します。
C#
namespace Quantum
{
using System;
using Photon.Deterministic;
public unsafe partial class MapCustomData
{
[Serializable]
public struct SpawnPointData
{
public FPVector3 Position;
public FPQuaternion Rotation;
}
public SpawnPointData DefaultSpawnPoint;
public SpawnPointData[] SpawnPoints;
public void SetEntityToSpawnPoint(Frame frame, EntityRef entity, Int32? index)
{
var transform = frame.Unsafe.GetPointer<Transform3D>(entity);
var spawnPoint = index.HasValue && index.Value < SpawnPoints.Length ? SpawnPoints[index.Value] : DefaultSpawnPoint;
transform->Position = spawnPoint.Position;
transform->Rotation = spawnPoint.Rotation;
}
}
}
そして、新しいクラスを作成して、スポーンポイントのベイク処理を記述します。
C#
namespace Quantum
{
using UnityEditor;
using UnityEngine;
public class SpawnPointBaker : MapDataBakerCallback
{
public override void OnBeforeBake(QuantumMapData data)
{
}
public override void OnBake(QuantumMapData data)
{
var customData = QuantumUnityDB.GetGlobalAssetEditorInstance<Map>(data.Asset.UserAsset.Id);
var spawnPoints = GameObject.FindGameObjectsWithTag("SpawnPoint");
if (customData == null || spawnPoints.Length == 0)
{
return;
}
var defaultSpawnPoint = spawnPoints[0];
if (customData.DefaultSpawnPoint.Equals(default(MapCustomData.SpawnPointData)))
{
customData.DefaultSpawnPoint.Position = defaultSpawnPoint.transform.position.ToFPVector3();
customData.DefaultSpawnPoint.Rotation = defaultSpawnPoint.transform.rotation.ToFPQuaternion();
}
customData.SpawnPoints = new MapCustomData.SpawnPointData[spawnPoints.Length];
for (var i = 0; i < spawnPoints.Length; i++)
{
customData.SpawnPoints[i].Position = spawnPoints[i].transform.position.ToFPVector3();
customData.SpawnPoints[i].Rotation = spawnPoints[i].transform.rotation.ToFPQuaternion();
}
#if UNITY_EDITOR
EditorUtility.SetDirty(customData);
#endif
}
}
}
このベイク処理は比較的シンプルです。シーン内のSpawnPoint
タグが付いたゲームオブジェクトをすべて集めて、その位置と回転を抽出し、独自アセットに保存します。最後に、アセットにダーティーマークを付けて、その変更をディスクに保存します。
独自データを使用するために、SpawnPoint
タグが付いたゲームオブジェクトをシーンに追加し、MapCustomDataAsset
を作成してマップのUserAsset
フィールドに割り当てます。シミュレーションでスポーンポイントを使用するには、次のコードを使用します。
C#
var data = frame.FindAsset<MapCustomData>(frame.Map.UserAsset);
data.SetEntityToSpawnPoint(f, entity, spawnPointIndex);
編集時のベイク
編集時のベイクについての情報は、マップベイクページをご覧ください。
実行時のベイク
実行時にマップをベイクすることも可能です。これは、プロシージャル生成されたマップや、編集時に不明なマップで便利です。
Quantum開始前
Quantumセッション開始前に新しいマップをベイクすることが1つの方法です。この方法には、2つのオプションがあります。
- セッション開始前に、すべてのクライアントが決定論的にマップをベイクする
- この方法は通常、マップ生成が決定論的である場合に使用されます。
- すべてのクライアントが同じ方法でマップをベイクすることを保証する必要があるため、より複雑な方法になります。これを実現するためには、マップ生成に使用する乱数のシードを共有し、コードが決定論的であることを確認してください。
- 各クライアントに乱数のシードを送信するだけで、クライアント自身がマップを生成できるため、帯域幅の節約になります。
実行時にQuantumUnityDB
に静的アセットを追加する方法は次の通りです。
C#
var generatedMap = new Map();
// マップを生成する...
// QuantumUnityDBにマップを追加する
QuantumUnityDB.Global.AddAsset(generatedMap);
- 1クライアントがマップをベイクし、他のクライアントに共有する
- エディター上で使用するマップベイクと同じコードを再利用できるため、よりシンプルな方法です。
- ただし、エディターのマップベイクは決定論的ではないため、Quantum開始前に、アセットを動的アセットにして、動的アセットの初期値に追加する必要があります。これによって、すべてのクライアントがマップを利用できるようになります。
Quantumセッション中
この方法はより複雑で、ゲームに大きく依存します。
これにはいくつかのアプローチがあります。
1クライアントがマップをベイクし、コマンドで他のクライアントに共有する
- これによって、エディター上で使用するマップベイクと同じコードを再利用できます。
- マップサイズがコマンドサイズに制限されます(または、手動で分割して結合する必要があります)。
セッション中に、すべてのクライアントが決定論的にマップをベイクする
- マップ生成が決定論的であれば、セッション中にすべてのクライアントがマップをベイクすることができます。
- これはセッション開始前にマップをベイクすることと実質的に同じですが、途中参加者がベイクイベントを見逃す可能性があるため、アセットは動的である必要があります。
注意点
実行時にマップをベイクすることには、いくつかの注意点があります。
- Unityエディターで使用されるマップベイクプロセスは、デフォルトでは決定論的ではありません(
float
からFP
への変換があるため)。 - アセットが
AssetDB
に追加された後に、Map Entities
要素の追加/削除はできません。