ナビメッシュエージェントの作成
ナビメッシュエージェント機能は、複数のコンポーネントに分けられています。
NavMeshPathfinder:マルチスレッドで経路探索を実行し、ウェイポイントと進行状況を保存しますNavMeshSteeringAgent:エージェントのステアリング(速度・加速度・回転速度・ブレーキの計算)を実行しますNavMeshAvoidanceAgent:エージェント同士が相互に回避できるようにしますNavMeshAvoidanceObstacle:エンティティをNavMeshAvoidanceAgentの回避対象にします
エージェントコンポーネントは、Transform2D/Transform3Dコンポーネントのいずれかで動作します。
すべてのコンポーネントは、一つのNavMeshAgentConfigコンフィグアセットを共有します。
これらは様々なユースケースに応じて、組み合わせることが可能です。
| 完全 | Pathfinder + SteeringAgent + AvoidanceAgent | 3つすべてのコンポーネントを持つエンティティは、経路を探索し、それに沿って移動し、動的に他のエージェントを回避できます。 |
| 回避なし | Pathfinder + SteeringAgent | 回避コードは実行されず、コンポーネントは回避関連のデータを保持しません。
SimulationConfig.Navigation.EnableAvoidanceを無効にすると、CPU時間を節約できます。 |
| カスタム移動 | Pathfinder + SteeringAgent | エージェントは経路探索と経路の保存が可能ですが、独自システムで移動シグナルISignalOnNavMeshMoveAgentを実装することで、ステアリングは無効化されます。(注意:エージェントが実際にターゲットを持つ場合のみコールバックが実行されます)
NavMeshAgentConfig.MovementTypeをCallbackに設定します。
SimulationConfig.Navigation.EnableNavigationCallbacksを有効にする必要があります。 |
| 回避ありのカスタム移動 | Pathfinder + SteeringAgent + AvoidanceAgent | Custom movement要件に加えて、ISignalOnNavMeshMoveAgentのdesiredDirectionパラメーターには、回避行動後の移動方向が含まれます。
|
| 経路探索のみ | Pathfinder | エージェントは経路探索とウェイポイント進行状況の保存を実行します。ステアリング・回避・移動は、独自システムで処理されます。
Custom movementの設定が必要です。
ステアリングコンポーネントなしでウェイポイント進行状況を機能させるには、エージェントがウェイポイントに接近する速度情報が必要です。 NavMeshPathfinder.WaypointDetectionDistanceSqrは毎フレーム設定する必要があります。 |
エンティティプロトタイプによるエージェントの作成
チュートリアルでは、ベイク済みQuantumナビメッシュを含むUnityシーンを前提にしています。(Unityナビメッシュのインポートをご覧ください)
GameObject > Quantum > Empty Entityから、シーン上にQuantumプロトタイプを作成するQuantumEntityPrototypeを選択して、Transformを2Dに設定するQuantumEntityPrototypeを選択して、NavMeshPathfinderを有効にするNavMeshPathfinder.Initial Targetを展開して、ワールド座標Positionを設定するInitial TargetのNavMeshで、ベイク済みQuantumナビメッシュアセットを選択する- 任意で
NavMeshAgentConfigアセットを割り当てるか、デフォルトのコンフィグを使用する
QuantumEntityPrototypeを選択して、NavMeshSteeringAgentを有効にするゲームオブジェクトを右クリック > 3D Object > Capsuleから、Unityビューにゲームオブジェクトを追加して、カプセルを1単位上に動かす- Open the Quantum Gizmosオーバーレイメニューを開き、
NavMesh Pathfinderを切り替える - Playを押す
コードによるエージェントの作成
チュートリアルでは、ベイク済みQuantumナビメッシュを含むUnityシーンを前提にしています。(Unityナビメッシュのインポートをご覧ください)
新しいQuantumシステムを作成するためには、QuantumUser > Simulationフォルダーを右クリックし、Create > Quantum > Systemを選択します。スクリプトとクラス名をNavMeshAgentTestSystemに変更して、NavMeshAgentTestSystemアセットを開き、DefaultSystemsConfig.Entriesに作成したシステムを追加してください。
Quantum Gizmosオーバーレイメニューを開き、NavMesh Pathfinderを有効にして、再生中にエージェントのギズモが表示されるようにします。Unityのプレハブを作成するには、Viewコンポーネントを追加する必要があります。(この例では説明しません)
以下のシステムをコピーして、OnInit()内でエージェントを作成してターゲット位置を設定します。
C#
namespace Quantum
{
using Photon.Deterministic;
using UnityEngine.Scripting;
[Preserve]
public unsafe class NavMeshAgentTestSystem : SystemMainThread
{
public override void OnInit(Frame frame)
{
base.OnInit(frame);
var entity = frame.Create();
// Transform3D(または2D)コンポーネントを追加する
frame.Set(entity, new Transform3D()
{
Position = FPVector3.Zero,
Rotation = FPQuaternion.Identity
});
// ファクトリーメソッドを使用してPathfinderコンポーネントを作成し、任意でNavMeshAgentConfigを渡す
var pathfinder = NavMeshPathfinder.Create(frame, entity, null);
// 名前からナビメッシュを探し、コンポーネントに追加する前にターゲットを設定する
var navmesh = frame.Map.NavMeshes["Navmesh"];
pathfinder.SetTarget(frame, new FPVector3(12, 0, 0), navmesh);
// pathfinderとステアリングコンポーネントをエンティティに追加する
frame.Set(entity, pathfinder);
frame.Set(entity, new NavMeshSteeringAgent());
}
public override void Update(Frame frame)
{
}
}
}
NavMeshPathfinderコンポーネント
NavMeshPathfinderは、経路の生成・ウェイポイントの保存・ウェイポイント進行を実行する主要なコンポーネントです。
このコンポーネントで経路を生成して移動するには、SetTarget()を呼び出す必要があります。入力されたTarget位置は保存され、微修正が可能なInternalTargetも作成されます。
ターゲットを設定すると、エージェントはIsActiveになります。ターゲットに到達すると、自動的に非アクティブ化されます。
ウェイポイントはGetWaypoint()から確認できます。ウェイポイント数はWaypointCountから、現在のウェイポイントはWaypointIndexから取得できます。
エージェントを即時停止するには、Stop()が使用できます。
NavMeshPathfinder.SetConfig()は、コンポーネント作成時または実行時に呼び出し可能です。現在のエージェントが経路を辿っていて、新しいコンフィグのウェイポイント数が異なる場合、その経路はリセットされます。コンフィグはエンティティのNavMeshSteeringAgentとNavMeshAvoidanceAgentコンポーネントで自動的に更新され、Speed・Acceleration・AvoidancePriority・Layer・Maskは、コンフィグ値にリセットされます。
NavMeshAgentConfig - 経路探索
| UpdateInterval | パフォーマンス最適化のため、ティックごとに経路探索や回避を実行しないように、エージェントを調整できます。
UpdateIntervalを1より大きい値に設定すると、更新頻度が減少します。これによって、エージェントの応答性は低下しますが、CPU時間を節約できます。エージェントのエンティティのインデックスを使用して正確な更新頻度を定義するため、すべてのエンティティが同じティックで更新されることはありません。
計算式は次の通りです。 updateAgent = entity.Index % agentConfig.UpdateInterval == f.Number % agentConfig.UpdateInterval
1 = 毎ティック更新2 = 2ティックごとに更新8 = 8ティックごとに更新 |
| PathQuality | A*のヒューリスティック関数を変更して、経路の品質に反映します。Goodは、品質とパフォーマンスとの最適なバランスを取ります。
Fast - 親Gとマンハッタン距離を使用します。Good - ゴールへの進入エッジ上にピボットを作成し、Gとマンハッタン距離を再計算します。Best - ゴールへの進入エッジ上にピボットを作成し、スタートへの別のピボットのGとユークリッド距離を再計算します。 |
| CachedWaypointCount | NavMeshPathfinderにキャッシュされるウェイポイント数を調整します。
コンポーネントに保存できるウェイポイント数には制限があります。これは、一時的ではないデータ量を増やすとシミュレーション速度が低下するためです。 エージェントが最後のウェイポイントに向かって移動を開始すると、自動的に経路探索が再実行され、ウェイポイントを更新するための新しい経路が計算されます。 キャッシュに保存される最初のウェイポイントは、 SetTarget()が呼び出された際のエージェントの現在位置で、ウェイポイント到達判定精度を高めるために使用されます。
|
| MaxRepathTimeout | タイムアウト時間(秒)で、この時間までにウェイポイントに到達しなかった場合、新しい経路探索がトリガーされます。エージェントがスタックする現象を軽減するためのフェイルセーフ機能です。無効化するには値を0に設定してください。 |
| LineOfSightFunneling | 有効にすると、視線判定を使用してウェイポイントが削除されます。
このオプションは、ナビメッシュのリージョンがメインのナビメッシュの中央にある場合(例:破壊可能オブジェクト)に有効にする必要があります。リージョンによって追加される三角形は、アクティブなリージョン付近で不自然な経路を生み出すことがあります。 |
| DynamicLineOfSight | 有効にすると、エージェントはウェイポイントをスキップ可能かどうか毎ティック確認します。このオプションは負荷が高いですが、経路上の不要なウェイポイントをすべて削除します。 |
| DynamicLineOfSightWaypointRange | これはDynamicLineOfSightと似ていますが、エージェントがウェイポイントの特定範囲内にいる場合のみトリガーされます。
0に設定すると無効化されます。
|
| AutomaticTargetCorrection | 無効化すると、ナビメッシュ外の位置にいる際にSetTarget()が失敗する可能性があります。
3Dナビメッシュでは絶対に無効化しないでください。 |
| AutomaticTargetCorrectionRadius | ターゲット周辺の有効なナビメッシュを検索する範囲です。
この値は開始位置の補正にも使用され、使用しない場合は半径 0.25の許容範囲が使用されます。
以下の画像では、ターゲットの黄色の Xについて、補正半径がナビメッシュ上の最も近い有効な位置を見つけて、Input Targetを変更せずに内部ターゲットを補正します。
3Dナビメッシュでは絶対に 0に設定しないでください。デフォルトは1です。
半径を不必要に大きくしすぎると、非常に負荷がかかる可能性があります。
|
| EnableWaypointDetection | この機能によって、エージェントがウェイポイントに到達しにくい状況(例:回転速度や回避が遅い)が緩和されます。
パラメーターの Axis ExtendとAxis Offsetは、ウェイポイント到達判定軸(黒線)を定義します。エージェントが黄色ゾーンに入ると、ウェイポイントに到達したとみなされます。
|
| DefaultWaypointDetectionDistance | エージェントがNavMeshSteeringAgentコンポーネントを持たない場合、この値はウェイポイント到達判定に使用され、エージェントのmax speed * delta timeに設定する必要があります。
この値は、各ティックで WaypointDetectionDistanceSqrを直接設定する際には使用されません。 |
NavMeshSteeringAgentコンポーネント
NavMeshSteeringAgentコンポーネントの使用は任意です。このコンポーネントはNavMeshPathfinderを必要とします。
AccelerationとMaxSpeedは、実行時に変更できます。初期値はコンフィグから設定されます。
回転速度と加速度を0に設定すると、無効化することができます。
CurrentSpeedとVelocityはコンポーネントから取得できます。
NavMeshAgentConfig - ステアリング
| MovementType | エージェントの移動は、transformに直接適用するか、移動コールバックを実行するカスタム移動ロジックを実装できます。
None - 移動は適用されません。Transform - 移動は、エンティティのTransform2D/Transform3Dコンポーネントに適用されます。Callback - 移動は適用されませんが、ISignalOnNavMeshMoveAgentシグナルが実行されます。 |
| VerticalPositioning | このオプションはTransform3D使用時のみ有効です。エージェントのY座標の計算方法を定義します。
None - 垂直方向の位置は適用されません。Navmesh - ナビメッシュにレイキャストします。Navmeshはデフォルト値ですが、キャラクターの移動に対してナビメッシュのジオメトリは単純すぎるという欠点があります。Physics - 3D物理ジオメトリにレイキャストします。Physicsを使用するには、Quantumコライダーで構成された歩行可能な地面が存在する必要があります。 |
| Speed | エージェントの最高速度です。
実行時に Speedを変更するには、NavMeshSteeringAgent.MaxSpeedを使用してください。 |
| AngularSpeed | エージェントの角速度(ラジアン/秒)です。
0に設定すると、エージェントの回転が無効になります。200以上に設定すると、回転が即時反映されるようになります。 |
| Acceleration | エージェントの加速度です。
0に設定すると、加速度が無効になります。
|
| StoppingDistance | エージェントがターゲット手前で停止するようになる距離で、オーバーシュートを回避して安定性を高めます。残距離がエージェントのティックあたりの移動距離未満になった場合、エージェントは停止します。 |
| AutoBraking | 有効にすると、目標に近づいたエージェントは減速を開始します。 |
| AutoBrakingDistance | エージェントが減速を開始するまでのターゲットへの距離です。 |
| ClampAgentToNavmesh | MovementTypeがTransformの場合のみ選択可能です。
有効にすると、物理コライダーと同様に、エージェントがナビメッシュ領域外から押し出されるようになります。エージェントは回避時などにナビメッシュ領域外に逸れることがあります。 |
| ClampAgentToNavmeshCorrection | 各ティックでエージェントが補正される割合(パーセント)です。 |
NavMeshAvoidanceAgentコンポーネント
NavMeshAvoidanceAgentコンポーネントを正しく動作させるためには、NavMeshPathfinderとNavMeshSteeringAgentコンポーネントが必要です。どちらもこのコンポーネントより先にエンティティにSet()する必要があります。
エージェントは、優先度とマスク・レイヤーのフィルタリングを使用して、他の移動エージェント(HRVO)を回避するための計算を実行します。NavMeshAgentConfigから優先度・マスク・レイヤーの初期値が設定されますが、実行時にコンポーネント上で変更可能です。
詳細は「エージェントの回避」セクションをご覧ください。
ナビメッシュエージェントのコールバック
すべてのエージェントのコールバックはメインスレッドから呼び出されるため、他のコンポーネントやエンティティへの読み書き時にマルチスレッドの問題は発生しません。
ナビゲーションエージェントのコールバックは、SimulationConfig.Navigation.EnableNavigationCallbacksから有効化する必要があります。
以下のシグナルは、エージェントを厳密に制御するための迅速なフィードバックを提供します。
C#
namespace Quantum {
public unsafe partial class NavMeshAgentTestSystem : SystemMainThread,
ISignalOnNavMeshSearchFailed,
ISignalOnNavMeshWaypointReached,
ISignalOnNavMeshMoveAgent {
}
}
| ISignalOnNavMeshSearchFailed | エージェントが自身の位置とSetTarget()で設定されたターゲット間に経路を作成できなかった(例:ターゲットがナビメッシュから離れすぎている)際に呼び出されるシグナルです。
このコールバック内で SetTarget()を呼び出した場合は、resetAgentパラメーターをfalseに設定する必要があります。 |
| ISignalOnNavMeshWaypointReached | エージェントがターゲットへの経路上のウェイポイントに到達した際に呼び出されるシグナルです。
WaypointFlagsは、ウェイポイントの追加情報を保持します。
Target - ウェイポイントがターゲットLinkStart - ウェイポイントがオフメッシュリンクの始点LinkEnd - ウェイポイントがオフメッシュリンクの終点RepathWhenReached - エージェントはウェイポイントに到達した際に経路の再計算を実行する |
| ISignalOnNavMeshMoveAgent | NavMeshAgentConfig.MovementTypeがCallbackに設定されていて、NavMeshSteeringAgentコンポーネントを持つエージェントにターゲットが設定されている場合に呼び出されるシグナルです。
desiredDirectionパラメーターは、エージェント内部のステアリングと回避によって割り出される、エージェント移動ベクトルの正規化された方向です。 |
移動するエージェントのコールバック実装例で、例えばKCCの入力としても使用できます。
C#
public void OnNavMeshMoveAgent(Frame frame, EntityRef entity, FPVector2 desiredDirection) {
var agent = frame.Unsafe.GetPointer<NavMeshSteeringAgent>(entity);
// エージェントを移動する単純な例
if (frame.Has<Transform2D>(entity)) {
var transform = frame.Unsafe.GetPointer<Transform2D>(entity);
transform->Position.X.RawValue = transform->Position.X.RawValue + ((desiredDirection.X.RawValue * frame.DeltaTime.RawValue) >> FPLut.PRECISION);
transform->Position.Y.RawValue = transform->Position.Y.RawValue + ((desiredDirection.Y.RawValue * frame.DeltaTime.RawValue) >> FPLut.PRECISION);
transform->Rotation = FPVector2.RadiansSignedSkipNormalize(FPVector2.Up, desiredDirection);
} else if (frame.Has<Transform3D>(entity)) {
var transform = frame.Unsafe.GetPointer<Transform3D>(entity);
transform->Position.X.RawValue = transform->Position.X.RawValue + ((desiredDirection.X.RawValue * frame.DeltaTime.RawValue) >> FPLut.PRECISION);
transform->Position.Z.RawValue = transform->Position.Z.RawValue + ((desiredDirection.Y.RawValue * frame.DeltaTime.RawValue) >> FPLut.PRECISION);
var desiredRotation = FPVector2.RadiansSignedSkipNormalize(FPVector2.Up, desiredDirection);
transform->Rotation = FPQuaternion.AngleAxis(desiredRotation * FP.Rad2Deg, -FPVector3.Up);
}
}
Back to top