Up to date
This page is up to date for Godot 4.2
.
If you still find outdated information, please open an issue.
使用 NavigationAgent¶
NavigationsAgents 是輔助節點,它結合了 Node2D/3D 繼承父節點的尋路、路徑追蹤和代理迴避功能。對於初學者來說,它們以更方便的方式代表父參與者節點促進對 NavigationServer API 的常見呼叫。
2D 和 3D 版本的 NavigationAgent 分別可用作:ref:NavigationAgent2D<class_NavigationAgent2D> 和:ref:NavigationAgent3D<class_NavigationAgent3D>。
New NavigationAgent nodes will automatically join the default navigation map on the World2D/World3D.
NavigationsAgent 節點是可選的,並不是使用導覽系統的硬性需求。它們的全部功能可以替換為腳本和對 NavigationServer API 的直接呼叫。
導覽¶
當導覽代理程式的「target_position」設定為全域位置時,它們會在目前導覽地圖上查詢新的導覽路徑。
填寫下列設定:
「navigation_layers」位元遮罩可用於限制代理可以使用的導覽網格。
“pathfinding_algorithm” 控制路徑搜尋如何在路徑搜尋中穿過導覽網格多邊形。
“path_postprocessing”設定在返回之前是否或如何更改尋路找到的原始路徑走廊。
path_metadata_flags
允許收集路徑返回的附加路徑點元資料。
警告
禁用路徑元旗標將禁用代理上的相關訊號發射。
導覽¶
為代理程式設定「target_position」後,可以使用「get_next_path_position()」函式來擷取路徑中要遵循的下一個位置。
收到下一個路徑位置後,使用您自己的移動程式碼將代理程式的父角色節點移向該路徑位置。
備註
導覽系統永遠不會移動 NavigationAgent 的父節點。該動作完全掌握在使用者及其自訂腳本的手中。
導覽代理有自己的內部邏輯來繼續目前路徑並呼叫更新。
The get_next_path_position()
function is responsible for updating many of the agent's internal states and properties.
The function should be repeatedly called once every physics_process
until is_navigation_finished()
tells that the path is finished.
The function should not be called after the target position or path end has been reached
as it can make the agent jitter in place due to the repeated path updates.
Always check very early in script with is_navigation_finished()
if the path is already finished.
這個節點有如下屬性可供設定。
“path_desired_distance”定義了代理程式將其內部路徑索引推進到下一個路徑位置的距離。
「target_desired_distance」定義了代理認為要到達的目標位置及其終點的路徑的距離。
「path_max_distance」定義代理程式何時要求新路徑,因為它距離目前路徑點段太遠。
當在 _physical_process() 中呼叫時,重要的更新都是由 get_next_path_position() 函式觸發的。
NavigationAgents 可以與「process」一起使用,但仍僅限於「physical_process」中發生的單一更新。
下面可以找到通常與 NavigationAgents 一起使用的各種節點的腳本範例。
在下方範例中:¶
編寫代理程式移動腳本時需要考慮一些常見的使用者問題和重要注意事項。
- 回程的路徑為空
If an agent queries a path before the navigation map synchronisation, e.g. in a
_ready()
function, the path might return empty. In this case theget_next_path_position()
function will return the same position as the agent parent node and the agent will consider the path end reached. This is fixed by making a deferred call or using a callback e.g. waiting for the navigation map changed signal.
- 特工被困在兩個位置之間跳舞
這通常是由於每個影格非常頻繁的路徑更新造成的,無論是有意還是無意(例如,最大路徑距離設定得太短)。尋路需要找到導覽網格上有效的最近位置。如果每個影格都請求新路徑,則第一個路徑位置可能最終會在代理目前位置的前後不斷切換,導致其在兩個位置之間跳舞。
- 代理有時會回溯
如果代理程式移動得非常快,它可能會超出 path_desired_distance 檢查,而不會推進路徑索引。這可能導致代理回溯到它後面的路徑點,直到它通過距離檢查以增加路徑索引。根據代理速度和更新速率相應地增加所需距離通常可以解決此問題,並且可以使用更平衡的導覽網格多邊形佈局,而不會在小空間內擠在一起太多的多邊形邊緣。
- 代理有時會向後尋找影格
與在兩個位置之間卡住的跳舞代理相同,這通常是由每個影格非常頻繁的路徑更新引起的。根據您的導覽網格佈局,尤其是當代理直接放置在導覽網格邊緣或邊緣連接上時,預計路徑位置有時會稍微「落後」角色目前方向。這種情況的發生是由於精度問題,並且並非總是能夠避免。如果演員立即旋轉以面向目前路徑位置,這通常只是一個可見的問題。
導覽¶
本節介紹如何使用特定於 NavigationAgent 的導覽迴避。
為了使 NavigationAgents 使用迴避功能,「enable_avoidance」屬性必須設定為「true」。
The velocity_computed
signal of the NavigationAgent node must be connected to receive the safe velocity calculation result.
在 _physical_process() 中的 NavigationAgent 節點上使用 set_velocity() 來使用代理父節點的目前速度來更新代理程式。
當代理啟用迴避時,每個物理訊框都會收到「safe_velocity」向量以及velocity_compulated訊號。此速度向量應用於移動導覽代理的父節點,以避免與其他使用代理或迴避障礙物的迴避發生碰撞。
備註
只有在同一地圖上註冊為迴避的其他代理才會被考慮在迴避計算中。
這個節點有如下屬性可供設定:
屬性“height”僅在 3D 中可用。高度與智慧體目前的全局 y 軸位置一起決定了智慧體在迴避模擬中的垂直位置。使用 2D 迴避的智慧體將自動忽略位於其下方或上方的其他智慧體或障礙物。
屬性「radius」控制迴避圓的大小,或在 3D 球體的情況下,控制代理人周圍的迴避圓的大小。該區域描述了代理的身體,而不是迴避機動距離。
當搜尋應避免的其他代理程式時,屬性「neighbor_distance」控制代理程式的搜尋半徑。較低的值可降低處理成本。
屬性「max_neighbors」控制在迴避計算中考慮多少其他代理(如果它們都具有重疊半徑)。較低的值會降低處理成本,但太低的值可能會導致代理忽略迴避。
屬性“time_horizon_agents”和“time_horizon_obstacles”控制其他代理或障礙物的迴避預測時間(以秒為單位)。當代理人計算他們的安全速度時,他們選擇可以保持這一秒鐘而不會與另一個迴避物件發生碰撞的速度。預測時間應盡可能短,因為代理會減慢速度以避免在該時間範圍內發生碰撞。
屬性“max_speed”控制代理迴避計算所允許的最大速度。如果智慧體父母的移動速度快於該值,則避免「safe_velocity」可能不夠準確,無法避免碰撞。
屬性「use_3d_avoidance」在下次更新時在 2D 迴避(xz 軸)和 3D 迴避(xyz 軸)之間切換代理。請注意,2D 迴避和 3D 迴避在單獨的迴避模擬中運作,因此在它們之間分配的代理不會相互影響。
屬性「avoidance_layers」和「avoidance_mask」是類似物理層的位元遮罩。代理人只會避開位於至少與其自己的迴避遮罩位元之一所配對的迴避層上的其他迴避物件。
「avoidance_priority」使具有較高優先權的代理忽略具有較低優先權的代理。這可用於在迴避模擬中賦予某些代理更重要的作用,例如重要的NPC角色,而無需不斷更改其整個迴避層或掩模。
迴避存在於自己的空間中,並且沒有來自導覽網格或物理碰撞的資訊。幕後迴避代理只是平坦 2D 平面上不同半徑的圓或空的 3D 空間中的球體。 NavigationObstacles 可用於為迴避模擬新增一些環境約束,請參閱 使用 NavigationObstacle。
備註
迴避不影響尋路。它應該被視為不斷移動的物件的附加選項,這些物件無法有效地(重新)烘焙到導覽網格以便在它們周圍移動。
使用 NavigationAgent enable_avoidance
屬性是切換迴避的首選選項。以下程式碼片段可用於切換代理程式上的迴避、建立或刪除迴避回呼或切換迴避模式。
extends NavigationAgent2D
var agent: RID = get_rid()
# Enable avoidance
NavigationServer2D.agent_set_avoidance_enabled(agent, true)
# Create avoidance callback
NavigationServer2D.agent_set_avoidance_callback(agent, Callable(self, "_avoidance_done"))
# Disable avoidance
NavigationServer2D.agent_set_avoidance_enabled(agent, false)
# Delete avoidance callback
NavigationServer2D.agent_set_avoidance_callback(agent, Callable())
extends NavigationAgent3D
var agent: RID = get_rid()
# Enable avoidance
NavigationServer3D.agent_set_avoidance_enabled(agent, true)
# Create avoidance callback
NavigationServer3D.agent_set_avoidance_callback(agent, Callable(self, "_avoidance_done"))
# Switch to 3D avoidance
NavigationServer3D.agent_set_use_3d_avoidance(agent, true)
# Disable avoidance
NavigationServer3D.agent_set_avoidance_enabled(agent, false)
# Delete avoidance callback
NavigationServer3D.agent_set_avoidance_callback(agent, Callable())
# Switch to 2D avoidance
NavigationServer3D.agent_set_use_3d_avoidance(agent, false)
建立腳本樣板¶
以下部分提供了通常與NavigationAgents 一起使用的節點的腳本模板。
角色為 Node3D¶
This script adds basic navigation movement to a Node3D with a NavigationAgent3D child node.
extends Node3D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
var movement_delta: float
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
if navigation_agent.is_navigation_finished():
return
movement_delta = movement_speed * delta
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_delta
if navigation_agent.avoidance_enabled:
navigation_agent.set_velocity(new_velocity)
else:
_on_velocity_computed(new_velocity)
func _on_velocity_computed(safe_velocity: Vector3) -> void:
global_position = global_position.move_toward(global_position + safe_velocity, movement_delta)
角色為 CharacterBody3D¶
This script adds basic navigation movement to a CharacterBody3D with a NavigationAgent3D child node.
extends CharacterBody3D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
if navigation_agent.is_navigation_finished():
return
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_speed
if navigation_agent.avoidance_enabled:
navigation_agent.set_velocity(new_velocity)
else:
_on_velocity_computed(new_velocity)
func _on_velocity_computed(safe_velocity: Vector3):
velocity = safe_velocity
move_and_slide()
角色為 RigidBody3D¶
This script adds basic navigation movement to a RigidBody3D with a NavigationAgent3D child node.
extends RigidBody3D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
if navigation_agent.is_navigation_finished():
return
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_speed
if navigation_agent.avoidance_enabled:
navigation_agent.set_velocity(new_velocity)
else:
_on_velocity_computed(new_velocity)
func _on_velocity_computed(safe_velocity: Vector3):
linear_velocity = safe_velocity