This document is about: QUANTUM 2
SWITCH TO

クエリ

はじめに

クエリでは、動的エンティティと静的コライダーが考慮される場合があります。光線、線、およびShapeのオーバーラップのAPIはどれもヒットであり非常に似ています(同じ種類のデータ)。

クエリ

ラインキャストとレイキャスト

C#

// For 2D
var hits = f.Physics2D.LinecastAll(FPVector2.Zero, FPVector2.One);
for (int i = 0; i < hits.Count; i++) {
    var hit = hits[i];
}

// For 3D
var hits = f.Physics3D.LinecastAll(FPVector3.Zero, FPVector3.One);
for (int i = 0; i < hits.Count; i++){
    var hit = hits[i];
}

結果の HitCollection オブジェクトには、次のプロパティが含まれます。

  • HitCollection の各アイテムには、EntityRefまたはStatic collider情報があります。これらは相互に排他的です。1つは有効で、もう1つは null です。
  • Count は常に HitCollection を反復するために使用します。
  • ヒットはソートされません。Sort()を呼び出してFPVector2にパスするとソートできます。こうすることでヒットが関数から提供される参照ポイントへの距離に応じてソートされます。

レイキャストは、ラインキャストのシンタックスシュガーです。同じように機能し、startend の代わりに startdirectionおよび max-distance を必要とします。 また、これらのオプションパラメータをラインキャストとレイキャストに渡すことができます。

  • キャストを実行する物理レイヤーを指定するLayerMask
  • キャストで検討するコライダーのタイプを指定するQueryOptions。

Shape Queries

Quantumは以下の通り2種類のShapeクエリに対応しています。

  • ShapeOverlap;
  • ShapeCasts (v2.1以降)。

これらは、Quantumで対応しているすべての動的なShapeと使用できます。

Note: CompoundShapesはShapeクエリの実行に使用できます。詳細は、Shape Config ページを参照してください。

ShapeOverlaps

OverlapShape()HitCollection を返します。必要なパラメーターは次のとおりです。

  • センター位置 (FPVector2 または FPVector3);
  • 回転 (FP または FPQuaternion (3D相当));
  • 形状(Shape2D または Shape3DPhysicsCollider から、または呼び出し時に作成)。

C#

// For 2D
var hits = f.Physics2D.OverlapShape(FPVector2.Zero, FP._0, Shape2D.CreateCircle(FP._1))
for (int i = 0; i < hits.Count; i++){
    var hit = hits[i];
}

// For 3D
var hits = f.Physics3D.OverlapShape(FPVector3.Zero, FPQuaternion.Identity, Shape3D.CreateSphere(1));
for (int i = 0; i < hits.Count; i++){
    var hit = hits[i];
}

注意: CompoundShapesはShapeOverlapsの実行のために使用できます。詳細は、Shape Configのページを参照してください。

ShapeCasts

Quantum 2.1以降ではShapeCasts (2D & 3D)が使用可能です。

ShapeCastAll()HitCollectionを返します。必要なパラメータは以下の通りです。

  • センター位置( FPVector2 または FPVector3);
  • 形状の回転(FP または FPQuaternion (3D相当));
  • 形状ポインタ(_Shape2D* _ または Shape3D*PhysicsCollider から、または呼び出し時に作成)。
  • ベクトルで表現された距離と方向 (FPVector2 または FPVector3 )。

C#

// For 2D
var shape = Shape2D.CreateCircle(FP._1);
var hits = f.Physics2D.ShapeCastAll(FPVector2.Zero, FP._0, &shape, FPVector2.One);
for (int i = 0; i < hits.Count; i++){
    var hit = hits[i];
}

// For 3D
var shape = Shape3D.CreateSphere(1);
var hits = f.Physics3D.ShapeCastAll(FPVector3.Zero, FPQuaternion.Identity, &shape, FPVector3.One);
for (int i = 0; i < hits.Count; i++){
    var hit = hits[i];
}

カスタムGJKベースアルゴリズムを使用しています。GJKConfig設定はSimulationConfigアセットのPhysics > GJKConfigセクションにあります。この設定では、トレードオフである精度と性能のバランスを調整できます。デフォルトでは通常サイズの形状にするためのバランスをとった値になっています。

  • Simplex Min/Max Bit Shift: 大胆に生の値に近づけることで、Physicsスペースの位置の有効範囲を妥協することなくケースの悪化を回避しながらVoronoy Simplexの点の精度をあげることができます。形状のスケールが巻き込まれたり、形状間の距離が狭すぎる場合は値を大きくすることを考えてみてください。
  • Shape Cast Max Iterations: ハードトレランス下でのソリューションの検索中にアルゴリズムによって行われるイテレーションの最大数。これを大きくすると、最悪ケースではパフォーマンスを犠牲にして正確な結果に近づく可能性がありますが、その逆もあります。
  • Shape Cast Hard Tolerance: このしきい値以下のイテレーション結果(形状間の最も近い距離)は終了条件として受け入れることができます。これを少なくすると、イテレーション回数を増やす代わりに正確な結果に近づく可能性がありますが、その逆もあります。
  • Shape Cast Soft Tolerance: Max Iterations内の定義されたハードトレランス下で受け入れ可能な結果が見つからなかった形状キャスト解像度は、それまでに見つかっている最もいい結果がこのソフトしきい値よりも下であればポジティブになります。その様なケースでは、このしきい値を上げるとフォールスポジティブの可能性が高くなり、下げるとフォールスネガティブの可能性があがります。

Sorting Hits

HitCollectionを返す全てのクエリはソート可能です。

  • Sort(): 2DではFPVector2、3ではFPVector3をとり、ヒットの提供された点までの各距離に従ってコレクションをソートします。
  • SortCastDistance(): ShapeCastクエリの結果をソートするのに使用します。引数を取らず、キャスト距離に応じてヒットの順番を決めます。

オプション

ブロードフェーズバージョンを含むすべてのクエリは QueryOptionsを使用して操作とその結果をカスタマイズすることができます。
QueryOptionsは、どのタイプのオブジェクトが考慮され、どのような情報が計算されるかをフィルタリングするマスクを作成します。バイナリ | 演算子を使用して、これらを組み合わせることができます。

ヒットの法線

最もパフォーマンスの高いクエリを提供するために、すべてのデフォルトクエリは2つの形状が重なっているかどうかのみを確認します。

追加の情報を受け取るためには、より多くの計算が必要となりますが、それによってさらにオーバーヘッドが発生します。そのため、QueryOptionsパラメータとして ComputeDetailedInfo を渡して明示的に指定する必要があります。これにより、ヒット数の計算が可能になります。

-ポイント

  • 正常
    -浸透

ray-triangle チェックでは、法線は常に三角形の法線となります。これは三角形のデータにキャッシュされているので、追加の計算はありません。

ヒットのフィルタリング

以下のQueryOptionsでは、クエリで使用するマスクを定義することができます。オブジェクトがパラメータとして指定された QueryOptionsと一致しない場合、スキップされます。QueryOptionsに一致するオブジェクトのみが評価され、結果として返されます。

  • HitStatics : 静的なコリダにのみヒットします。

  • HitKinematics : 以下の条件のいずれかを満たすエンティティにヒットします。

    • PhysicsColliderを持ち、PhysicsBodyを持たないエンティティ
    • PhysicsCollider と disabled PhysicsBody を持つエンティティ
    • PhysicsColliderと__kinematic__ PhysicsBodyを持つエンティティ
  • HitDynamics : _enabled_かつ_non-kinematic_のPhysicsBodyを持つエンティティにのみヒットします。

  • HitTriggers : コライダーをトリガーするためには、他のフラグと組み合わせて使用する必要があります。

  • HitAll : PhysicsColliderを持つ全てのエンティティにヒットします。

デフォルトでは、クエリは HitAllオプションを使用します。 他のオプションを選択すると、計算が節約されます。

ブロードフェイズクエリ

Quantum には、物理システムの間に解決される物理クエリ(レイキャストとオーバーラップ)を注入するためのオプションがあります。そのためには、次のことを行う必要があります。

  1. システムを作成する。
  2. システムを作成し、SystemSetup.cs に追加する際に Core.PhysicsSystem の前に挿入する。
  3. Core.PhysicsSystemの後に実行されているシステムの情報を取得する。

この設定は、物理ステップの並列処理の恩恵を受け、物理ステップ後の通常のクエリよりもはるかに高速になります。

C#

  public static class SystemSetup {
    public static SystemBase[] CreateSystems(RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
      return new SystemBase[] {

        // pre-defined core systems
        new Core.CullingSystem2D(),
        new Core.CullingSystem3D(),

        // Placing systems scheduling Broadphase queries here
        // allows them to benefit from the CullingSystems on predicted frames.
        new ProjectileHitQueryInjectionSystem(),

        new Core.PhysicsSystem2D(),
        new Core.PhysicsSystem3D(),

        new Core.NavigationSystem(),
        new Core.EntityPrototypeSystem(),

        // user systems go here
        // This is also where systems retrieving the results of broadphase queries go
        new ProjectileHitRetrievalSystem(),
      };
    }
  }

_ 注:_ ブロードフェーズクエリは、ソルバーが実行される前に物理エンジンにスケジュールされている/注入されているため、注入済みクエリやスケジュール済みクエリと呼ばれることもあります。

クエリの注入

物理処理の前に実行されていたメインスレッドシステムからクエリを注入することができます。注入されたクエリは 0 ベースのインデックスを返し、このインデックスを使って物理システムが実行された後に結果を取得することができます。クエリのインデックスは、フレーム内で_生成され__、__消費されるべき_インデックスなので、ロールバック可能なフレームデータの外側も含めて、どこにでも格納することができます。

C#

namespace Quantum
{
    public unsafe struct ProjectileFilter
    {
        public EntityRef EntityRef;
        public Transform3D* Transform;
        public Projectile* Component;
    }

    public unsafe class ProjectileHitQueryInjectionSystem : SystemMainThread
    {
        public override void Update(Frame f)
        {
            var projectileFilter = f.Unsafe.FilterStruct<ProjectileFilter>();
            var projectile = default(ProjectileFilter);

            while (projectileFilter.Next(&projectile))
            {
                projectile.Component->PathQueryIndex = f.Physics3D.AddRaycastQuery(
                    projectile.Transform->Position,
                    projectile.Transform->Forward,
                    projectile.Component->Speed * f.DeltaTime);

                var spec = f.FindAsset<WeaponSpec>(projectile.Component->WeaponSpec.Id);

                projectile.Component->DamageZoneQueryIndex = f.Physics3D.AddOverlapShapeQuery(
                    projectile.Transform->Position,
                    projectile.Transform->Rotation,
                    spec.AttackShape.CreateShape(f),
                    spec.AttackLayers);
            }
        }
    }
}

重要: AddRaycastQueryAddOverlapShapeQuery が返すクエリインデックスは、後でクエリの結果を取得するために絶対に必要です。したがって、ヒットした結果を処理する必要のあるエンティティに添付されたコンポーネントに保存しておくことをお勧めします。

クエリ結果の取得

クエリの結果は、物理の後に実行されるいかなるシステムからでも取得することができます。結果(HitCollection*)を取得するには、Frame.Physics.GetQueryHits()に保存されている前のインデックスを渡す必要があります。

C#

using Photon.Deterministic;

namespace Quantum
{
    public unsafe class ProjectileHitRetrievalSystem : SystemMainThread
    {
        public override void Update(Frame f)
        {
            var projectileFilter = f.Unsafe.FilterStruct<ProjectileFilter>();
            var projectile = default(ProjectileFilter);

            while (projectileFilter.Next(&projectile))
            {
                var hitsOnTrajectory = f.Physics3D.GetQueryHits(projectile.Component->PathQueryIndex);
                if (hitsOnTrajectory.Count <= FP._0)
                {
                    projectile.Transform->Position =
                        projectile.Transform->Rotation *
                        projectile.Transform->Forward *
                        projectile.Component->Speed * f.DeltaTime;
                    continue;
                }

                var damageZoneHits = f.Physics3D.GetQueryHits(projectile.Component->DamageZoneQueryIndex);

                for (int i = 0; i < damageZoneHits.Count; i++)
                {
                    // Apply damage logic
                }
            }
        }
    }
}

それに加えて、 Frame.Physicsからも利用できるpublic bool GetAllQueriesHits(out HitCollection * queryHits、out int querysCount)呼び出しを介してすべてのBroadphase結果を取得できます。

注意事項

ブロードフェーズクエリを使用する際の注意点:

  • 大規模な数値(投射物など)の場合、パフォーマンスは約20倍向上します。
    -物理システムが起動する前のフレームの状態に基づいています。
  • ブロードフェーズクエリは、フレーム間で引き継がれません。つまり、物理の前のフレームの開始時に注入する必要があります。物理が実行された後に注入されたブロードフェーズクエリが結果を返すことはありません。これは、Quantumの物理がステートレスだからです。

CCDのエミュレート

Quantumの物理エンジンはステートレスです。このようなシステムでは、連続的なヒット検出は過度な負荷がかかります。ステートレス物理エンジンでCCDの動作をエミュレートするためのソリューションには、通常、レイキャストまたは1フレームの予想される動きに及ぶ形状の重なりが含まれます。

これは通常、発射体などの高速で移動するエンティティを使用する際にあがるトピックです。高速移動するオブジェクトのサイズに応じて、以下のアプローチのいずれかを使用することをお勧めします。

  • 長さを velocity * deltaTime とした移動方向の短いレイ。
  • 単一のオーバーラップ。形状のオーバーラップは複合形状でも可能であることに注意してください。

これらのソリューションはいずれも、100%の精度でCCDを再現し、全体的なパフォーマンスを大幅に向上させます。さらに性能を向上させるために、これをブロードフェーズクエリと組み合わせることもできます。

Back to top