ナビゲーションシステム
はじめに
Quantumナビゲーションシステムは、予測ナビゲーションメッシュとゲームのワールドの状況を判断して進んでいくエージェントを提供します。以下の2つのパーツで構成されています。
ナビゲーションメッシュ は、エージェントが 歩く ことのできる三角形で構成されたデータ構造です。
- メッシュは手動で書くことも編集することもでき、またUnityのNavMeshから変換することもできます。
- 3Dデータをエクスポート済みでも、高さの概念はまだ認識されていません。
- マップごとに複数のナビゲーションメッシュを使用できます。
- NavMeshは標準のA*パスファインディングアルゴリズムを搭載しています。
- メッシュは、動的に切り替え可能な領域を含めることができます(移動できないUnity navmeshの障害物のように)。
ナビゲーションメッシュエージェント は、ナビゲーションメッシュを通してエンティティを進行させるQuantumコンポーネントです。
- ターゲットを受信した後、エージェントはレベルの中を自立して移動します。
- エージェントは優先順位に従って回避システムを使用します。
- エージェントは動体となるようにマークされるため、壁を突き破ったりはできません。
ヒント
ナビゲーションシステム機能はアクションRPGサンプルで使用されています。 ActionRPGのサンプルドキュメントからダウンロードしてください。
ワークフロー
マップデータにMapNavMeshDefinition
スクリプトを追加するか、またはマップデータの下にGameObjectを作成し、そこにNavMeshDefinitionスクリプトを添付します。
ここにQuantum navmeshの中間データがあります。このデータは、後でNavMesh Quantumアセットにベイクされます。
ここからUnity NavMeshをインポートすること(A)も、手動でQuantumナビゲーションシステムを作成および編集すること(B)も可能です。

MapNavMeshDefinition
GameObjectが選択されると、Quantum navmeshがシーンに描画されます。
Toggle Always Draw
はGameObjectが選択されていない場合でも、navmeshを描画します。Toggle Draw Mesh
は三角形を輪郭どおりに描画します。Toggle Optimized Gizmos
はデバッグレンダリングを最適化しますが、手動編集モードとの互換性はありません。Export Test Mesh
はnavmeshをファイルにエクスポートします。

Quantum Navmeshの作成
A) Unity NavMeshのインポート
Unityのドキュメントに従ってUnity NavMeshを作成およびベイクします。(Off Meshリンク、Height Mesh、weightはサポートされていません。)
または、 NavMeshComponents アドオン(Unity Githubページからダウンロード可能)を使用します。 次に、コンポーネントを
NavMeshSurfaces
リストにリンクします。インスペクターで Unityからインポート ボタンを押します。
Weld Indentical Vertices
は同一の頂点を組み合わせます。 常に有効にしてください。Weld Vertices Epslion
はイプシロンを微調整して頂点を溶接します。Fix Triangles On Edges
は頂点が他の三角形のエッジ上にある三角形を修正します。誤った境界の検出を防ぐためです。Import Regions
によってQuantum navmeshリージョンのインポートが可能になります。リージョンがない場合は、これをオフにします。Region Detection Margin
はUnity NavMeshがソースメッシュサイズにあまり適合しないため、必要です。値がnavmeshエリアに追加され、すべてのQuantum Regionスクリプトに対して確認され、正しいリージョンIDが選択されます。リージョンが検出されない場合、この値を大きくします。Convert Unity Areas To Quantum Region
リージョンのワークフローを参照してください。Agent Radius
はUnity navmeshの生成に使用される最小エージェント半径を抽出することにより、インポート中に上書きされます。 エージェントのより正確なウェイポイントを計算するために使用されます。
1.2.4以前の既知の問題
Advanced Options を展開して Navmeshの検証 を実行し、頂点の距離の組み合わせ を調整します。UnityNavMeshをインポートする際、互いに非常に近くにある頂点は結合されます。まれに、正しい結果を得るためにデルタを拡大する必要があります。Validate Navmesh を実行するとわかります。
- UnityのNavMeshフォーマットは弊社のものとは異なります。例えば、メッシュがチャンクに分かれていて、実際に必要な数よりも多くの三角形が作成されてしまい、さらに三角形は頂点を共有していません。安全にボーダーを検出するため、これを適切なメッシュに変換する必要があります。まさにこの2つの事実が、適切なボーダーを検出することでQuantumでのNavMeshベイキングを時折失敗させる要因になります。このような時は、エージェントが明らかに空っぽの道におかしい選択をするのでわかります。この現象は、Unityの NavMeshComponents (UnityのGithubページからダウンロード可能)を使用し、NavMeshSurfaceインスペクター上で別のタイル設定を適用(以下を参照してください。)することで軽減できます。

B) ナビゲーションメッシュを手動で編集
- Start Editing NavMesh を押すとNavMeshの編集をするためのマウスクリックを制限しようとします。他のゲームオブジェクトを再度選択できるようにするには Stop Editing NavMesh をクリックします。
- Add Vertex を3回おこなって新しいメッシュの作成を始めます。新しい3つの頂点は (0,0,0)で作成されます。
- シーン内で頂点を1つ選択しUnityの移動ツールを使用してトランスレートします。
- 3つの頂点全てを別々に動かした後、shift を押しながら頂点をクリックして、3つ 全てを選択し、Create Triangle をクリックするか、T キーを押します。後でパスファインディングの際に、三角形だけ使用します。
- ちなみに、編集の全ステップはすべて元に戻すことができます。
- 今度は 2つ の頂点を選択し、 Insert Vertex + Create Triangle を使用するかショートカット T を押してメッシュを取り除きます。
- 頂点を複数選択すると、それらをグループとして移動させることができます。
- X キーを押すと、頂点の選択を拡大できます。
- 頂点を選択し Delete Vertices を押すか、ショートカット Backspace を押して削除します。
- 任意の数の頂点を選択し、Duplicate And Flip を使用してそれらをミラーリングします (0, 0, 0).

既知の問題
- 頂点ハンドルのgizmoがシーン内に描かれないこともあります。Unityレイアウトをリセット(Unityエディターの右上の端、Layoutボタンの下)してください。
C) あらゆるメッシュをMapNavMeshDefinitionとして使用
MapNavMeshDefinition
クラスもしくはMapNavMeshDefinitionEditor.ImportFromUnity()
でデータがどのように入力されているか確認します。例として、MeshFilterから情報を入力すると非常に簡単です。
C#
public class MapNavMeshDefinition : MonoBehaviour {
public MapNavMeshVertex[] Vertices;
public MapNavMeshTriangle[] Triangles;
}
Quantum NavMeshをベイクする
Unity navmeshをインポートするか、Quantum navmesh定義を手動で編集した後、navmeshをQuantumアセットにベイクする必要があります。
- グリッドがNavMesh全体を覆っていることを確認してください。
- Bake Nav Meshes を押します。 この手順では、生のナビゲーションメッシュデータをQuantum NavMesh形式に変換します。境界線のエッジが生成され、三角形と境界線がグリッド構造に挿入されて高速にアクセスされ、グリッドセルごとに最も近い三角形のルックアップテーブルが作成されます。
- 追加のファイルが作成され、MapData(
NavMeshLinks
)に添付されます。ファイルを選択すると、それが最終的なNavMesh.AssetObjectであることに気付くでしょうが、バイナリデータの大部分はなく、Data Filepath
フィールドで指定されたバイナリファイルに保存されます。理由は、ScriptableObjectsのサイズ制限です。 追加のファイルは、NavMesh.AssetObjectのLoaded
-コールバックのUnityDB.Init()
で読み込まれます。

ヒント
Quantumマップのベイク処理にカスタムスクリプトを挿入するのは簡単です。OnBeforeBake-callbackにチェーンすることにより、上記のすべてのステップを自動的に実行できます。 コミュニティWebサイトの Auto Baking Map Dataページをご覧ください。
Quantum NavMeshエージェントのセットアップ
SystemSetupでNavMeshAgentSystemを有効にします:
C#
public static class SystemSetup {
public static SystemBase[] CreateSystems(RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
return new SystemBase[] {
// pre-defined core systems
new Core.PhysicsSystemPre(),
new Core.NavMeshAgentSystem(),
// ..
};
}
}
Quantum DSLのエンティティにNavMeshAgentコンポーネントを追加します(たとえば、メインキャラクターに)。Transform2Dは必須で、DynamicBodyは任意です。
entity Character[PLAYER_COUNT] {
use Transform2D;
use DynamicBody;
use Prefab;
use NavMeshAgent;
fields {
player_ref Player;
}
}
エンティティを作成する際に、目的のAgentConfigを設定:
C#
c->NavMeshAgent.AgentConfig = DB.FindAsset<NavMeshAgentConfig>("MyAgentConfig");
コンポーネントに目的のターゲットを設定し、渡されたNavMeshを使用してそれに向かって移動:
C#
var nm = f.Map.NavMeshes["NavMesh"];
var i = f.GetPlayerInput(c->Player);
c->NavMeshAgent.SetTarget(i->Data.Target, nm, false);
ヒント
エージェントが「UsePhysics」に設定されている場合、エージェントの物理マテリアルでFreezeRotationをtrueに設定するようにしてください。こうすると、オーバーステアやその他エージェントのステアリングで起きる回転に関する問題を防ぐことができます。
エージェントゲームオブジェクトにアニメーションを設定するには、次のような処理を行うスクリプトをプレハブに添付します。 Speed変数を使用するアニメーションコントローラーを作成してください。
C#
public class CharacterAnimation : MonoBehaviour {
public float AnimSpeedFactor = 0.5f;
private Animator anim;
private EntityPrefabRoot root;
void Start() {
anim = GetComponentInChildren<Animator>();
root = GetComponent<EntityPrefabRoot>();
}
unsafe void Update() {
if (QuantumRunner.Default != null) {
var entity = QuantumRunner.Default.Game.Frames.Current.GetEntity(root.EntityRef);
var body = Quantum.Entity.GetDynamicBody(entity);
float speed = body->Velocity.Magnitude.AsFloat;
anim.SetFloat("Speed", speed);
if (speed > 0.1f)
anim.speed = speed * AnimSpeedFactor;
else
anim.speed = 1;
}
}
}
Navmeshリージョンのセットアップ
Quantum 1.2.4以降
Navmeshリージョンは、実行中に確定的にアクティブ化およびインアクティブ化できるnavmeshの事前定義された部分です。ダイナミックカービングテクニックと比べるとリージョンのパフォーマンスオーバーヘッドはわずかです。
Unity NavMeshエリアは、Quantumリージョンが存在するはずの三角形を生成するために利用されます。追加のゲームオブジェクト(MeshRenderを使用)は、リージョンを生成する位置に配置する必要があります。
NavMeshSurfacesを使用する場合も同様です。 さらに、NavmeshModifierスクリプトを追加のゲームオブジェクトに追加します。
リージョンの最大数は64です。同じリージョンIDを様々な場所で使用することができます。限界に近づいた場合には、リージョンIDを再利用してください。
デフォルトのQuantum navmeshはリージョンではないため、切り替えることはできません。
- 1つのUnity NavMeshエリアを予約して、「QuantumRegion」として指定します。通常、必要なのは1つだけです。リージョンが正確に隣り合っている場合(エッジを共有している場合)、異なるエリアIDでセットアップする必要があります。

- 手順1)で作成したUnity NavMesh Areasを、Quantum NavMeshインスペクターの
Convert Unity Areas To Quantum Regions
に追加します:

- MeshRenderer があり、Quantum RegionをNavMeshに投影するシーンのGameObjectsに
MapNavMeshRegion
スクリプトを追加します。
手順1)からUnity NavMeshエリアを選択し、Navigation Staticがオンに切り替えられていることを確認し、リージョンにId
(シミュレーションで参照できるリージョンとして)を指定します。

- Unity NavMeshをベイクします
生成された三角形は、ソースメッシュレンダラーと完全には一致しません。Region Detection Margin
は、リージョンを識別するためにメッシュレンダラーのバウンディングボックスに対して結果の三角形を確認する際、マージンを追加するために使用されます。QuantumEditorSettings.DrawNavMeshRegionIds
を切り替えてデバッグします。
UnityからQuantum NavMeshをインポートする
NavMeshをベイクする(マップインスペクター上)
コード内のQuantumリージョンの切り替え
C#
var nm = f.Map.NavMeshes["NavMeshUnity"];
var isActive = nm.IsRegionActive(f, "Door_002");
// Toggle by name
nm.SetRegionActive(f, "Door_002", !isActive);
// Get region index from region name
byte GetNavMeshIndex(Frame f, NavMesh navMesh, string name)
{
NavMeshRegion navMeshRegion;
if (navMesh.RegionMap.TryGetValue(name, out navMeshRegion))
{
return navMeshRegion.Index;
}
return byte.MaxValue;
}
// Toggle region on off by its index (starting by 0)
nm.SetRegionActive(f, regionIndex, !nm.IsRegionActive(f, flags));
既知の問題
新しいマップを読み込む際は、
Frame.ClearAllNavMeshRegions()
を呼び出す必要があります。リージョンを有効または無効にするとき、すべてのエージェントはパスを変更する必要があります:
NavMeshAgent.ForceRepath()
(1.2.4 B3以降) Quantumナビメッシュは、QUANTUM_XYもアクティブな場合に内部的にXZ面表示も使用します。ベイキングの際に、XY面でNavMeshSurfaceによって生成されたUnityナビメッシュははずされます。
(PathQuality Bestを使用して1.2.4 B3で修正)余分な三角形(有効なリージョンの近く)の導入により、ヒューリスティックは視覚的に正しくない初期ウェイポイントを選択することがあります。また、アクティブなリージョンに向かって若干曲がっているパスを作成することがあります。
(1.2.4 B3で修正)リージョンはまだ法線を生成しません。最大のユニットが視覚的に周辺を移動することができるように、リージョンを十分に余裕のある大きさにしてください。
実行時のデバッグ
EditorConfigSettingsに移動すると、Unityエディターで実行時にNavMeshと追加情報を描画するためのオプションが追加されます。

Draw Nav Mesh
はNavMesh Unityのように、さらにリージョンをレンダリングします。 無効なリージョンはグレー表示されます。Draw Nav Mesh Borders
では、最後にNavMeshラインキャストに使用される境界線を視覚化できます。リージョンがオフに切り替わると、リージョンの境界線(赤色)が有効になります。Draw Pathfinder Funnel
は最新の情報でのみ機能するため、エージェントが1つしかない場合にのみ実際に使用できます。 コードはUnity内にあり、簡単に拡張できます。

Quantumエンティティ状態インスペクターでもエージェントの状態をデバッグできます。

API
NavMeshAgentConfig

Radius
エージェントの半径です。Speed
ユニット内のエージェントの秒速です。Angular Speed
はエージェントの回転速度に影響します。0 に設定すると、エージェントは回転しません。高い値 に設定すると、回転が瞬時になります。Acceleration
は加速時のエージェントの速さに影響します。0 に設定すると、加速は行われません。Epsilon
は目的に到達するまでの距離のしきい値です。Loaded()の際にNNavMeshAgentConfig.MinEpsilon
にクランプされます。Max Repath Timeout
秒単位。この間隔では再パスが強制されます。Path Quality
経路探索経験則の品質を選択します。Fast
はA経験則に対してマンハッタン距離を使用し、Aノード候補に三角形の中心を使用します。Good
はA*ノード候補を作成する場合、三角形の角を計算に入れますBest
は、Aノード位置に可能な場合には、A経験則に対しユークリッド距離を使用し、(1.2.4 RC1以降では)Atriangle vs. scan line intersectionを使用します。リージョンを使用している場合は、このオプションを推奨します!
Use Physics
これを切り替えて、直接translationを設定する代わりにエージェントが動体(DynamicBody
コンポーネントが必要です。)に速度を設定するようにします。Dynamic Line Of Sight
エージェントがNavMesh.LineOfSight()
チェックを使用してウェイポイントを確認できる場合、ここを切り替えて スキップ するようにします。Dynamic Line Of Sight Waypoint Range
上記と同じですが、ウェイポイントまでの範囲内にある場合にのみ有効になります。無効にするには0に設定します。FindValidTargetCellRange
SetTarget()の位置がnavmesh 上に ない* 場合、それは修正され、navmesh 上の 有効な位置に移動します。デフォルトでは、これは視覚的に不正確な場合があり、この検索を有効にすることで結果の品質を上げることができます(値> 0)。位置のセルおよび各方向のX-1隣接セルは、navmesh上の 最も近い位置を検索します。検索は非常に複雑になる可能性があるため、この値を高く設定しすぎないでください。- 既知の問題
Show Debug
エージェントの動作と経路探索のgizmoを描く:ピンクの小さな点がターゲットを示し、黄色の大きな点が現在のウェイポイントです。回避レガシー
Allow Flock Behavior
移動方向が近接距離にいる他のエージェントに影響されます。Priority
優先度が高い((高い( 値の)エージェントは、回避を実行するときに他のエージェントの影響を受けません。
Avoidance Experimental (Quantum 1.2.4以降)
Priority
優先度の高い(高い 値の)エージェントは、優先度の低いエージェントと回避率を25%から75%に分割します。Avoidance Quality
None: 回避はオフ、Low: レガシー回避、Medium: 最小候補、Good: 最高の費用対効果、High = 実験的Max Avoidance Candidates
相互に影響を与えるエージェントの品質と数に応じて、スムーズな回避を維持するにはこの値を増やす必要があります。Clamp Agent To Navmesh
を使用して、エージェントが積極的に回避してnavmesh内に留まるようにします。Clamp Agent Radius Threshold
最適化されたクランプ計算を使用するエージェントの最小半径です(NavMesh.MinAgentRadiusを減算)。小さなエージェントがnavmeshの外に出る場合、これを増やすことでエージェントを含みます。Clamp Agent Correction
は、各ティックに適用される修正の割合です。 これを増やすと、浸透がさらに少なくなります。Reduce Avoidance At Waypoints
これを有効にして、ウェイポイントに近づくときのエージェントの回避を減らします。移動エージェントがエージェントがnavmeshから外れるのを緩和している場合にのみ使用します。これに代わるものはまだ開発中です。物理的なボディを使用してウェイポイントで立ち往生しているエージェントを緩和する場合は、Dynamic Line Of Sight Waypoint Range
を設定してみてください。Reduce Avoidance Factor
これはエージェントの半径と乗算され、回避の影響が二次的に減少する距離を表します。
ブレイク
BreakingDistance
0に設定した場合(デフォルトおよび推奨)、エージェントは現在の速度を基準として使用してブレイクを開始します。 距離をオーバーライドするには、この値を0より上に設定します。BreakingOnWaypoint
有効にすると、エージェントはウェイポイントの範囲に到達したときにブレイクを適用します。BreakingOnWaypointFactor
ウェイポイントに適用されるブレイクの量を減らします。BreakingOnWaypointMinAngleRad
ウェイポイントでのブレイクを適用する必要がある場合の最小角度を設定します。
SimluationConfig.NavMeshAgent

Proximity Factor
(Quantum 1.2.3以前) 近接と見なされるエージェント間の距離。2つのエージェントを組み合わせた半径で乗算されます。衝突回避ベクトルの計算中に近隣エージェントを照会するための最大距離として使用されます。大きいほど、より正確に回避できますが、より多くのCPUを使用します。Avoidance Range
(Quantum 1.2.4以降) 2つのエージェント間の距離から、お互いに影響を及ぼし始めたときの半径を引いたもの。Update Interval
は、アップデート動作を参照します。(1 = 1ティックごと、2 = 2ティックごと)Default Nav Mesh Agent
この設定は、特定の設定に割り当てられていない全てのエージェントに使用されます。Use Legacy Avoidance
(since Quantum 1.2.4以降) これを無効にすると、新しい回避システムが有効になります。
NavMeshAgent
C#
public struct NavMeshAgent {
// Agent linear velocity
FPVector2 CurrentVelocity;
// Target position that has be set
FPVector2 Target { get; }
// Target position that may have been corrected
FPVector2 InternalTarget { get; }
// Is the agent working
bool IsActive { get; set; }
// Set the config also when it is running
NavMeshAgentConfig AgentConfig { get; set; }
// Access how many waypoints the agent currently uses (max 4)
int MaxWaypoints { get; }
// Access what waypoint the agents currently steers towards (-1 is inactive)
int CurrentWaypoint { get; }
// Use this to initilizes the agent and set the config
void Init(NavMeshAgentConfig agentConfig = null);
// See below
void SetTarget(FPVector2 target, NavMesh navMesh, bool resetSpeed);
// Triggers a repath next tick (internally sets CurrentWaypoint to -1)
void ForceRepath();
// Access agent waypoint positions (throws when index > MaxWaypoints)
FPVector2 GetWaypoint(int index)
}
SetTarget(target, navMesh, resetSpeed)
ナビゲーションエージェントの目的のターゲットを設定します。
target
2Dにおけるエージェントのターゲットです。navMesh
ナビゲーションに用いられるNavMeshです。resetSpeed
再ターゲット化する際は現在のエ-ジェントの速度はリセットされます。
NavMesh
NavMeshは、マップのディクショナリ(NavMeshAsset.Nameをキーとして使用)に保管されます。以下に従ってアクセスしてください。
C#
Frame.Map.NavMeshes["NavMeshName"];
C#
public class NavMesh : AssetObject {
// The path finder object
NavMeshPathFinder PathFinder;
// Makes sure that the position is on the map grid.
void ClampToGrid(ref FPVector2 position);
// Will return true is the a triangle can be found in the NavMesh at this position (uses FindTriangle internally).
// If inclusive is false, positions what lie exactly on the triangle edge will return false.
bool Contains(FPVector2 position, INavMeshRegionMask areaMask, Boolean inclusive = false);
// Same as the above. The y-component of the 3D position is set to zero.
bool Contains(FPVector3 position, INavMeshRegionMask areaMask, Boolean inclusive = false);
// Will return true if the position is inside the map grid. Sets the out parameters to the closest triangle vertices.
bool FindClosestTriangle(FPVector2 position, out FPVector2 v0, out FPVector2 v1, out FPVector2 v2, INavMeshRegionMask regionMask)
// Will return a triangle index >= 0 when the position could be moved to inside the navmesh (closestPosition).
// The position is tried to be moved minDistanceToBorder units into the navmesh.
// Cell arround the position are checked when maxCellsChecked < 0.
Int32 FindClosestTriangle(FPVector2 position, FP minDistanceToBorder, Int32 maxCellsChecked, INavMeshRegionMask regionMask, out FPVector2 closestPosition)
// Returns true is the two positions are in line of sight of each other on the NavMesh.
bool LineOfSight(FPVector2 p0, FPVector2 p1, INavMeshRegionMask regionMask);
bool LineOfSight(FPVector2 p0, FPVector2 p1, INavMeshRegionMask regionMask, ref FPVector2 hit);
// Same as the above. The y-component of the 3D position is set to zero.
bool LineOfSight(FPVector3 p0, FPVector3 p1, INavMeshRegionMask regionMask);
bool LineOfSight(FPVector3 p0, FPVector3 p1, INavMeshRegionMask regionMask, ref FPVector3 hit);
// Use this to convert region ids to cache its index
Dictionary<string, NavMeshRegion> RegionMap { get; }
// Check is region is active
bool IsRegionActive(INavMeshRegionMask f, byte index);
bool IsRegionActive(INavMeshRegionMask f, string id);
bool IsRegionActive(INavMeshRegionMask f, ulong flags);
// Toggle a region
void SetRegionActive(INavMeshRegionMask f, ulong flags, bool isActive);
void SetRegionActive(INavMeshRegionMask f, byte index, bool isActive);
void SetRegionActive(INavMeshRegionMask f, string id, bool isActive);
}
LineOfSight(p0, p1, regionMask, ref hit)
Frame
クラスは INavMeshRegionMaskインターフェースを実装します。これは、有効なリージョンを確認するために必要です。- 任意で、p0に最も近いヒット位置を生成します。
NavMeshPathFinder
全てのナビゲーションメッシュにインスタンス化したパスファインダーがあります。NavMesh.PathFinder経由でアクセス可能です。
C#
public class NavMeshPathFinder : IDisposable {
// Funneled path
int PathSize { get; }
NavMeshPathVertex[] Path { get; }
// Pathfinder raw path
int RawPathSize { get; }
NavMeshPathVertex[] RawPath { get; }
// Find a path from start to end (check PathSize > 1 for progress)
FPVector2 FindPath(FPVector2 start, FPVector2 end, FP epsilon, Int32 findValidTargetCellRange, INavMeshRegionMask regionMask, PathQuality quality = PathQuality.Good, NavMesh navmesh = null);
// Same as the above. The y-component of the 3D position is set to zero.
FPVector3 FindPath(FPVector3 start, FPVector3 end, FP epsilon, Int32 findValidTargetCellRange, INavMeshRegionMask regionMask, PathQuality quality = PathQuality.Good, NavMesh navmesh = null);
// Sets the navmesh the path finder operates on.
void SetNavMesh(NavMesh navmesh);
}
FindPath(start, end, epsilon, findValidTargetCellRange, regionMask, quality, navmesh)
navmeshの外側にある場合、正しい終了位置を返します。
epsilon
ターゲットをnavmeshの境界からnavmesh内に移動するためのオフセット。0より大きいはずです。エージェントは、半径からNavmesh.MinAgentRadiusを引いた値を使用します。findValidTargetCellRange
0より大きい場合:位置がオフメッシュの場合、周囲のセルの境界交差が確認されます。regionMask
FrameクラスはINavMeshRegionMaskインターフェースを実装します。これは、有効なリージョンを確認するために必要です。quality
PathQuality
は、ファネルアルゴリズムがヒューリスティックを使用する範囲を定義します。navmesh
オプションで、navmeshを渡します。内部的にSetNavMesh()が呼び出されます。
NavMeshコールバック
ISignalOnNavMeshTargetReached
は、エージェントがNavMeshAgentConfigAsset上で Epsilon パラメータを使用して目標のターゲットに到達したとき呼び出されます。
ISignalOnNavMeshUpdateSteering
(Quantum 1.2.3以降)内部の進行システムを上書きするのに使用します。以下の例を参照してください。
これは全てのエージェントに対し一度呼ばれます。ここでブランチを切るとエージェントごとに別のロジックを作成できます。SimulationConfig内でNavMeshAgent-UpdateIntervalを設定すると、このコールバックが、内部バージョンと同様に各エージェントに対し低頻度で呼ばれます。
NavMeshAgent.SetTarget()を呼び出せば、ここからパスファインダーをリセットできます。強制的に再パスを行う場合は、NavMeshAgent.ForceRepath()を呼び出します。
下記のサンプルでの操作(steering)と回避は、次の設定でうまく実行できます:
-
- エージェントには動的または運動学的コライダーがあります
- AgentConfig.UsePhysics = True
- SimulationConfig.ProximityFactor = 2 および UpdateInterval = 1
- PhysicsMaterial.Drag = 4
C#
public struct NavMeshAgentSteeringData {
FPVector3 Waypoint; // Current waypoint
Boolean IsTarget; // Is the waypoint the final target
FP BreakingFactor; // Internally calculated breaking factor
FPVector3 AvoidanceVector; // Accumulated avoidance forces
bool IgnoreInternalSteering; // Set this to true to deactivate the internal steering
}
C#
public unsafe class MyNavSystem : SystemBase, ISignalOnNavMeshUpdateSteering {
public void OnNavMeshUpdateSteering(Frame f, NavMeshAgent* agent, Entity* entity, NavMeshAgentSteeringData* data) {
// This is important as it will disable the internal steering. Otherwise your changes will be overwritten.
data->IgnoreInternalSteering = true;
var transform = Entity.GetTransform2D(entity);
var direction = (data->Waypoint.XZ - transform->Position).Normalized;
// Add the avoidance vector into our desired moving direction
// Use the dot product with the agents right vector to only take lateral avoidance into account.
var avoidance = data->AvoidanceVector.XZ;
var localRight = FPVector2.Rotate(FPVector2.Right, transform->Rotation);
var dot = FPVector2.Dot(localRight, avoidance);
direction += localRight * dot;
// agent->CurrentVelocity is used to apply velocity to the agent.
// You can also set the velocity on the dynamic body directly, but then you have to disable UsePhysics on the NavMeshAgentConfigAsset.
agent->CurrentVelocity = direction.Normalized * agent->AgentConfig.Speed;
// Set the direction that the actor is facing or set it on the dynamic body directly
// The dynamic body might still be rotating by itself after steering because it received angular rotation velocity due to physics contacts.
// Reset it manually or switch rotations off completely.
transform->Rotation = FPMath.Lerp(transform->Rotation, FPVector2.RadiansSigned(FPVector2.Up, direction), f.DeltaTime * agent->AgentConfig.AngularSpeed);
}
}
OnNavMeshUpdateAvoidance
(Quantum 1.2.3以降) 内部回避ベクトル計算を上書きします。下記のサンプルを参照してください。
このコールバックは、SimulationConfig.ProximityFactor値域に定義された同じセルの中にあるエージェントのペアごとに一度呼ばれます。
C#
public struct NavMeshAgentAvoidanceData
{
FPVector3 AvoidanceVectorA;
FPVector3 AvoidanceVectorB;
Boolean IgnoreInternalAvoidance;
}
C#
public unsafe class MyNavSystem : SystemBase, ISignalOnNavMeshTargetReached, ISignalOnNavMeshUpdateAvoidance {
public void OnNavMeshUpdateAvoidance(Frame f, NavMeshAgent* agentA, Entity* entityA, NavMeshAgent* agentB, Entity* entityB, NavMeshAgentAvoidanceData* data) {
// This will deactivate the internal avoidance calculation that would happen afterwards.
// You can still add avoidance here and let the internal avoidance add even more.
data->IgnoreInternalAvoidance = true;
// This is a very simliplied approach to calculate an avoidance vector for this particular pair.
// AvoidanceVectorA set here for example will be added to the the accumulated NavMeshAgentSteeringData.AvoidanceVector in the steering callback.
// Additional features could be taking the agents looking direction or agent->CurrentVelocity into account.
// Or using a cross vector to improve the evasion actors facing each other.
// Or adding agent priority, ...
var posA = Entity.GetTransform2D(entityA)->Position;
var posB = Entity.GetTransform2D(entityB)->Position;
var direction = posB - posA;
var dist = direction.Magnitude;
direction = direction.Normalized / dist;
data->AvoidanceVectorA = -direction.XOY;
data->AvoidanceVectorB = direction.XOY;
}
}
Back to top