Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
NavigationAgent 사용하기
NavigationsAgents는 상위 노드를 상속하는 Node2D/3D에 대한 경로 찾기, 경로 따르기 및 에이전트 회피 기능을 결합한 도우미 노드입니다. 이는 초보자에게 보다 편리한 방식으로 상위 행위자 노드를 대신하여 NavigationServer API에 대한 일반적인 호출을 촉진합니다.
NavigationAgent의 2D 및 3D 버전은 각각 NavigationAgent2D 및 :ref:`NavigationAgent3D<class_NavigationAgent3D>`로 제공됩니다.
새로운 NavigationAgent 노드는 World2D/:ref:`World3D<class_World3D>`의 기본 탐색 지도에 자동으로 연결됩니다.
NavigationsAgent 노드는 선택 사항이며 내비게이션 시스템을 사용하기 위한 필수 요구 사항은 아닙니다. 전체 기능을 스크립트로 대체하고 NavigationServer API를 직접 호출할 수 있습니다.
팁
GDScript는 GDScript 형식 문자열도 지원합니다.
NavigationAgent 경로 탐색
NavigationAgent는 ``target_position``가 전역 위치로 설정된 경우 현재 탐색 지도에서 새 탐색 경로를 쿼리합니다.
적 씬은 다음 노드들을 사용할 것입니다:
navigation_layers비트마스크는 에이전트가 사용할 수 있는 탐색 메시를 제한하는 데 사용할 수 있습니다.``pathfinding_algorithm``는 경로 검색에서 탐색 메시 다각형을 통해 경로 찾기가 이동하는 방법을 제어합니다.
``path_postprocessing``는 경로 찾기에서 찾은 원시 경로 복도가 반환되기 전에 변경되는지 여부 또는 방법을 설정합니다.
``path_metadata_flags``를 사용하면 경로에서 반환된 추가 경로 지점 메타데이터를 수집할 수 있습니다.
simplify_path및simplify_epsilon속성은 경로에서 덜 중요한 지점을 제거하는 데 사용할 수 있습니다.
경고
경로 메타 플래그를 비활성화하면 에이전트에서 관련 시그널 방출이 비활성화됩니다.
내비게이션
에이전트에 대해 target_position``가 설정된 후 경로에서 따라야 할 다음 위치는 ``get_next_path_position() 기능을 사용하여 검색할 수 있습니다.
다음 경로 위치가 수신되면 에이전트의 상위 액터 노드를 자신의 이동 코드를 사용하여 이 경로 위치로 이동시킵니다.
참고
네비게이션 시스템은 NavigationAgent의 상위 노드를 이동하지 않습니다. 무브먼트는 전적으로 사용자와 사용자 정의 스크립트의 손에 달려 있습니다.
NavigationAgent에는 현재 경로를 진행하고 업데이트를 호출하는 자체 내부 논리가 있습니다.
get_next_path_position() 기능은 에이전트의 많은 내부 상태 및 속성을 업데이트하는 역할을 합니다. 이 함수는 ``is_navigation_finished()``가 경로가 완료되었음을 알릴 때까지 ``physics_process``마다 한 번씩 반복적으로 호출되어야 합니다. 반복되는 경로 업데이트로 인해 에이전트가 제자리에 지터될 수 있으므로 대상 위치 또는 경로 끝에 도달한 후에는 함수를 호출해서는 안 됩니다. 경로가 이미 완료된 경우 항상 ``is_navigation_finished()``를 사용하여 스크립트에서 매우 일찍 확인하십시오.
적 씬은 다음 노드들을 사용할 것입니다:
다음 경로 위치의 ``path_desired_distance``에서 에이전트는 내부 경로 인덱스를 다음 경로 위치로 이동합니다.
목표 경로 위치로부터 ``target_desired_distance``에서 에이전트는 도달할 목표 위치와 그 끝의 경로를 고려합니다.
이상적인 경로에서 다음 경로 위치까지 ``path_max_distance``에서 에이전트는 경로가 너무 멀리 밀렸기 때문에 새 경로를 요청합니다.
중요한 업데이트는 모두 _physics_process()``에서 호출될 때 ``get_next_path_position() 함수로 트리거됩니다.
NavigationAgent는 ``process``와 함께 사용할 수 있지만 여전히 ``physics_process``에서 발생하는 단일 업데이트로 제한됩니다.
NavigationAgent와 함께 일반적으로 사용되는 다양한 노드에 대한 스크립트 예제는 아래에서 자세히 확인할 수 있습니다.
다음 예제에서:
에이전트 이동 스크립트를 작성할 때 고려해야 할 몇 가지 일반적인 사용자 문제와 중요한 주의 사항이 있습니다.
- 경로가 비어 있는 상태로 반환됩니다.
에이전트가 내비게이션 지도 동기화 전에 경로를 쿼리하는 경우(예:
_ready()함수에서는 경로가 비어 있는 상태로 반환될 수 있습니다. 이 경우get_next_path_position()함수는 에이전트 상위 노드와 동일한 위치를 반환하고 에이전트는 경로 끝에 도달한 것으로 간주합니다. 이 문제는 지연된 호출을 하거나 콜백을 사용하여 해결됩니다. 내비게이션 지도가 시그널로 변경되기를 기다리는 중입니다.
- 에이전트가 두 위치 사이에서 춤을 추고 있습니다.
이는 일반적으로 고의적이든 우연이든 매 프레임마다 매우 빈번하게 경로를 업데이트하는 경우 발생합니다(예: 최대 경로 거리가 너무 짧게 설정됨). 길 찾기는 탐색 메시에서 유효한 가장 가까운 위치를 찾아야 합니다. 매 프레임마다 새 경로가 요청되면 첫 번째 경로 위치가 에이전트의 현재 위치 앞뒤로 지속적으로 전환되어 에이전트가 두 위치 사이에서 춤을 추게 될 수 있습니다.
- 상담원이 가끔 역추적을 하는 경우가 있습니다.
에이전트가 매우 빠르게 이동하는 경우 경로 인덱스를 진행하지 않고 path_desired_distance 확인을 초과할 수 있습니다. 이로 인해 에이전트가 경로 인덱스를 증가시키기 위한 거리 확인을 통과할 때까지 현재 뒤에 있는 경로 지점으로 역추적하게 될 수 있습니다. 에이전트 속도와 업데이트 속도에 따라 원하는 거리를 늘리면 일반적으로 이 문제가 해결될 뿐만 아니라 작은 공간에 너무 많은 다각형 가장자리가 비좁아지지 않는 보다 균형 잡힌 탐색 메시 다각형 레이아웃이 제공됩니다.
- 에이전트는 때때로 프레임을 찾기 위해 뒤를 돌아봅니다.
두 위치 사이에 멈춰 춤추는 에이전트와 마찬가지로 이는 일반적으로 매 프레임마다 매우 빈번한 경로 업데이트로 인해 발생합니다. 내비게이션 메시 레이아웃에 따라, 특히 에이전트가 내비게이션 메시 가장자리 또는 가장자리 연결 위에 직접 배치되는 경우 경로 위치가 때때로 액터의 현재 방향보다 약간 "뒤"될 것으로 예상됩니다. 이는 정밀도 문제로 인해 발생하며 항상 피할 수는 없습니다. 이는 일반적으로 액터가 현재 경로 위치를 향하도록 즉시 회전하는 경우에만 눈에 보이는 문제입니다.
내비게이션
이 섹션에서는 NavigationAgent와 관련된 탐색 회피를 사용하는 방법을 설명합니다.
NavigationAgent가 회피 기능을 사용하려면 avoidance_enabled 속성을 ``true``로 설정해야 합니다.
안전한 속도 계산 결과를 받으려면 NavigationAgent 노드의 velocity_computed 시그널를 연결해야 합니다.
NavigationAgent 노드의 ``velocity``를 ``_physics_process()``에서 설정하여 에이전트 상위 노드의 현재 속도로 에이전트를 업데이트합니다.
에이전트에서 회피가 활성화되어 있는 동안 safe_velocity 벡터는 모든 물리 프레임마다 속도 계산된 시그널와 함께 수신됩니다. 이 속도 벡터는 에이전트를 사용하는 다른 회피 또는 회피 장애물과의 충돌을 피하기 위해 NavigationAgent의 상위 노드를 이동하는 데 사용되어야 합니다.
참고
회피를 위해 등록된 동일한 맵의 다른 에이전트만 회피 계산에 고려됩니다.
참고
The NavigationAgent must be supplied with a target_position attribute,
even if you are only using the agent for avoidance. Otherwise, the safe_velocity
received from the velocity_computed signal will always be the zero vector.
다음 NavigationAgent 속성은 회피와 관련이 있습니다.
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``는 우선순위가 높은 에이전트가 우선순위가 낮은 에이전트를 무시하도록 합니다. 이는 회피 시뮬레이션에서 특정 에이전트에 더 많은 중요성을 부여하는 데 사용될 수 있습니다. 전체 회피 레이어나 마스크를 지속적으로 변경하지 않고 플레이할 수 없는 중요한 캐릭터입니다.
회피는 자체 공간에 존재하며 내비게이션 메시나 물리 충돌로 인한 정보가 없습니다. 씬 회피 에이전트 뒤에는 평평한 2D 평면의 반경이 다른 원이나 빈 3D 공간의 구가 있습니다. NavigationObstacles를 사용하면 회피 시뮬레이션에 일부 환경 제약 조건을 추가할 수 있습니다. :ref:`doc_navigation_using_navigationobstacles`를 참조하세요.
참고
회피는 길 찾기에 영향을 미치지 않습니다. 이는 주변을 이동하기 위해 탐색 메시에 효율적으로 (재)구울 수 없는 지속적으로 움직이는 개체에 대한 추가 옵션으로 간주되어야 합니다.
참고
RVO 회피는 자연스러운 에이전트 행동에 대해 암시적인 가정을 합니다. 예: 에이전트는 서로 만날 때 할당될 수 있는 합리적인 통과 방향으로 이동합니다. 이는 매우 임상적인 회피 테스트 시나리오가 일반적으로 실패한다는 것을 의미합니다. 예: 완전히 반대되는 속도로 서로 직접적으로 이동하는 에이전트는 패스하는 쪽을 할당할 수 없기 때문에 실패합니다.
NavigationAgent avoidance_enabled 속성을 사용하는 것은 회피를 전환하는 데 선호되는 옵션입니다. 다음 코드 조각을 사용하여 에이전트 회피를 전환하거나 회피 콜백을 생성 또는 삭제하거나 회피 모드를 전환할 수 있습니다.
extends NavigationAgent2D
func _ready() -> void:
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())
using Godot;
public partial class MyNavigationAgent2D : NavigationAgent2D
{
public override void _Ready()
{
Rid agent = GetRid();
// Enable avoidance
NavigationServer2D.AgentSetAvoidanceEnabled(agent, true);
// Create avoidance callback
NavigationServer2D.AgentSetAvoidanceCallback(agent, Callable.From(AvoidanceDone));
// Disable avoidance
NavigationServer2D.AgentSetAvoidanceEnabled(agent, false);
//Delete avoidance callback
NavigationServer2D.AgentSetAvoidanceCallback(agent, default);
}
private void AvoidanceDone() { }
}
extends NavigationAgent3D
func _ready() -> void:
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)
using Godot;
public partial class MyNavigationAgent3D : NavigationAgent3D
{
public override void _Ready()
{
Rid agent = GetRid();
// Enable avoidance
NavigationServer3D.AgentSetAvoidanceEnabled(agent, true);
// Create avoidance callback
NavigationServer3D.AgentSetAvoidanceCallback(agent, Callable.From(AvoidanceDone));
// Switch to 3D avoidance
NavigationServer3D.AgentSetUse3DAvoidance(agent, true);
// Disable avoidance
NavigationServer3D.AgentSetAvoidanceEnabled(agent, false);
//Delete avoidance callback
NavigationServer3D.AgentSetAvoidanceCallback(agent, default);
// Switch to 2D avoidance
NavigationServer3D.AgentSetUse3DAvoidance(agent, false);
}
private void AvoidanceDone() { }
}
스크립트 템플릿 만들기
다음 섹션에서는 NavigationAgent와 함께 일반적으로 사용되는 노드용 스크립트 템플릿을 제공합니다.
extends Node2D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent2D = get_node("NavigationAgent2D")
var movement_delta: float
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector2):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
# Do not query when the map has never synchronized and is empty.
if NavigationServer2D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
if navigation_agent.is_navigation_finished():
return
movement_delta = movement_speed * delta
var next_path_position: Vector2 = navigation_agent.get_next_path_position()
var new_velocity: Vector2 = 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: Vector2) -> void:
global_position = global_position.move_toward(global_position + safe_velocity, movement_delta)
extends CharacterBody2D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent2D = get_node("NavigationAgent2D")
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector2):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
# Do not query when the map has never synchronized and is empty.
if NavigationServer2D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
if navigation_agent.is_navigation_finished():
return
var next_path_position: Vector2 = navigation_agent.get_next_path_position()
var new_velocity: Vector2 = 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: Vector2):
velocity = safe_velocity
move_and_slide()
extends RigidBody2D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent2D = get_node("NavigationAgent2D")
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector2):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
# Do not query when the map has never synchronized and is empty.
if NavigationServer2D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
if navigation_agent.is_navigation_finished():
return
var next_path_position: Vector2 = navigation_agent.get_next_path_position()
var new_velocity: Vector2 = 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: Vector2):
linear_velocity = safe_velocity
using Godot;
public partial class MyNode2D : Node2D
{
[Export]
public float MovementSpeed { get; set; } = 4.0f;
NavigationAgent2D _navigationAgent;
private float _movementDelta;
public override void _Ready()
{
_navigationAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
_navigationAgent.VelocityComputed += OnVelocityComputed;
}
private void SetMovementTarget(Vector2 movementTarget)
{
_navigationAgent.TargetPosition = movementTarget;
}
public override void _PhysicsProcess(double delta)
{
// Do not query when the map has never synchronized and is empty.
if (NavigationServer2D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
{
return;
}
if (_navigationAgent.IsNavigationFinished())
{
return;
}
_movementDelta = MovementSpeed * (float)delta;
Vector2 nextPathPosition = _navigationAgent.GetNextPathPosition();
Vector2 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * _movementDelta;
if (_navigationAgent.AvoidanceEnabled)
{
_navigationAgent.Velocity = newVelocity;
}
else
{
OnVelocityComputed(newVelocity);
}
}
private void OnVelocityComputed(Vector2 safeVelocity)
{
GlobalPosition = GlobalPosition.MoveToward(GlobalPosition + safeVelocity, _movementDelta);
}
}
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
[Export]
public float MovementSpeed { get; set; } = 4.0f;
NavigationAgent2D _navigationAgent;
public override void _Ready()
{
_navigationAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
_navigationAgent.VelocityComputed += OnVelocityComputed;
}
private void SetMovementTarget(Vector2 movementTarget)
{
_navigationAgent.TargetPosition = movementTarget;
}
public override void _PhysicsProcess(double delta)
{
// Do not query when the map has never synchronized and is empty.
if (NavigationServer2D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
{
return;
}
if (_navigationAgent.IsNavigationFinished())
{
return;
}
Vector2 nextPathPosition = _navigationAgent.GetNextPathPosition();
Vector2 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * MovementSpeed;
if (_navigationAgent.AvoidanceEnabled)
{
_navigationAgent.Velocity = newVelocity;
}
else
{
OnVelocityComputed(newVelocity);
}
}
private void OnVelocityComputed(Vector2 safeVelocity)
{
Velocity = safeVelocity;
MoveAndSlide();
}
}
using Godot;
public partial class MyRigidBody2D : RigidBody2D
{
[Export]
public float MovementSpeed { get; set; } = 4.0f;
NavigationAgent2D _navigationAgent;
public override void _Ready()
{
_navigationAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
_navigationAgent.VelocityComputed += OnVelocityComputed;
}
private void SetMovementTarget(Vector2 movementTarget)
{
_navigationAgent.TargetPosition = movementTarget;
}
public override void _PhysicsProcess(double delta)
{
// Do not query when the map has never synchronized and is empty.
if (NavigationServer2D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
{
return;
}
if (_navigationAgent.IsNavigationFinished())
{
return;
}
Vector2 nextPathPosition = _navigationAgent.GetNextPathPosition();
Vector2 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * MovementSpeed;
if (_navigationAgent.AvoidanceEnabled)
{
_navigationAgent.Velocity = newVelocity;
}
else
{
OnVelocityComputed(newVelocity);
}
}
private void OnVelocityComputed(Vector2 safeVelocity)
{
LinearVelocity = safeVelocity;
}
}
extends Node3D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
var physics_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):
# Save the delta for use in _on_velocity_computed.
physics_delta = delta
# Do not query when the map has never synchronized and is empty.
if NavigationServer3D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
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) -> void:
global_position = global_position.move_toward(global_position + safe_velocity, physics_delta * movement_speed)
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):
# Do not query when the map has never synchronized and is empty.
if NavigationServer3D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
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()
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):
# Do not query when the map has never synchronized and is empty.
if NavigationServer3D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
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
using Godot;
public partial class MyNode3D : Node3D
{
[Export]
public float MovementSpeed { get; set; } = 4.0f;
NavigationAgent3D _navigationAgent;
private float _movementDelta;
public override void _Ready()
{
_navigationAgent = GetNode<NavigationAgent3D>("NavigationAgent3D");
_navigationAgent.VelocityComputed += OnVelocityComputed;
}
private void SetMovementTarget(Vector3 movementTarget)
{
_navigationAgent.TargetPosition = movementTarget;
}
public override void _PhysicsProcess(double delta)
{
// Do not query when the map has never synchronized and is empty.
if (NavigationServer3D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
{
return;
}
if (_navigationAgent.IsNavigationFinished())
{
return;
}
_movementDelta = MovementSpeed * (float)delta;
Vector3 nextPathPosition = _navigationAgent.GetNextPathPosition();
Vector3 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * _movementDelta;
if (_navigationAgent.AvoidanceEnabled)
{
_navigationAgent.Velocity = newVelocity;
}
else
{
OnVelocityComputed(newVelocity);
}
}
private void OnVelocityComputed(Vector3 safeVelocity)
{
GlobalPosition = GlobalPosition.MoveToward(GlobalPosition + safeVelocity, _movementDelta);
}
}
using Godot;
public partial class MyCharacterBody3D : CharacterBody3D
{
[Export]
public float MovementSpeed { get; set; } = 4.0f;
NavigationAgent3D _navigationAgent;
public override void _Ready()
{
_navigationAgent = GetNode<NavigationAgent3D>("NavigationAgent3D");
_navigationAgent.VelocityComputed += OnVelocityComputed;
}
private void SetMovementTarget(Vector3 movementTarget)
{
_navigationAgent.TargetPosition = movementTarget;
}
public override void _PhysicsProcess(double delta)
{
// Do not query when the map has never synchronized and is empty.
if (NavigationServer3D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
{
return;
}
if (_navigationAgent.IsNavigationFinished())
{
return;
}
Vector3 nextPathPosition = _navigationAgent.GetNextPathPosition();
Vector3 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * MovementSpeed;
if (_navigationAgent.AvoidanceEnabled)
{
_navigationAgent.Velocity = newVelocity;
}
else
{
OnVelocityComputed(newVelocity);
}
}
private void OnVelocityComputed(Vector3 safeVelocity)
{
Velocity = safeVelocity;
MoveAndSlide();
}
}
using Godot;
public partial class MyRigidBody3D : RigidBody3D
{
[Export]
public float MovementSpeed { get; set; } = 4.0f;
NavigationAgent3D _navigationAgent;
public override void _Ready()
{
_navigationAgent = GetNode<NavigationAgent3D>("NavigationAgent3D");
_navigationAgent.VelocityComputed += OnVelocityComputed;
}
private void SetMovementTarget(Vector3 movementTarget)
{
_navigationAgent.TargetPosition = movementTarget;
}
public override void _PhysicsProcess(double delta)
{
// Do not query when the map has never synchronized and is empty.
if (NavigationServer3D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
{
return;
}
if (_navigationAgent.IsNavigationFinished())
{
return;
}
Vector3 nextPathPosition = _navigationAgent.GetNextPathPosition();
Vector3 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * MovementSpeed;
if (_navigationAgent.AvoidanceEnabled)
{
_navigationAgent.Velocity = newVelocity;
}
else
{
OnVelocityComputed(newVelocity);
}
}
private void OnVelocityComputed(Vector3 safeVelocity)
{
LinearVelocity = safeVelocity;
}
}