This document is about: QUANTUM 3
SWITCH TO

クエリ

はじめに

クエリは、動的エンティティと静的コライダーを考慮に入れることができます。レイ、ライン、およびシェイプオーバーラップの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または静的コライダー情報を保持しています。これらは排他的であり、1つが有効であれば、もう1つはnullになります。
  • Countは常にHitCollectionを反復処理するために使用するべきです。
  • ヒットはソートされていません。Sort()を呼び出し、FPVector2を渡すことでソートできます。これにより、提供されたリファレンスポイントからの距離に基づいてヒットがソートされます。

レイキャストはラインキャストの構文シュガーです。働きは同じで、startおよびendの代わりにstartdirection、およびmax-distanceを指定するだけです。また、ラインキャストおよびレイキャストにオプションのパラメータを渡すことができます:

  • LayerMask:どの物理レイヤーに対してキャストを行うかを指定します。
  • QueryOptions:キャストで考慮するコライダーのタイプを指定します。

Quantumは、次の2つのタイプのシェイプクエリをサポートしています。

  • ShapeOverlap
  • ShapeCasts

これらは、Quantumでサポートされているすべての動的シェイプに対して使用できます。

注意: CompoundShapesは、シェイプクエリを実行するために使用できます。詳細については、シェイプコンフィグ(Shape Config) のページをお読みください。

シェイプオーバーラップ(ShapeOverlaps)

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

  • センター位置(FPVector2またはFPVector3);
  • 回転(FPまたは3Dの場合はFPQuaternion);
  • シェイプ(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];
}

シェイプキャスト(ShapeCasts)

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

  • センター位置(FPVector2またはFPVector3);
  • シェイプの回転(3Dの場合はFPまたはFPQuaternion);
  • シェイプポインタ(Shape2DまたはShape3DPhysicsColliderからのもの、または呼び出す際に作成されたもの);
  • 距離および方向をベクトルとして表現したもの(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ベースのアルゴリズムを使用します。初期のシェイプキャスト位置がコライダー内に含まれている場合、QueryOptionsパラメータのDetectOverlapsAtCastOriginフラグが有効でない限り、そのコライダーとのヒットを検出することはできません。このフラグが有効であると、開始位置で追加のチェックが行われます。

GJKConfigの設定は、SimulationConfigアセットのPhysics > GJKConfigセクションで利用できます。これらの設定により、精度とパフォーマンスのバランスを取ることができ、両者にはトレードオフがあります。デフォルトの値は、一般的なサイズのシェイプに妥協してバランスが取れています。

  • Simplex Min/Max Bit Shift:Voronoiシンプルックス内のポイントの精度を向上させるため、原始的な値を徐々にシフトさせることができ、退化したケースを回避しながら物理空間内の有効範囲のポジションを損なうことがありません。関与するシェイプのスケールやそれらの間の距離が非常に小さい場合は、値を増やすことを検討してください。
    • Shape Cast Max Iterations:アルゴリズムがハードトレランス以下のソリューションを探す際に実行される最大反復回数です。これを増加させることで、パフォーマンスが最悪の場合にコストがかかる一方、より正確な結果が得られる可能性があります。その逆も然りです。
      • Shape Cast Hard Tolerance:このしきい値よりも低い反復結果(シェイプ間の最も近い距離)は、終了条件として許容されます。これを減少させることで、より正確な結果が得られる可能性がありますが、その分多くの反復が必要になります。その逆も然りです。
      • Shape Cast Soft Tolerance:定義されたハードトレランスの下で許可された最大反復内に受け入れ可能な結果が見つからない場合でも、これまでに見つかった最良の結果がこのソフトしきい値を下回っていれば、ポジティブな結果が返されます。このような場合にこのしきい値を増加させると、偽陽性の可能性が高まる一方、減少させると偽陰性が増加します。

ヒットのソート

すべてのクエリは、HitCollectionを返す場合にソートできます。

  • Sort():2Dの場合はFPVector2、3Dの場合はFPVector3を受け取り、提供されたポイントまでの距離に基づいてコレクションをソートします。
  • SortCastDistance()ShapeCastクエリの結果をソートするために使用されます。引数は必要なく、キャスト距離に基づいてヒットを順序付けます。

オプション

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

ヒット法線

最もパフォーマンスの良いクエリを提供するために、すべてのデフォルトクエリは、2つのシェイプが重なっているかどうかのみをチェックします。デフォルトでは、ほとんどのクエリはヒットポイントや法線を取得しません。

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

  • ポイント
  • 法線
  • 侵入深度

レイと三角形の衝突チェックにおいて、法線は常に三角形の法線となります。この法線は三角形データにキャッシュされているため、この場合は追加の計算は行われません。

ヒットのフィルタリング

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

  • HitStatics:静的コライダーのみをヒットします。

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

    • PhysicsColliderを持ち、__PhysicsBody__がないエンティティ
    • PhysicsColliderを持ち、__無効__なPhysicsBodyを持つエンティティ
    • PhysicsColliderを持ち、__運動エンティティ__のPhysicsBodyを持つエンティティ
  • HitDynamics:__有効__かつ__非運動エンティティ__のPhysicsBodyを持つエンティティのみをヒットします。

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

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

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

ブロードフェーズクエリ

Quantumには、物理システム中に物理クエリ(レイキャストやオーバーラップ)を解決するためのオプションがあります。このためには次の手順が必要です:

  1. システムを作成します。
  2. それをCore.PhysicsSystemの前にあるユーズシステム設定アセットに追加します。
  3. Core.PhysicsSystemの後で実行される任意のシステムで情報を取得します。

この設定は、物理ステップでの並行処理の利点を活用しており、物理処理後の通常のクエリよりも大幅に高速です。

Broadphase Queries System

注: ブロードフェーズクエリは、ソルバーが実行される前に物理エンジンにスケジュールまたはインジェクトされるため、時にはインジェクテッドクエリやスケジュールクエリとも呼ばれます。

クエリのインジェクション

物理処理の前に実行される任意のメインスレッドシステムからクエリをインジェクトすることが可能です。インジェクトされたクエリは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);
            }
        }
    }
}

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

クエリ結果の取得

クエリ結果は、コア物理システムの後で実行される任意のシステムから取得できます。結果(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
                }
            }
        }
    }
}

そのほかに、public bool GetAllQueriesHits(out HitCollection* queriesHits, out int queriesCount)を呼び出すことで、すべてのブロードフェーズ結果を取得することも可能です。この呼び出しもFrame.Physicsから利用できます。

注意事項

ブロードフェーズクエリを使用する際に留意すべき重要な点は以下の通りです:

  • パフォーマンスは、大量のデータ(例:弾丸)に対して約20倍向上します。
  • 物理システムが稼働する前のフレームの状態に基づいています。
  • ブロードフェーズクエリはフレーム間で引き継がれません。つまり、物理処理の前にフレームの開始時にインジェクトする必要があります。物理処理が実行された後にインジェクトされたブロードフェーズクエリは、決して結果を返しません。これは、Quantumの物理エンジンが状態を持たない(ステートレス)ためです。

CCDのエミュレーション

Quantumの物理エンジンは状態を持たないため、連続衝突検出(CCD)はこのようなシステムでは過度にコストがかかります。状態を持たない物理エンジンでCCDの動作をエミュレートするためのソリューションは、通常、予想される動きを1フレーム分延長したレイキャストやシェイプオーバーラップを用いることです。

このトピックは、弾丸のような速く動くエンティティと組み合わせてよく出てきます。速く動くオブジェクトのサイズに応じて、次のいずれかのアプローチを使用することをお勧めします:

  • 移動方向に沿った短いレイを、長さをvelocity * deltaTimeとする;または、
  • 単一のオーバーラップを使用する;シェイプオーバーラップは複合シェイプでも行うことができます。

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

Back to top