Creating Navmesh Agents
導航網格代理功能分為多個組件。
NavMeshPathfinder
:執行多執行緒路徑尋找,存儲路點和路點進度NavMeshSteeringAgent
:執行代理導向計算,包括加速度、速度、旋轉速度和制動NavMeshAvoidanceAgent
:允許代理相互避讓NavMeshAvoidanceObstacle
:使實體可被NavMeshAvoidanceAgent
避讓
代理組件可與Transform2D
或Transform3D
組件配合使用。
所有組件共用一個配置NavMeshAgentConfig
資產。
它們可以組合用於不同的使用場景。
完整功能 | Pathfinder, SteeringAgent和AvoidanceAgent | 具備所有三個組件的實體可以尋找路徑、沿路徑移動並動態避讓其他代理。 |
無避讓功能 | Pathfinder和SteeringAgent | 不會執行任何避讓程式碼,且組件不存儲任何與避讓相關的數據。
可關閉 SimulationConfig.Navigation.EnableAvoidance 以節省更多 CPU 資源。 |
自定義移動 | Pathfinder和SteeringAgent | 代理可以尋找並存儲路徑,但通過在自定義系統中實現移動信號ISignalOnNavMeshMoveAgent 可禁用導向代理(注意:僅當代理實際有目標時才會執行回調)。
NavMeshAgentConfig.MovementType 需設為Callback 。
必須啟用 SimulationConfig.Navigation.EnableNavigationCallbacks 。 |
帶避讓的自定義移動 | Pathfinder, SteeringAgent和AvoidanceAgent | 除了自定義移動 的要求外,ISignalOnNavMeshMoveAgent 的desiredDirection 參數包含經避讓調整後的移動方向。 |
僅路徑尋找 | Pathfinder | 代理執行路徑尋找、路點存儲和進度管理。導向、避讓和移動由自定義系統處理。
需要 自定義移動 中的設置。
若無導向組件,路點進度需依賴代理接近路點的速度信息。必須每幀設置 NavMeshPathfinder.WaypointDetectionDistanceSqr 。 |
使用實體原型建立代理
本教程需要一個包含 Quantum 設置的 Unity 場景,包括已烘焙的 Quantum 導航網格(參見導入 Unity 導航網格)
- 在場景中創建 Quantum 原型:
GameObject > Quantum > Empty Entity
- 選擇
QuantumEntityPrototype
,將Transform
設為2D
- 選擇
QuantumEntityPrototype
並啟用NavMeshPathfinder
- 展開
NavMeshPathfinder.Initial Target
並設置世界Position
- 在
NavMesh
的Initial Target
中,選擇已烘焙的 Quantum 導航網格資產 - 可選擇分配
NavMeshAgentConfig
資產,否則使用默認配置
- 展開
- 選擇
QuantumEntityPrototype
並啟用NavMeshSteeringAgent
- 向GameObject添加 Unity 視圖:
right-click on the GameObject > 3D Object > Capsule
,並將膠囊體向上移動 1 單位 - 打開 Quantum Gizmos 菜單疊加層,開啟
NavMesh Pathfinder
- 按遊玩按鈕

在代碼中建立代理
本教程需要一個包含 Quantum 設置的 Unity 場景,包括已烘焙的 Quantum 導航網格(參見導入 Unity 導航網格)。
右鍵點擊文件夾QuantumUser > Simulation
,選擇Create > Quantum > System
創建新的 Quantum 系統。將腳本和類重命名為NavMeshAgentTestSystem
,打開NavMeshAgentTestSystem
資產,並將新系統添加到DefaultSystemsConfig.Entries
中。
打開 Quantum Gizmos 菜單疊加層,開啟NavMesh Pathfinder
,以便在進入運行模式時顯示代理 gizmos。為了讓 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();
// Add a transform 3d component or 2d component
frame.Set(entity, new Transform3D()
{
Position = FPVector3.Zero,
Rotation = FPQuaternion.Identity
});
// Create the pathfinder component using the factory method, optionally pass a NavMeshAgentConfig
var pathfinder = NavMeshPathfinder.Create(frame, entity, null);
// Find the navmesh by name and set a target before adding the component
var navmesh = frame.Map.NavMeshes["Navmesh"];
pathfinder.SetTarget(frame, new FPVector3(12, 0, 0), navmesh);
// Add the pathfinder and steering components to the entity
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 = 每隔一幀更新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 標記的是目標。修正半徑會找到導航網格上最近的有效位置,修正內部目標,同時保持輸入目標 不變。
對於 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 | 代理移動可直接應用於變換,或通過執行移動回調驅動自定義移動邏輯。
None - 不應用移動Transform - 移動應用於同一實體上的Transform2D 或Transform3D 組件Callback - 不應用移動,但執行ISignalOnNavMeshMoveAgent 信號 |
VerticalPositioning | 此選項僅在使用 3D 變換時有效。定義代理的 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);
// simple demonstration how to move the agent.
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