This document is about: QUANTUM 2
SWITCH TO

Creating Navmesh Agents

從Quantum 2.0開始,導航網格代理被分為多個元件。我們注意到使用導航網格及轉向的開發人員希望控制最終移動結果,這非常合理,因為它對於遊戲體驗而言經常是非常重要的。新的導航網格代理組件應有助於開發人員選擇導航網格支援的組合,而不會損失多重執行緒效能,也不會執行不必要的組件或浪費不必要的記憶體。

代理元件為NavMeshPathfinderNavMeshSteeringAgentNavMeshAvoidanceAgent。一個獨立的元件為NavMeshAvoidanceObstacle

可透過兩種方式建立代理實體:使用Unity中的實體原型或在程式碼中組譯實體。它們仍使用NavMeshAgentConfig Quantum資產。

以Unity中的實體原型建立代理

  • 透過Unity選單建立一個空的Quantum原型:GameObject/Quantum/Empty Entity
  • 選擇實體並設定Transform2D
  • 切換NavMeshPathfinder元件
    • 選擇預設NavMeshAgentConfig
    • 切換Initial Target並從Unity場景選擇一個轉換,以提供可到達的初始位置
    • 選擇已嵌入Quantum導航網格(請見導航網格工作流程)
  • NavMeshSteeringAgent上切換
  • 為了看見路徑Gizmos,有兩種方式:
    • 在預設NavMeshAgentConfig上啟用Show Debug Steering
    • QuantumEditorSettings中啟用導航網格Gizmo Draw Pathfinder Funnel
  • 按下遊玩
navmesh agent prototype

以程式碼中的元件建立代理

另一種方式是在程式碼中組譯代理實體。

一開始實體需要一個 Transform2DTransform3D 元件,並且新增一個 View 元件將使得它在場景中有一個已轉譯的預製件。

最重要的元件是 NavMeshPathfinder。它執行路徑尋找,儲存目標位置及一個使用者定義的導航點數量和偵測導航點前進。這個元件需要透過NavMeshPathfinder.Create()處理站方法來建立,該方法在一個NavMeshAgentConfig中傳送。

NavMeshSteeringAgent 元件是選擇性的,並且需要一個NavMeshPathfinder。它有最大速度、加速及旋轉速度變數,這些變數在運行階段可更改,並且它沿著路徑操控實體的方向。不使用這個元件的話,開發人員可更改MovementTypeCallback,並且插入他們自己的移動,同時有最新的迴避資料。停用旋轉速度及加速的方式是設定它們為0。

NavMeshAvoidanceAgent 需要NavMeshPathfinderNavMeshSteeringAgent元件,其必須是這個元件之前的實體上的Set()。這個代理執行迴避運算,已迴避其他移動中的實體(HRVO),這是透過使用優先順序,及遮罩及圖層篩選來完成。優先順序、遮罩及圖層一開始由NavMeshAgentConfig設定,在運行階段在元件上可更改。

如果您希望代理由一個物理主體來操控方向,舉例而言這可以防止代理穿透靜態碰撞,那麼實體需要一個 PhysicsCollider2D/3D 及一個 PhysicsBody2D。為了啟用這個,您需要在其NavMeshAgentConfig中設定MovementTypeDynamicBody

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);

    // find a random point to move to
    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());
}

啟用NavMesh代理Gizmos Draw Nav Mesh Agents以在場景視窗中啟用代理Gizmo繪製。

重要代理設定

路徑尋找器

NavMeshPathfinder.SetConfig() 可在元件建立時和運行階段時被執行。如果代理目前遵循一個路徑,而來自新設定的導航點計數不同,路徑將被重設。在實體的NavMeshSteeringAgentNavMeshAvoidanceAgent元件上的設定將自動更新,而且速度、加速、迴避優先順序、圖層及遮罩的值將被重設為設定的值。

NavMeshAgentConfig.MaxRepathTimeout 是在此時間(以秒為單位)內未到達一個導航點而觸發代理路徑尋找的該時間。這更像是一種防故障機制,可避免卡住的代理。將此值設定為0將停用。

NavMeshAgentConfig.LineOfSightFunneling 在使用位於主要導航網格的中央之中的導航網格區域時應啟用。舉例而言它的組建可被消除。區域引入的額外的三角形有時可在啟用區域附近導致有些奇怪的路徑。這個選項將移除區域附近的不必要的導航點。

NavMeshAgentConfig.DynamicLineOfSight 讓代理檢查在各個刷新時是否可以跳過導航點。這個選項耗用資源,但是將移除在其路徑上的任何不必要的導航點。

另一方面如果 NavMeshAgentConfig.DynamicLineOfSightWaypointRange 被設定,則只在靠近導航點(範圍)的各個刷新時執行視線檢查。這不需要啟用DynamicLineOfSight就能使用。

NavMeshAgentConfig.FindValidTargetCellRangeSetTarget()時,選擇一個導航網格外面的位置時提供協助。範圍參數將搜尋鄰近單元格,以最佳化導航網格中的目標的替換。

考量下圖中以黃色x所標記的目標。搜尋到的特定單元格範圍的單元格以數字標記。舉例而言,單元格範圍0將無法找到目的地。另一方面範圍1將找到在導航網格上的接近位置,如黃色虛線所示。請注意,將單元格範圍增加到不合理的高數量將對效能產生很大影響。

navmesh agent waypoint reached detection axis

使用 NavMeshAgentConfig.CachedWaypointCount 來設定在NavMeshPathfinder上快取的導航點數量。請記得,儲存更多非暫時性資料將減慢模擬。第一個儲存在快取中的導航點是代理在調用SetTarget()時有的目前的位置,並且用於強化到達導航點的偵測。當代理開始轉向最後一個導航點時,它將再次自動運行路徑尋找,以避免代理在計算一個幀時沒有一個有效的導航點。

當您注意到代理在到達導航點時有困難時(例如因為緩慢的旋轉速度或迴避),啟用到達導航點的偵測有助於 NavMeshAgentConfig.EnableWaypointDetection。隨後的參數Axis ExtendAxis Offset定義了到達導航點的偵測軸(黑線)。如果一個代理進入黃色區域,視為已經到達導航點。

navmesh agent waypoint reached detection axis
到達導航點的偵測軸

如果路徑尋找器元件沒有附帶的轉向元件,將使用 DefaultWaypointDetectionDistance 以執行到達導航點的偵測,並且應該設定為代理最大速度 * 差量時間。請見「有用的導航網格代理設定」章節,了解如何強化到達導航點的偵測。

轉向代理

NavMeshAgentConfig.StoppingDistanceAutoBraking 用於接近最終目標的代理。StoppingDistance是代理停在目的地之前的絕對距離,設定這個值有助於代理保持穩定及不會過衝。代理在剩餘距離少於代理目前每次刷新的移動距離時,總是會停止。

AutoBraking更多的是一種視覺功能,它可以在到達目的地之前減慢代理的速度,也可以用於穩定代理的停止行為。AutoBrakingDistance定義了代理開始減速的目的地周圍的半徑。內部使用平方根來平滑地剎車。

如果使用移動類型PhysicsBody的幾何不排斥導航網格代理,特別是在使用迴避的時候,那麼代理將移動到導航網格之外。為了完全地防止這種情況,並且讓代理沿著導航網格的邊界滑動,啟用 NavMeshAgentConfig.ClampAgentToNavmesh

代理可以潛在地有一個比導航網格的MinAgentRadius更大的半徑。Quantum透過移動代理導航網格到離邊界更遠的地方,來支援這個功能,但是這讓夾緊代理到導航網格變得更加複雜,而且參數 ClampAgentToNavmeshRadiusThreshold 有助於選擇應選擇的方法。當較小的代理傾向於移動到導航網格之外時,請增加半徑。

為了穩定校正,只移動代理的整個穿透深度的一個百分比(ClampAgentToNavmeshCorrection)。

更新間隔

為了效能最佳化的理由,各個個別的代理(設定)可被設定為 不在 每次模擬刷新時運行路徑尋找及迴避。設定NavMeshAgentConfig.UdpateInterval到一個高於1的值,以減少它獲得的更新數量。這將使代理回應更少,但也節省處理器時間。代理實體索引用於定義更新的確切刷新,這樣不是所有實體都在同一個刷新被更新。

公式是:

C#

updateAgent = entity.Index % agentConfig.UpdateInterval == f.Number % agentConfig.UpdateInterval
  • 1 = 每次刷新更新
  • 2 = 每隔一次刷新更新
  • 8 = 每隔八次刷新更新,等等

使用導航網格代理回調

來自代理的所有回調從主要執行緒被調用,並且在存取一個寫入其他元件及實體時不導致多重執行緒問題。

導航代理回調必須被選擇啟用。開啟您的模擬設定並切換Enable Navigation Callbacks

simulation config
在模擬設定中啟用導航代理回調

以下信號將提供立即的回饋,其可用於進一步控制代理。

C#

namespace Quantum {
  public unsafe partial class NavMeshAgentTestSystem : SystemMainThread,
                                              ISignalOnNavMeshSearchFailed,
                                              ISignalOnNavMeshWaypointReached,
                                              ISignalOnNavMeshMoveAgent {
    }
}

當代理不能在其目前位置及SetTarget()中設定的目標之間建立一個路徑時,調用 ISignalOnNavMeshSearchFailed。舉例而言,目的地不能被配對到導航網格。在這個回調時,當您運行SetTarget()時,設定resetAgent參數到偽。

當代理在其通往目標的路徑上到達一個導航點時,調用 ISignalOnNavMeshWaypointReached 。查看WaypointFlags列舉以取得更多關於導航點的資訊:TargetLinkStartLinkEnd

只在NavMeshAgentConfig.MovementType設定為Callback,以及代理有一個NavMeshSteeringAgent元件時,調用 ISignalOnNavMeshMoveAgentdesiredDirection參數是已標準化的方向,其是內部轉向及迴避認為代理運動向量應該的方向。

C#

public void OnNavMeshMoveAgent(Frame f, EntityRef entity, FPVector2 desiredDirection) {
    var agent = f.Unsafe.GetPointer<NavMeshSteeringAgent>(entity);

    // simple demonstration how to move the agent.
    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以節省處理器時間。

自訂移動但附有Quantum迴避

使用所有三個元件(路徑尋找器、轉向代理和迴避代理)。迴避代理依賴於轉向代理的一些部分,縱使您希望覆寫它。在NavMeshAgentConfig中,設定MovementTypeCallback,並且執行ISignalOnNavMeshMoveAgent信號(請見先前章節)。desiredDirection參數包含迴避更改的移動方向。

Back to top