This document is about: QUANTUM 3
SWITCH TO

マップベイク

概要

Quantumでは、マップはUnityシーンとQuantumMapDataの2つで構成されます。

Unityシーンはマップとあわせてロードされ、EntityViewのようなマップの視覚要素を含みます。Unityのゲームオブジェクトは、ゲームのエンティティのレンダリングを担当します。

一方QuantumMapDataは、決定論的シミュレーションでゲームプレイを進行するために使用されるマップの情報を含みます。

マップベイク

QuantumSampleGameシーンには、SampleMapアセットを参照するQuantumMapDataコンポーネントが付いています。SampleMapはシーンのResourcesフォルダーにあります。

新しいゲームシーンを作成する際に、新しいQuantumMapDataコンポーネントを作成/設定する方法はいくつかあります。

  1. 上部ツールバー(Quantum/Setup/Create New Quantum Scene)から、新しいQuantumシーンを作成する。
  2. または、既に作成されたシーンをQuantumシーンに変換する(Quantum/Setup/Add Quantum To Current Scene)。
  3. または、マップを手動で設定する。QuantumMapDataコンポーネントのゲームオブジェクトを作成し、コンテキストメニュー(Create/Quantum/Assets/Map)から新しいMapアセットを作成します。

プロジェクトのQuantumEditorSettingsからは、シーン保存時・再生時・アプリビルド時などでの自動マップベイクの有効/無効を切り替えることができます。

自動マップベイクを無効にすることが便利な場面もありますが、手動マップベイクは時間がかかりパイプラインでヒューマンエラーが起こる可能性に注意することが重要です。そのため一般的なほとんどのプロジェクトでは、自動マップベイクを有効にしておくことを推奨します。

QuantumEditorSettings MapBaking
QuantumEditorSettingsのマップベイク

Quantumでシーンをベイクするには、シーン内のゲームオブジェクトにQuantumMapDataコンポーネントが存在する必要があります。さらに、ナビメッシュが存在する場合は、QuantumMapNavMeshUnityコンポーネントも必要です。Quantum SDKで提供されているQuantumSAmpleGameシーンには、必要なセットアップが行われたQuantumMapDataが用意されています。

QuantumMapDataコンポーネントには、必要に応じて手動でベイクプロセスをトリガーできるボタンが含まれています。Bake All Modeは、ベイクプロセスの特定のステップをスキップするように調整できます。

MapData Component
QuantumMapDataコンポーネント

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にベイクされたすべてのデータにアクセスして調整できます。

必要に応じて、仮想コールバックをオーバーライドできます。OnBeforeBakeNavmeshOnCollectNavMeshBakeDataOnCollectNavMeshesOnBakeNavMesh、そしてベイクフラグとベイクトリガーを提供するOnBeforeBakeのオーバーロードがあります。

独自データをマップに追加

ゲームマップには、コライダーやナビメッシュ以外にもゲームプレイに重要な要素が含まれることがあります。含める必要があるデータが不変である場合は、マップ上のエンティティに追加することは必須ではなく、UnityコンポーネントMapDataUser 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つのオプションがあります。

  1. セッション開始前に、すべてのクライアントが決定論的にマップをベイクする
    • この方法は通常、マップ生成が決定論的である場合に使用されます。
    • すべてのクライアントが同じ方法でマップをベイクすることを保証する必要があるため、より複雑な方法になります。これを実現するためには、マップ生成に使用する乱数のシードを共有し、コードが決定論的であることを確認してください。
    • 各クライアントに乱数のシードを送信するだけで、クライアント自身がマップを生成できるため、帯域幅の節約になります。

実行時にQuantumUnityDBに静的アセットを追加する方法は次の通りです。

C#

var generatedMap = new Map();

// マップを生成する...

// QuantumUnityDBにマップを追加する

QuantumUnityDB.Global.AddAsset(generatedMap); 
  1. 1クライアントがマップをベイクし、他のクライアントに共有する
    • エディター上で使用するマップベイクと同じコードを再利用できるため、よりシンプルな方法です。
    • ただし、エディターのマップベイクは決定論的ではないため、Quantum開始前に、アセットを動的アセットにして、動的アセットの初期値に追加する必要があります。これによって、すべてのクライアントがマップを利用できるようになります。

Quantumセッション中

この方法はより複雑で、ゲームに大きく依存します。

これにはいくつかのアプローチがあります。

  1. 1クライアントがマップをベイクし、コマンドで他のクライアントに共有する

    • これによって、エディター上で使用するマップベイクと同じコードを再利用できます。
    • マップサイズがコマンドサイズに制限されます(または、手動で分割して結合する必要があります)。
  2. セッション中に、すべてのクライアントが決定論的にマップをベイクする

    • マップ生成が決定論的であれば、セッション中にすべてのクライアントがマップをベイクすることができます。
    • これはセッション開始前にマップをベイクすることと実質的に同じですが、途中参加者がベイクイベントを見逃す可能性があるため、アセットは動的である必要があります。

注意点

実行時にマップをベイクすることには、いくつかの注意点があります。

  • Unityエディターで使用されるマップベイクプロセスは、デフォルトでは決定論的ではありません(floatからFPへの変換があるため)。
  • アセットがAssetDBに追加された後に、Map Entities要素の追加/削除はできません。
Back to top