This document is about: QUANTUM 3
SWITCH TO

マテリアライゼーション

はじめに

Component PrototypeEntity Prototypeからエンティティやコンポーネントのインスタンスを作成するプロセスは、__マテリアライズ__と呼ばれます。

マップアセットにベイクされたシーンプロトタイプのマテリアライズは、Frame.Create APIを使用してコードによって作成されたインスタンスのマテリアライズと同じルールと実行フローに従います。

プロトタイプとインスタンス

コンポーネントインスタンスとエンティティインスタンスはゲームの状態の一部であり、言い換えれば、実行時に操作可能です。DSLで宣言されたコンポーネントは、それに対応するComponent Prototypesを生成するために使用されます。コード生成されたプロトタイプは、命名規則MyComponentPrototypeに従います。

Component PrototypesEntity Prototypesはどちらも__アセット__です。これは、ゲームステートの一部ではなく、実行時に不変であり、すべてのクライアントで常に同一である必要があることを意味します。各Component Prototypeには、対応するアセットを見つけるために使用できるComponentPrototypeRefがあります。これを使用して、Frame.FindPrototype<MyComponentNamePrototype>(MyComponentPrototypeRef)を呼び出すことで、対応するアセットを検索できます。

コンポーネントプロトタイプ

Component Prototypeを拡張して、マテリアライズに直接使用されないデータを含めることが可能です。これにより、特定のコンポーネントのインスタンス間で共有データを持ったり、ゲームステートをスリムに保つためにフレームから読み取り専用データを除外したりすることができます。

コード生成されたComponent Prototypesは、簡単に拡張できる部分クラスです:

  1. MyComponentNamePrototype.Partial.csというC#ファイルを作成します;
  2. スクリプトの本体をQuantum.Prototypes名前空間に配置します;

これにより、Component Prototypeアセットに追加データを加え、カスタムマテリアライズロジックを追加するために部分的なMaterializeUser()メソッドを実装できます。

コンポーネントプロトタイプに追加のUnityプロトタイプアダプタを生成する必要がある場合、デフォルトでは部分として出力されません。回避策については、Unityプロトタイプアダプタセクションを参照してください。

以下の例は、アーケードレーシングテンプレートに見られるVehicleコンポーネントのマテリアライズを示しています。

Vehicleコンポーネントは、主に実行時に計算される動的値を保持します。設計選択により、これらの変数はUnityエディターで初期化されるべきではないため、DSLでのコンポーネント定義ではこれらのパラメータにExcludeFromPrototype属性を使用して、Unityエディターでデザイナーが操作できるVehiclePrototypeアセットから除外しています。Nitroパラメータのみが編集可能で、デザイナーが特定のVehicleをどの程度のニトロで初期化するかを決定できるようにしています。

C#

component Vehicle
{
    [ExcludeFromPrototype]
    ComponentPrototypeRef Prototype;

    [ExcludeFromPrototype]
    Byte Flags;
    [ExcludeFromPrototype]
    FP Speed;
    [ExcludeFromPrototype]
    FP ForwardSpeed;
    [ExcludeFromPrototype]
    FPVector3 EngineForce;
    [ExcludeFromPrototype]
    FP WheelTraction;

    [ExcludeFromPrototype]
    FPVector3 AvgNormal;

    [ExcludeFromPrototype]
    array<Wheel>[4] Wheels;

    FP Nitro;
}

VehiclePrototypeアセットは、デザイナーがカスタマイズ可能な読み取り専用パラメータを提供するために拡張されます。したがって、VehiclePrototypeアセットは特定の車両エンティティプロトタイプ「タイプ」のすべてのインスタンスで共有される値を保持できます。Vehicleコンポーネント内のPrototypeパラメータは、AssetRefに対するコンポーネント特定の同等物であるComponentPrototypeRefタイプです。それを populated するために、部分的なMaterializeUser()メソッドを使用してVehiclePrototypeの参照を割り当てます。

C#

using Photon.Deterministic;
using Quantum.Inspector;
using System;

namespace Quantum.Prototypes
{
public unsafe partial class VehiclePrototype
{
    // PUBLIC METHODS

    [Header("Engine")]
    public FP EngineForwardForce = 130;
    public FP EngineBackwardForce = 120;
    public FPVector3 EngineForcePosition;
    public FP ApproximateMaxSpeed = 20;

    [Header("Hand Brake")]
    public FP HandBrakeStrength = 10;
    public FP HandBrakeTractionMultiplier = 1;

    [Header("Resistances")]
    public FP AirResistance = FP._0_02;
    public FP RollingResistance = FP._0_10 * 6;
    public FP DownForceFactor = 0;
    public FP TractionGripMultiplier = 10;
    public FP AirTractionDecreaseSpeed = FP._0_50;

    [Header("Axles")]
    public AxleSetup FrontAxle = new AxleSetup();
    public AxleSetup RearAxle = new AxleSetup();

    [Header("Nitro")]
    public FP MaxNitro = 100;
    public FP NitroForceMultiplier = 2;

    // PARTIAL METHODS
    partial void MaterializeUser(Frame frame, ref Vehicle result, in PrototypeMaterializationContext context)
    {
        result.Prototype = context.ComponentPrototypeRef;
    }

    [Serializable]
    public class AxleSetup
    {
        public FPVector3 PositionOffset;
        public FP Width = 1;
        public FP SpringForce = 120;
        public FP DampingForce = 175;
        public FP SuspensionLength = FP._0_10 * 6;
        public FP SuspensionOffset = -FP._0_25;
    }
}
}

VehiclePrototype内のパラメータは、コンポーネントインスタンスに見られる動的値を計算するために必要な値を保持しており、Vehicleコンポーネントが取り付けられているエンティティの挙動に影響を与えます。例えば、プレイヤーが追加のNitroを取得した場合、Vehicleコンポーネントに保持されている値は、VehiclePrototypeに見られるMaxNitro値に制限されます。これにより、非同期化のペナルティの下で制限が課せられ、ゲームステートをスリムに保つことができます。

C#

namespace Quantum
{
    public unsafe partial struct Vehicle
    {
        public void AddNitro(Frame frame, EntityRef entity, FP amount)
        {
            var prototype = frame.FindPrototype<Vehicle_Prototype>(Prototype);
            Nitro = FPMath.Clamp(Nitro + amount, 0, prototype.MaxNitro);
        }
    }
}

マテリアライズ順序

すべてのEntity Prototypeのマテリアライズ、シーンプロトタイプを含む、は次の手順を順番に実行します:

  1. 空のエンティティが作成されます。
  2. Entity Prototypeに含まれる各Component Prototypeについて:
    1. コンポーネントインスタンスがスタック上に作成されます;
    2. Component Prototypeがコンポーネントインスタンスにマテリアライズされます;
    3. MaterializeUser()が呼び出されます(ただし、これを実装するのは_オプション_です);
    4. コンポーネントがエンティティに追加され、ISignalOnComponentAdded<MyComponent>シグナルがトリガーされます。
  3. マテリアライズされた各エンティティに対してISignalOnEntityPrototypeMaterializedが呼び出されます。
    • マップ/シーンのロード:このシグナルは、すべてのシーンプロトタイプがマテリアライズされた後、すべてのエンティティおよびEntity Prototypeペアに対して呼び出されます。
    • Frame.Create()で作成された:このシグナルは、プロトタイプがマテリアライズされた直後に呼び出されます。

Component Prototypeのマテリアライズステップは、あらかじめ決められた順序でデフォルトコンポーネントをマテリアライズします。

C#

Transform2D
Transform3D
Transform2DVertical
PhysicsCollider2D
PhysicsBody2D
PhysicsCollider3D
PhysicsBody3D
PhysicsJoints2D
PhysicsJoints3D
PhysicsCallbacks2D
PhysicsCallbacks3D
CharacterController2D
CharacterController3D
NavMeshPathfinder
NavMeshSteeringAgent
NavMeshAvoidanceAgent
NavMeshAvoidanceObstacle
View
MapEntityLink

すべてのデフォルトコンポーネントがマテリアライズされた後、ユーザー定義のコンポーネントがアルファベット順にマテリアライズされます。

C#

MyComponentAA
MyComponentBB
MyComponentCC
...

Unity Prototype Adapters

コンポーネントの任意のフィールドがReplaceTypeHintAttributeを使用している場合、または以下のいずれかのタイプである場合:

  • EntityRef
  • EntityPrototypeRef
  • ComponentPrototypeRef
  • ComponentPrototypeRef<T>

その場合、Quantumは追加のコンポーネントプロトタイプアダプタタイプを生成します。アダプタタイプはQuantum.Prototypes.Unity名前空間に配置され、元のプロトタイプと同じフィールドを持っていますが、次のタイプ置換が行われることを除きます:

  • EntityRef -> QuantumEntityPrototype
  • EntityPrototypeRef -> QUnityEntityPrototypeRef
  • ComponentPrototypeRef -> QUnityComponentPrototypeRef
  • ComponentPrototypeRef<T> -> QUnityComponentPrototypeRef<T>
  • [ReplaceTypeHintAttribute]引数がフィールドタイプの代わりに使用される

アダプタが生成されると、UnityのMonoBehaviourベースのプロトタイプラッパーは、それをプロトタイプの代わりに使用します。これにより、プロトタイプはUnityオブジェクトの参照と連携して動作するように見えますが、シミュレーションとの互換性は保たれます。

ほとんどの場合、このプロセスはユーザーにとって完全に透明です。ただし、このプロセスには副作用があり、コンポーネントプロトタイプが部分として出力されるのを防ぎます。その理由は、アダプタのフィールドと変換が元のプロトタイプと同期している必要がありますが、一度部分クラスが関与すると、コード生成は何が追加されたかを知る方法がなくなるからです。[CodeGen(ForcePartialPrototype)]属性を使用して部分コンポーネントプロトタイプを強制することはできますが、その場合はアダプタのConvertUserメソッドを実装することを確認してください。

Back to top