This document is about: QUANTUM 3
SWITCH TO

Shape Config

はじめに

Shape Configは、シェイプの情報を保持します。これによって、エディター上で設定オプションを簡単に表示したり、コードでのシェイプの初期化を効率化できます。
ShapeConfigは2D/3Dの両方が存在し、それぞれShape2DConfig/Shape3DConfigになります。

現在、ShapeConfigは動的エンティティでの使用を想定しています。対応するシェイプの種類は次の通りです。

// 2Dの場合

  • Circle
  • Box
  • Polygon
  • Edge
  • Compound (複数のシェイプの組み合わせ)

// 3Dの場合

  • Sphere
  • Box
  • Capsule
  • Compound (複数のシェイプの組み合わせ)

上記すべてのシェイプは、ShapeOverlapPhysicsColliderと互換性があります。

ShapeConfigをエディターに表示する

独自アセットにShape2DConfig/Shape3DConfigを含めると、自動的にエディター上に表示されいます。

C#

namespace Quantum
{
    public unsafe partial class WeaponSpec
    {
        public Shape3DConfig AttackShape;
        public LayerMask AttackLayers;
        public FP Damage;
        public FP KnockbackForce;
    }
}

上記スニペットから生成されたアセットは、インスペクター上ですべてのShapeConfigオプションが表示されます。

ShapeConfig Setting of the example asset as shown in the Unity Editor
例:WeaponSpecアセットのShapeConfig設定がUnityエディター上で表示される

ShapeConfigからシェイプを作成/使用する

ShapeConfig使用する際は、CreateShapeメソッドを呼び出します。これによって、ShapeConfigアセットに保持されている情報が自動的に処理され、設定内のデータから適切なシェイプとパラメーターが作成されます。

C#

private static void Attack(in Frame frame, in EntityRef entity)
{
    // 攻撃範囲にOverlapShapeを使用して、近接攻撃を実行する

    var transform = frame.Unsafe.GetPointer<Transform3D>(entity);
    var weapon = frame.Unsafe.GetPointer<Weapon>(entity);
    var weaponSpec = frame.FindAsset<WeaponSpec>(weapon->WeaponSpec.Id);

    var hits = frame.Physics3D.OverlapShape(
        transform->Position,
        transform->Rotation,
        weaponSpec.AttackShape.CreateShape(frame),
        weaponSpec.AttackLayers);
        
    // ヒット対象を反復するゲームロジック
}

PhysicsColliderのシェイプの初期化時にも、同様の処理を実行できます。

Compoundシェイプ

Compoundシェイプは、複数のシェイプで構成されるシェイプです。Compoundシェイプ作成に使用できるシェイプは、最初に紹介したリストに含まれるものになります。

PhysicsColliderShapeOverlapは、Compoundシェイプと完全に互換性があります。

現在、Quantumは永続的Compoundシェイプを提供しています。つまり、ヒープ内の他のシェイプのバッファを指すシェイプです。このバッファは、手動で解放するまでフレーム間で永続します。

新しいCompoundシェイプの作成

Compoundシェイプは、CreateShapeメソッドを呼び出すだけでShapeConfigから作成できます。または、Shape.CreatePersistentCompoundから手動で作成し、後でFreePersistentを呼び出して解放します。以下はライフサイクルを管理する方法の例で、3Dシェイプにも同様に適用できます。

C#

    // 永続的Compoundシェイプを作成する。実際にシェイプを追加するまでは、メモリ割り当ては発生しない。
    var compoundShape = Shape2D.CreatePersistentCompound();

    // Compoundシェイプにシェイプを追加する(shape1とshape2は任意のシェイプ)
    compoundShape.Compound.AddShape(f, shape1);
    compoundShape.Compound.AddShape(f, shape2);

    (...) // ゲームロジック

    // 手動で解放するまでこのCompoundシェイプは永続する
    compoundShape.Compound.FreePersistent(f);

APIには、RemoveShapesGetShapesFreePresistentなどのメソッドも提供されています。これらメソッドの詳細情報は、SDKのdocsフォルダー内のAPIドキュメントをご覧ください。

既存Compoundシェイプからのコピー

既存のCompoundシェイプをコピーして、新しいCompoundシェイプを作成することもできます。

C#

// 上記の例の既存Coumpoundシェイプを使用する

var newCompoundShape = Shape2D.CreatePersistentCompound();
newCompoundShape.Compound.CopyFrom(f, ref oldCompoundShape);

新しいCompoundシェイプを作成すると、新しいバッファも作成されます。このバッファは、他の永続的Compoundシェイプのように、開発者が手動で解放する必要があります。

個別のシェイプへのアクセス

シェイプを反復処理する例として、GetShapesメソッドを使用してバッファのポインタを取得し、単純なforループからCompoundシェイプに含まれるすべてのShape*にアクセスする方法があります。
メソッドが返すcoundはメモリ内にシェイプのポインタが含まれる境界を表すため、それを超えないようにしてください。

C#

if (shape->Compound.GetShapes(frame, out Shape3D* shapesBuffer, out int count))
{
    for (var i = 0; i < count; i++)
    {
        Shape3D* currentShape = shapesBuffer + i;
        // シェイプで何かする
    }
}

Compoundコライダー

Compoundコライダーは、Compoundシェイプを持つ通常のコライダーです。

エディター上で作成

エディター上では、Entity Component Physics Collider 2D/3DEntity Prototypeスクリプトから、Compoundコライダーを作成するオプションが表示されています。

ShapeConfig for a Compound Collider in the Entity Prototype script as shown in the Unity Editor
Unityエディター上のEntity Prototypeスクリプトから、Compoundコライダー(Sphere + Box)を作成する

Compoundシェイプを持つコライダーのプロトタイプを作成する場合は、自動的にメモリ管理が行われます。つまり、コライダーは自身が持つCompoundシェイプを管理するため、手動でメモリを解放する必要はありません。

コードで作成

コードからコライダーを作成する場合は、単純にCompoundシェイプをCreateファクトリーメソッドに渡します。前セクションのコードスニペットのようにCompoundシェイプを作成した後は、(...)を次のように置き換えてコライダーを作成できます。

C#

    var collider = PhysicsCollider2D.Create(f, compoundShape);
    f.Set(entity, collider);

上記のコードスニペットでは、collider.ShapecompoundShapeはヒープ内の異なるバッファを指しています。コライダーを作成するためだけにCompoundシェイプを使用していた場合は、その直後に解放することが可能です。コライダーが破棄/削除されると、メモリ内のコピーは解放されます。

メモリに関する重要な備考

ファクトリーメソッドCreate()の一部として使用されたコライダーは、Compoundシェイプのバッファのコピーのみを作成します。

C#

    var compoundShape = Shape2D.CreatePersistentCompound();
    compoundShape.Compound.AddShape(f, shape1);
    compoundShape.Compound.AddShape(f, shape2);

    // collider1とcollider2は、それぞれcoumpoundShepeバッファのコピーを作成する
    // collider1とcollider2は、それぞれ破棄/削除時に自身のコピーを解放する
    var collider1 = PhysicsCollider2D.Create(f, compoundShape);
    f.Set(entity1, collider1);
    
    var collider2 = PhysicsCollider2D.Create(f, compoundShape);
    f.Set(entity2, collider2);
    
    // 不要になったcompoundShapeのバッファは、ここで解放する
    compoundShape.Compound.FreePersistent(f);

対照的に、通常のシェイプのコライダーを作成した後にcollider.Shape = someCompoundを設定する場合は、バッファのコピーは作成されません。つまり、collider.ShapesomeCompoundは同じバッファを指すことになります。複数のCompoundコライダーやCompoundシェイプが同じバッファを指している場合、一方が解放されると、他方のバッファの参照が事実上壊れてしまうため、非常に危険です。

C#

    var compoundShape = Shape2D.CreatePersistentCompound();
    compoundShape.Compound.AddShape(f, shape1);
    compoundShape.Compound.AddShape(f, shape2);

    var collider1 = PhysicsCollider2D.Create(f, default(Shape2D));
    collider1.Shape = compoundShape;
    f.Set(entity1, collider1);

    var collider2 = PhysicsCollider2D.Create(f,  Shape2D.CreateCircle(1));
    collider2.Shape = compoundShape;
    f.Set(entity2, collider2);

    // collider1.Shapeとcollider2.ShapeとcompoundShapeは、すべて同じバッファを指す
    // ここでcompoundShapeを解放すると、collider1とcollider2が壊れてしまう
    compoundShape.Compound.FreePersistent(f);

ただし、これを適切に実行できれば、シェイプのメモリ管理を簡素化することができます。シェイプをコライダーに割り当て、メモリ管理の責任をコライダーに移譲することで、コライダーが破棄/削除された際に自動的にメモリを解放することができます。

C#

    var compoundShape = Shape2D.CreatePersistentCompound();
    compoundShape.Compound.AddShape(f, shape1);
    compoundShape.Compound.AddShape(f, shape2);

    var collider1 = PhysicsCollider2D.Create(f, Shape2D.CreateCircle(1));
    collider1.Shape = compoundShape;
    f.Set(entity1, collider1);

    // compoundShepeのバッファはcollider1によって参照されていて、
    // 自動的に破棄/削除されるため、手動で解放する必要はない

Compoundシェイプクエリ

Compoundシェイプクエリは、ブロードフェーズクエリと通常のクエリの両方を完全にサポートしています。動作やパフォーマンスは、複数のクエリを実行する場合と同様ですが、結果は同じHitCollectionで返されます。

Compoundシェイプの入れ子

Compoundシェイプの入れ子は、物理エンジンでサポートされていますが、2つの制限があります。

  • シェイプは階層内において、ヒープ内の同じバッファの参照を1つだけ保持できます。既に参照されているバッファを追加すると、デバッグモードではエラーが発生します。これは解放時に、循環参照や無効なポインタが発生する問題を避けるためです。
  • Compoundシェイプの入れ子は、エディター上ではサポートされていません(警告メッセージが表示されます)。これはUnityのシリアライザーの制限によるもので、ShapeConfigでより複雑な構造とドロワーが必要になるためです。
Back to top