クエリ システムを使用する

Gameware Navigation には、メモリにロードした NavData に対して使用する高性能なランタイム クエリのセットが用意されています。これらのクエリを使用して、次のような地形およびキャラクタの移動の可能性に関する幅広い知覚上の質問に回答することができます。

クエリ システムの概要

クエリのタイプ

使用可能なクエリーのタイプには、次のものがあります。

  • CanGo クエリは、レイ、球、またはセグメントが NavMesh の境界に衝突することなく開始地点から目的点まで進むことができるかどうかをテストします。
  • Cast クエリは、レイ、球、またはセグメントが NavMesh の境界に衝突することなく指定された方向に開始地点から進むことができる距離をテストします。
  • NavMesh 内の有効な位置を調べるクエリ
  • 指定した特定の条件(指定した位置に最も近いトライアングル、指定した位置に最も近い境界など)を満たす NavData 要素を検索するクエリ
  • NavMesh 上の開始地点から目的点までのパスを計画する、A* クエリーなどのパス ファインディング クエリー
  • NavData に対する衝突テストを行う CollisionRayCastQuery衝突システムを使用する」を参照してください。
  • その他

各タイプのクエリは、独自のクラスにカプセル化されています。完全なリストについては、IQuery クラスを実装するクラスを参照してください。各クエリ タイプの入力と出力を含む完全な説明については、クラスの説明を参照してください。

クエリとデータベース

それぞれのクエリは、1 つの Database に現在ロードされている NavData の完全なセットを考慮します。1 つのクエリを複数の Databases に対して同時に実行することはできません(ただし、異なる Databases に対してクエリの複数のコピーを平行して実行することはできます)。また、1 つのクエリを Database にロードされる一部のセクタに制限することもできません。

アトミック クエリとタイム スライス クエリ

クエリの各クラスは、アトミックまたはタイム スライスのいずれかです。

  • アトミック クエリは、1 フレーム内で計算を完了します。これらは計算中に呼び出しスレッドをブロックするため、処理を要求した直後に結果を得ることができます。これらのクエリ クラスはすべて IAtomicQuery から派生します。
  • タイム スライス クエリは、通常多くの計算を必要とし、CPU 使用率のピークを回避するために複数のフレームに計算を分割することができます。これらのクエリ クラスはすべて ITimeSlicedQuery から派生します。

WorkingMemory

すべてのクエリは、作業メモリ(一時データを記録するために計算時に使用するバッファ)を必要とします。たとえば、AStarQuery では、NavData のパスを検索するときに、調べる候補ノードのリストを記録するために作業メモリを使用する必要があります。

クエリを処理するたびに、計算時に使用するためにそのクエリに対して WorkingMemory クラスのインスタンスを提供することができます。インスタンスを提供しない場合、クエリは、クエリが調べるように設定されている Database が保持している共有バッファを使用します。

WorkingMemory インスタンスは常に 1 つのクエリのみが使用することができます。

即時クエリと非同期クエリ

作成した各クエリは、2 つのモードのいずれかで実行することができます。

  • 要求に応じて即時。このモードはクエリが処理されている間呼び出しスレッドをブロックしますが、クエリの結果にすぐにアクセスできることが保証されます。
  • 非同期、Gameware Navigation の次の更新フェーズ中。このモードでは、計算中に呼び出しスレッドが他のタスクを続けることができ、CPU のピークを回避するために World に組み込まれたシステムを利用します。現在のフレームでクエリを処理するための CPU 割り当てが経過している場合、アトミック クエリの処理は次のフレームまで遅延することができ、タイム スライス クエリは一時停止して次の更新で再開することができます。しかし、この方法を使用すると、少なくとも 1 つ、場合によっては複数のフレームでクエリの結果にアクセスできなくなります。

データ生成プロセスの後処理フェーズなど、ゲーム ループ外でクエリを起動する場合は、ブロックの方法を使用する必要があります。

クエリを設定する

クエリを設定するには:

  1. 実行するクエリ クラスのインスタンスを作成します。
  2. クエリーがその処理中に使用する NavData の Database と関連付けるために、そのクエリーの BindToDatabase() メソッドを呼び出します。また、この呼び出しによりすべてのオプション設定パラメータの値がリセットされ、クエリーをデフォルトの状態に戻します。
  3. クエリー クラスにより提供されるアクセサ メソッドを呼び出してクエリーのオプション設定パラメータを設定します。設定できる設定パラメータの種類は、使用しているクエリーのタイプによって異なります。詳細については、クエリー クラスのクラスの説明を参照してください。
  4. クエリーの Initialize() メソッドを呼び出し、必要な入力データを使用して設定します。詳細については、クエリ クラスのクラスの説明を参照してください。入力データをゲームの座標系から Gameware Navigation 座標系に変換することを忘れないようにします(必要な場合)。

    この呼び出しではその他の設定パラメータの値はリセットされないため、新しい入力データでクエリーを初期化するたびに再設定する必要はなく、同じクエリーを複数回実行する際に同じ設定値を再利用できます。

たとえば、次のコードは RayCastQuery を設定します。

Kaim::RayCastQuery<Kaim::DefaultTraverseLogic> rayCastQuery;
rayCastQuery.BindToDatabase(m_world->GetDatabase(0));

// Optional configuration ...
rayCastQuery.SetDynamicOutputMode(Kaim::QUERY_SAVE_TRIANGLES);

rayCastQuery.Initialize(start, move2D);

同じ Database に対して同じ設定を使用してクエリーを再実行する場合に行う必要があるのは、Initialize() を呼び出して新しい入力値を提供することだけです。

NavTag をテストする

多くのタイプのクエリでは、調べる NavData に関連付けられているカスタム NavTag を考慮することができます。これらのクエリはすべて、テンプレート引数として述部を受け取ります。クエリは NavTag 間のトランジションを検出すると、この述部からクラス メソッドを呼び出し、新しい NavTag を拒否するかどうかや、トランジションを交差できるかどうかを特定します。

この述部は、PathFollower クラスによって受け取られるものと同じで、AStarQuery によって使用されるカスタマイザとほとんど同じメソッドに依存しています。述語クラスに実装する必要があるインタフェースの要件の詳細については、「NavTag を禁止、回避、優先する」を参照してください。

NavTag の設定の詳細については、「カスタム データでタグ付けする」も参照してください。

クエリを起動する

クエリの起動は、即時に実行するか非同期に実行するかによって異なります。

即時モードでクエリを起動する

即時モードでクエリを起動するには、PerformQueryBlocking() メソッドを呼び出します。

例:

rayCastQuery.PerformQueryBlocking();

World の更新処理中にクエリを起動しない限り、任意のスレッドから即時クエリを起動することに問題はありません。

非同期にクエリを起動する

World の次の更新時に非同期にクエリを起動するには、World::PushAsyncQuery() メソッドを呼び出し、クエリ オブジェクトを渡します。World ではキューにクエリが配置され、CPU 割り当てが許可されると即座にクエリが実行されます。

例:

m_world->PushAsyncQuery(&rayCastQuery);

独自の QueryQueue を使用する

独自の CPU 割り当てを使用して独自の QueryQueue オブジェクトを設定し、クエリの処理を管理することができます。QueryQueue を処理すると、CPU 割り当てができなくなるまで順番にキューの先頭にあるクエリを実行します。そして、(通常は次のフレームで)再度処理するまで計算を一時停止します。

詳細については、QueryQueue クラスを参照してください。

即時モードのクエリの実行と同様に、World の更新処理中に処理しない限り、任意のスレッドから QueryQueue を処理することに問題はありません。

ただし、非同期モードのクエリの実行と同様に、キューが現在のフレームでの処理を終了した後すぐに各クエリの結果が得られるという保証はありません。

クエリの結果を取得する

GetResult() メソッドを呼び出すと、いつでもクエリの現在の結果を取得することができます。

すべてのクエリは、クエリ クラスに固有の列挙によって処理の状態と結果を示します。この列挙にはクエリ クラスと同じ名前が含まれ、接尾語 Result が付きます。たとえば、RayCastQuery では RayCastQueryResult が生成されます。

非同期にクエリを起動する場合、結果コードを使用すると、クエリが処理を正常に終了したかどうかを確認することができます。一般的に、DONE が含まれる列挙値はすべて、クエリの計算が完了していることを示します。たとえば、RayCastQuery では、処理を完了するまでに RAYCAST_NOT_INITIALIZED または RAYCAST_NOT_PROCESSED を返し、その後 RAYCAST_DONE_START_OUTSIDERAYCAST_DONE_ARRIVALPOS_FOUND_MAXDIST_REACHEDRAYCAST_DONE_LACK_OF_WORKING_MEMORY のいずれかを返すことがあります。

クエリのタイプと処理の結果によっては、クエリのその他のデータ メンバーから追加の出力を取得できる場合もあります。以下を参照してください。

交差したデータを取得する

NavMesh を通じて伝達する一部のクエリのタイプでは、交差するデータを記録することができます。たとえば、クエリは、伝達中に交差するトライアングルのリスト、または異なる NavTag と交差するサブセグメントのリストを記録できることがあります。クエリの後、交差したデータのリストを取得し、交差した地形のプロパティをクエリで検索することができます。たとえば、トライアングルに関連付けられている NavTag、高度の変更などに基づいてキャラクタが決定することができます。

これらのクエリにはすべて、SetDynamicOutputMode () メソッドが含まれています。これは、記録する必要がある出力データのタイプをクエリに伝えるために呼び出す必要があります。デフォルトでは、交差したデータは記録されません。SetDynamicOutputMode() を呼び出し、DynamicOutputMode 列挙から目的の値を渡すことにより、クエリに交差したデータを記録するように明示的に指示する必要があります。

交差したデータは、GetQueryDynamicOutput() メソッドを呼び出すことによって取得できます。これは、QueryDynamicOutput オブジェクトへのポインタを返します。このオブジェクトのメソッドにより、保存されたデータにアクセスできます。出力データをゲームの座標系に変換して戻すことを忘れないようにします(必要な場合)。

たとえば、次のコードは、交差する NavMesh のトライアングルを取得するように RayCastQuery を設定する方法を示しています。

Kaim::RayCastQuery<Kaim::DefaultTraverseLogic> rayCastQuery;
rayCastQuery.BindToDatabase(m_world->GetDatabase(0));
...
// enable saving triangles
rayCastQuery.SetDynamicOutputMode(Kaim::QUERY_SAVE_TRIANGLES);

rayCastQuery.Initialize(start, dir2D, maxdist);
rayCastQuery.PerformQueryBlocking();

// retrieve data
Kaim::QueryDynamicOutput* qoutput = rayCastQuery.GetQueryDynamicOutput();

// iterate through the list of triangles
KyUInt32 numberOfTriangles = qoutput->GetNavTrianglePtrCount();
for (KyUInt32 i_triangle = 0; i_triangle < numberOfTriangles; i_triangle++)
{
   const Kaim::NavTrianglePtr thisTriangle = qoutput->GetNavTrianglePtr(i_triangle);
   // handle the triangle here.
}

ビジュアル デバッグ

各フレームで任意のクエリの SendVisualDebug() メソッドを呼び出して、そのクエリに関する視覚的なデバッグ データをレンダリングすることができます。Navigation Lab をゲームに接続している間は、クエリは自動的に表示リストを作成し、Navigation Lab に送信します。

表示リストの内容と、その結果 Navigation Lab に表示されるデータは、クエリのタイプによって異なります。クエリに表示される視覚的なデータの意味を解釈するには、sdk¥include¥gwnavruntime¥queries¥blobs ディレクトリの QueryDisplayListBuilder クラスにあるインライン実装を参照してください。

クエリ完了の通知を受け取る

クエリを非同期に実行する場合や、処理するためにキューに配置する場合は、クエリが完了したときに通知を受け取ると便利です。処理の現在の状態を特定するのにクエリ結果をポールする必要がありません。

クエリの完了通知を受け取るには:

  1. IOnDone インタフェースを実装するカスタム クラスを作成します。
  2. クエリを起動する前に、カスタム クラスの新しいインスタンスを作成し、クエリ クラスの m_onDone メンバーを新しいインスタンスへのポインタに設定します。

クエリを実行のために World にプッシュする場合、クエリが処理されると、World の更新時にオブジェクトの IOnDone::OnDone() メソッドが自動的に呼び出されます。独自の QueryQueue を使用する場合、オブジェクトはクエリが処理された後、次回 QueryQueue::FlushQueries() を呼び出すときに呼び出されます。

クエリ クラスの m_onDone メンバーは、IOnDone::OnDone() メソッドが呼び出されると KY_NULL にリセットされます。同じクエリ インスタンスを再起動し、通知を再度受け取る場合は、m_onDone メンバーを再設定する必要があります。

Navigation Lab のクエリ

Gameware Navigation API のクエリの多くは、クエリを実行し、クエリの結果についてビジュアル フィードバックを提供する Navigation Lab にも公開されています。これは、地形用に生成された NavData をテストし、各クエリのさまざまな入力および出力を理解するのに役立ちます。

Navigation Lab でのクエリの実行の概要については、「Navigation Lab で作業を開始する」を参照してください。