ナビメッシュエージェントの作成
ナビメッシュエージェントの使用
Quantum 2.0 以降、ナビメッシュエージェントは複数のコンポーネントに分割されました。ナビメッシュとステアリングを扱う開発者は、最終的な移動結果を制御したいと考えることが多くあります。これはゲーム体験にとって非常に重要であるため、多くの意味を持ちます。新しいナビメッシュエージェントパーツは、マルチスレッドパフォーマンスを失うことなく、無駄な部分を実行したり、不要なメモリを浪費したりすることなく、ナビメッシュサポートの組み合わせを選択するのに役立ちます。
エージェントコンポーネントは NavMeshPathfinder、NavMeshSteeringAgent、および NavMeshAvoidanceAgent です。スタンドアロンコンポーネントは NavMeshAvoidanceObstacle です。
エージェントエンティティは、Unity 内でエンティティプロトタイプを使用するか、コードでエンティティを構築することにより作成できます。これらは依然として Quantum アセット NavMeshAgentConfig を使用します。
Unity でのエンティティプロトタイプを使用したエージェントの作成
- Unity メニューで空の Quantum プロトタイプを作成します:
GameObject/Quantum/Empty Entity - エンティティを選択し、
Transformを2Dに設定します。 NavMeshPathfinderコンポーネントをオンにします。- デフォルトの
NavMeshAgentConfigを選択します。 Initial Targetをオンにして、Unity シーンから移動するための初期位置を提供するトランスフォームを選択します。- ベイクされた Quantum ナビメッシュを選択します(ナビメッシュワークフローを参照)。
- デフォルトの
NavMeshSteeringAgentを有効にします。- パスのギズモを見るには:
- デフォルトの
NavMeshAgentConfigでShow Debug Steeringを有効にするか、 QuantumEditorSettingsでナビメッシュギズモDraw Pathfinder Funnelを有効にします。
- デフォルトの
- プレイボタンを押します。
コードでのコンポーネントを使用したエージェントの作成
代わりに、エージェントエンティティはコード内で組み立てることもできます。
最初に、エンティティは Transform2D または Transform3D コンポーネントを必要とし、View コンポーネントを追加することで、シーンにプレハブがレンダリングされます。
最も重要なコンポーネントは NavMeshPathfinder です。これにより、経路探索が行われ、ターゲット位置とユーザー定義のウェイポイントの数を保存し、ウェイポイントの進行状況を検出します。このコンポーネントは NavMeshPathfinder.Create() ファクトリーメソッドを使用して作成し、NavMeshAgentConfig を渡します。
NavMeshSteeringAgent コンポーネントはオプションであり、NavMeshPathfinder を必要とします。最大速度、加速度、および回転速度の変数があり、実行中に変更できます。また、経路に沿ってエンティティを操縦します。このコンポーネントを使用しない場合、開発者は MovementType を Callback に変更し、最新の回避データを取得しながら独自の移動を注入できます。回転速度と加速度は 0 に設定して無効化できます。
NavMeshAvoidanceAgent は NavMeshPathfinder と NavMeshSteeringAgent の両方のコンポーネントを必要とし、これらはそのコンポーネントの前にエンティティに Set() される必要があります。このエージェントは、優先度やマスク、レイヤーを使用して他の移動エージェントを回避する計算を行います。最初には NavMeshAgentConfig で設定されている優先度、マスク、レイヤーは、実行中にコンポーネントで変更することができます。
エージェントが物理ボディによって操縦されるようにしたい場合(たとえば、エージェントが静的衝突を貫通しないようにする)には、エンティティには PhysicsCollider2D/3D と PhysicsBody2D が必要です。これを有効にするには、NavMeshAgentConfig の MovementType を DynamicBody に設定する必要があります。
C#
public override void OnInit(Frame f) {
base.OnInit(f);
var entity = f.Create();
f.Set(entity, new Transform3D() { Position = FPVector3.Zero, Rotation = FPQuaternion.Identity });
var config = f.FindAsset<NavMeshAgentConfig>(NavMeshAgentConfig.DEFAULT_ID);
var pathfinder = NavMeshPathfinder.Create(f, entity, config);
// 移動するためのランダムなポイントを見つける
var navmesh = f.Map.NavMeshes["Navmesh"];
if (navmesh.FindRandomPointOnNavmesh(FPVector2.Zero, FP._10, f.RNG, *f.NavMeshRegionMask, out FPVector2 randomPoint)) {
pathfinder.SetTarget(f, randomPoint, navmesh);
}
f.Set(entity, pathfinder);
f.Set(entity, new NavMeshSteeringAgent());
}
ナビメッシュエージェントギズモ Draw Nav Mesh Agents を有効にして、シーンウィンドウ内でエージェントギズモの描画を有効にします。
重要なエージェント設定
パスファインダー
NavMeshPathfinder.SetConfig() は、コンポーネントの作成中および実行時に実行できます。エージェントが現在経路を追従していて、新しい設定からのウェイポイント数が異なる場合、経路はリセットされます。この設定は、エンティティの NavMeshSteeringAgent および NavMeshAvoidanceAgent コンポーネント上で自動的に更新され、Speed、Acceleration、AvoidancePriority、Layer、Mask の値は設定値にリセットされます。
NavMeshAgentConfig.MaxRepathTimeout は、ウェイポイントがこの時間内に到達しなかったときにエージェント経路探索をトリガーする秒数です。これは、詰まっているエージェントを軽減するためのフェイルセーフです。値を 0 に設定すると無効化されます。
NavMeshAgentConfig.LineOfSightFunneling は、ナビメッシュの中間に位置するナビメッシュリージョンが使用される場合に有効にする必要があります。たとえば、破壊可能な建物です。リージョンによって導入される余分な三角形は、アクティブリージョンの近くに異常な経路を引き起こすことがあります。このオプションにより、リージョンの近くに不要なウェイポイントが削除されます。
NavMeshAgentConfig.DynamicLineOfSight は、エージェントが各ティックごとにウェイポイントをスキップできるか確認します。このオプションはコストが高いですが、経路に不必要なウェイポイントを削除します。
一方で NavMeshAgentConfig.DynamicLineOfSightWaypointRange が設定されている場合、ラインオブサイトのチェックはウェイポイントに近い場合(範囲)にのみ各ティックごとに実行されます。これにより、DynamicLineOfSight を有効にしていなくても機能します。
NavMeshAgentConfig.AutomaticTargetCorrection/Radius は、SetTarget() 実行時にナビメッシュの外に位置が選択された場合に役立ちます。範囲パラメータは、ターゲットの周囲で最適なターゲットの置き換えを検索するために使用され、ナビメッシュ内にあります。
この値はまた、開始位置を修正するためにも使用され、そうでなければ許容のために半径0.25を使用します。
以下の画像では、黄色の X でマークされたターゲットがあります。範囲 0 は、たとえば、目的地を見つけるのに失敗します。一方、範囲 1 は、黄色の点線で示されたナビメッシュ上の最も近い位置を見つけます。検索範囲を非現実的に大きくすると、パフォーマンスに大きな影響を与えることになります。
NavMeshPathfinder のキャッシュされたウェイポイント数を NavMeshAgentConfig.CachedWaypointCount を使用して設定します。より多くの非一時的データを保存すると、シミュレーションが遅くなることを忘れないでください。キャッシュに保存されている最初のウェイポイントは、SetTarget() が呼び出されたときのエージェントの現在の位置であり、ウェイポイント到達の検出を強化するために使用されます。エージェントが最後のウェイポイントに向かって操作を開始すると、自動的に再経路探索を実行して、フレーム計算中にエージェントが有効なウェイポイントを持たない状況を軽減します。
NavMeshAgentConfig.EnableWaypointDetection をオンにすると、エージェントがウェイポイントに到達するのに苦労している場合(たとえば、回転速度が遅い場合や回避が原因の場合)に役立ちます。次のパラメータ Axis Extend と Axis Offset は、ウェイポイント到達検出の軸(黒い線)を定義します。エージェントが黄色のゾーンに入ると、ウェイポイントに到達したと見なされます。
パスファインダーコンポーネントにステアリングコンポーネントが伴っていない場合は、DefaultWaypointDetectionDistance を使用してウェイポイント到達検出を実行し、エージェントの最大速度 * デルタ時間に設定する必要があります。「便利なナビメッシュエージェント設定」セクションでは、ウェイポイント到達検出を向上させる方法を説明します。
ステアリングエージェント
NavMeshAgentConfig.StoppingDistance と AutoBraking は、最終ターゲットに接近しているエージェントに適用されます。StoppingDistance は、エージェントが目的地の前で停止する絶対距離であり、この値を設定することでエージェントが安定し、オーバーシュートを防ぐのに役立ちます。エージェントは、残り距離がエージェントの現在の移動距離より小さい場合は常に停止します。
AutoBraking は、目的地に到達する前にエージェントの速度を減速させる視覚的な機能であり、エージェントの停止動作を安定させるのにも使用できます。AutoBrakingDistance は、エージェントが減速を始める目的地の周囲の半径を定義します。内部的には、ブレーキ動作をスムーズにするために平方根が使用されます。
ナビメッシュエージェントが PhysicsBody の MovementType を使用して幾何学に押し戻されない場合、特に回避を使用している場合、エージェントはナビメッシュの外に移動します。これを完全に防ぎ、エージェントがナビメッシュの境界に沿ってスライドするようにするには、NavMeshAgentConfig.ClampAgentToNavmesh を有効にします。
エージェントは、ナビメッシュの MinAgentRadius よりも大きな半径を持つ可能性があります。Quantum は、エージェントのウェイポイントを境界からさらに遠くに移動させることでこれをサポートしていますが、ナビメッシュにエージェントを固定することは非常に複雑になります。そのため、ClampAgentToNavmeshRadiusThreshold パラメータは、どのテクニックを選択するべきかを選定する手助けをします。エージェントがナビメッシュの外に移動しないように、半径を増やします。
修正を安定させるため、エージェントは全体の侵入深度のうちの一定の割合(ClampAgentToNavmeshCorrection)のみ移動します。
更新間隔
パフォーマンス最適化の理由から、各個々のエージェント(設定)は、パスファインディングと回避を 毎回のシミュレーションティックで実行しない ように構成できます。NavMeshAgentConfig.UpdateInterval を 1 より大きい値に設定して、更新の回数を減らします。これにより、エージェントの反応が鈍くなりますが、CPU の時間が節約されます。エージェントエンティティのインデックスは、更新する正確なティックを定義するのに使用されるため、すべてのエンティティが同じティックで更新されるわけではありません。
この公式は次のようになります:
C#
updateAgent = entity.Index % agentConfig.UpdateInterval == f.Number % agentConfig.UpdateInterval
- 1 = 毎ティックで更新
- 2 = 毎2ティックで更新
- 8 = 毎8ティックで更新、など
ナビメッシュエージェントコールバックの使用
エージェントからのすべてのコールバックはメインスレッドから呼び出され、他のコンポーネントやエンティティへのアクセス時にマルチスレッドの問題を引き起こしません。
ナビゲーションエージェントのコールバックはオプトイン形式です。シミュレーション設定を開いて、Enable Navigation Callbacks を切り替えます。
以下のシグナルは、エージェントをさらに制御するために使用できる即時フィードバックを提供します。
C#
namespace Quantum {
public unsafe partial class NavMeshAgentTestSystem : SystemMainThread,
ISignalOnNavMeshSearchFailed,
ISignalOnNavMeshWaypointReached,
ISignalOnNavMeshMoveAgent {
}
}
ISignalOnNavMeshSearchFailed は、エージェントが現在位置から SetTarget() で設定されたターゲットまでのパスを生成できなかったときに呼び出されます。たとえば、目的地がナビメッシュに一致しない場合です。このコールバックの際に SetTarget() を実行する場合、resetAgent パラメータを false にしてください。
ISignalOnNavMeshWaypointReached は、エージェントがターゲットへの経路のウェイポイントに到達したときに呼び出されます。ウェイポイントに関する詳細については、WaypointFlags 列挙型を参照してください: Target、LinkStart、LinkEnd。
ISignalOnNavMeshMoveAgent は、NavMeshAgentConfig.MovementType が Callback に設定され、エージェントに NavMeshSteeringAgent コンポーネントがある場合にのみ呼び出されます。desiredDirection パラメータは、内部のステアリングと回避がエージェントの移動ベクトルが何であるべきかを考慮して正規化された方向です。
C#
public void OnNavMeshMoveAgent(Frame f, EntityRef entity, FPVector2 desiredDirection) {
var agent = f.Unsafe.GetPointer<NavMeshSteeringAgent>(entity);
// エージェントを移動させるためのシンプルなデモ
if (f.Has<Transform2D>(entity)) {
var transform = f.Unsafe.GetPointer<Transform2D>(entity);
transform->Position.X.RawValue = transform->Position.X.RawValue + ((desiredDirection.X.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
transform->Position.Y.RawValue = transform->Position.Y.RawValue + ((desiredDirection.Y.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
transform->Rotation = FPVector2.RadiansSignedSkipNormalize(FPVector2.Up, desiredDirection);
} else if (f.Has<Transform3D>(entity)) {
var transform = f.Unsafe.GetPointer<Transform3D>(entity);
transform->Position.X.RawValue = transform->Position.X.RawValue + ((desiredDirection.X.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
transform->Position.Z.RawValue = transform->Position.Z.RawValue + ((desiredDirection.Y.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
var desiredRotation = FPVector2.RadiansSignedSkipNormalize(FPVector2.Up, desiredDirection);
transform->Rotation = FPQuaternion.AngleAxis(desiredRotation * FP.Rad2Deg, -FPVector3.Up);
}
}
一般的なナビメッシュエージェントのセットアップ
パスファインドのみ
MapNavMeshPathfinder コンポーネントを使用して、マルチスレッドパスファインディングを実行し、ターゲットとウェイポイントを保存し、ウェイポイントインデックスの進行を行います。ステアリング、回避、および移動は独自のシステムで制御します。
ウェイポイントの進行が機能するためには、パスファインダーコンポーネントがウェイポイントに到達する速度に関する情報が必要です。各フレームごとに WaypointDetectionDistanceSqr プロパティを設定してください。
回避なし
パスファインダーとステアリングエージェントコンポーネントのみを使用します。回避コードは実行されず、コンポーネントは回避関連のデータを保持しません。SimulationConfig.Navigation.EnableAvoidance をオフにして CPU 時間を節約します。
カスタム移動だが Quantum 回避あり
すべての三つのコンポーネント(パスファインダー、ステアリングエージェント、回避エージェント)を使用します。回避エージェントはステアリングエージェントの一部に依存しますが、オーバーライドしたい場合は、NavMeshAgentConfig で MovementType を Callback に設定し、ISignalOnNavMeshMoveAgent シグナルを実装します(前のセクションを参照)。desiredDirection パラメータには、回避によって変更された移動方向が含まれています。