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...
Usando NavigationAgents
NavigationAgents são nós auxiliares que combinam funcionalidades para busca de caminhos, acompanhamento de caminhos e desvio de agentes para um nó pai que herde de Node2D/3D. Eles facilitam chamadas comuns para a API do NavigationServer em nome do nó ator pai de uma maneira mais conveniente para iniciantes.
As versões 2D e 3D de NavigationAgents estão disponíveis como NavigationAgent2D e NavigationAgent3D, respectivamente.
Novos nós NavigationAgent se juntarão automaticamente ao mapa de navegação padrão no World2D/World3D.
Os nós NavigationAgent são opcionais e não são um requisito estrito para usar o sistema de navegação. Toda a sua funcionalidade pode ser substituída por scripts e chamadas diretas à API do NavigationServer.
Dica
Para usos mais avançados considere Usando NavigationPathQueryObjects em vez de nós NavigationAgent.
Busca de caminho com NavigationAgent
NavigationAgents consultam um novo caminho de navegação em seu mapa de navegação atual quando sua target_position é definida com uma posição global.
O resultado da busca de caminhos(pathfinding) pode ser influenciado com as seguintes propriedades.
A máscara de bits
navigation_layerspode ser usada para limitar as malhas de navegação que o agente pode usar.O
pathfinding_algorithmcontrola como a busca de caminhos viaja através dos polígonos da malha de navegação na busca do caminho.O
path_postprocessingdefine se ou como o corredor de caminho bruto encontrado pela busca de caminhos é alterado antes de ser retornado.As
path_metadata_flagsativam a coleta de metadados adicionais dos pontos do caminho retornados pelo caminho.As propriedades
simplify_pathesimplify_epsilonpodem ser usadas para remover pontos menos críticos do caminho.
Aviso
Desativar as flags de metadados do caminho desativará as emissões de sinais relacionados no agente.
Seguimento de caminho com NavigationAgent
Depois que uma target_position foi definida para o agente, a próxima posição a ser seguida no caminho pode ser recuperada com a função get_next_path_position().
Assim que a próxima posição do caminho for recebida, mova o nó ator pai do agente em direção a essa posição do caminho com o seu próprio código de movimento.
Nota
O sistema de navegação nunca move o nó pai de um NavigationAgent. O movimento está inteiramente nas mãos dos usuários e de seus scripts personalizados.
NavigationAgents possuem sua própria lógica interna para prosseguir com o caminho atual e solicitar atualizações.
A função get_next_path_position() é responsável por atualizar muitos dos estados e propriedades internos do agente. A função deve ser chamada repetidamente uma vez a cada physics_process até que is_navigation_finished() informe que o caminho terminou. A função não deve ser chamada após a posição de destino ou o fim do caminho ter sido alcançado, pois pode fazer o agente tremer no lugar devido às repetidas atualizações de caminho. Sempre verifique bem cedo no script com is_navigation_finished() se o caminho já foi concluído.
As seguintes propriedades de distância influenciam o caminho que segue o comportamento.
A uma distância
path_desired_distanceda próxima posição do caminho, o agente avança seu índice de caminho interno para a próxima posição subsequente do caminho.A uma distância
target_desired_distanceda posição de destino do caminho, o agente considera que a posição de destino foi alcançada e o caminho chegou ao seu fim.A uma distância
path_max_distancedo caminho ideal para a próxima posição do caminho, o agente solicita um novo caminho porque foi empurrado para muito longe.
As atualizações importantes são todas acionadas com a função get_next_path_position() quando chamada em _physics_process().
NavigationAgents podem ser usados com process, mas ainda estão limitados a uma única atualização que acontece em physics_process.
Exemplos de script para vários nós comumente usados com NavigationAgents podem ser encontrados mais abaixo.
Problemas comuns de Pathfollowing
Existem alguns problemas comuns dos usuários e ressalvas importantes a serem consideradas ao escrever scripts de movimento de agentes.
- O caminho é retornado vazio
If an agent queries a path before the navigation map synchronization, 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.
- O agente está travado "dançando" entre duas posições
Isso geralmente é causado por atualizações de caminho muito frequentes a cada quadro, seja deliberadamente ou por acidente (por exemplo, a distância máxima do caminho definida como muito curta). A busca de caminhos precisa encontrar a posição válida mais próxima na malha de navegação. Se um novo caminho for solicitado a cada quadro, as primeiras posições do caminho podem acabar alternando constantemente à frente e atrás da posição atual do agente, fazendo com que ele dance entre as duas posições.
- O agente às vezes está voltando atrás (backtracking)
Se um agente se move muito rápido, ele pode ultrapassar a verificação de path_desired_distance sem nunca avançar o índice do caminho. Isso pode levar o agente a retroceder para o ponto do caminho que ficou para trás até que ele passe na verificação de distância para aumentar o índice do caminho. Aumentar as distâncias desejadas de acordo com a velocidade do seu agente e a taxa de atualização geralmente resolve isso, assim como um layout de polígono de malha de navegação (navmesh) mais equilibrado, com não muitos vértices de polígono aglomerados em espaços pequenos.
- O agente às vezes olha para trás por um quadro
O mesmo que ocorre com os agentes travados dançando entre duas posições, isso geralmente é causado por atualizações de caminho muito frequentes a cada quadro. Dependendo do layout da sua malha de navegação, e especialmente quando um agente é colocado diretamente sobre uma aresta de malha de navegação ou conexão de aresta, espere que as posições do caminho estejam às vezes ligeiramente "atrás" da orientação atual dos seus atores. Isso acontece devido a problemas de precisão e nem sempre pode ser evitado. Esse geralmente é apenas um problema visível se os atores forem rotacionados instantaneamente para encarar a posição atual do caminho.
Evasão com NavigationAgent
Esta seção explica como usar o desvio de obstáculos (avoidance) específico para NavigationAgents.
Para que os NavigationAgents usem o recurso de desvio, a propriedade avoidance_enabled deve ser definida como true.
O sinal velocity_computed do nó NavigationAgent deve ser conectado para receber o resultado do cálculo da velocidade segura.
Defina a velocity do nó NavigationAgent em _physics_process() para atualizar o agente com a velocidade atual do nó pai do agente.
Enquanto o desvio estiver ativado no agente, o vetor safe_velocity será recebido com o sinal velocity_computed a cada quadro de física. Esse vetor de velocidade deve ser usado para mover o nó pai do NavigationAgent a fim de evitar colisões com outros agentes ou obstáculos que usem o desvio.
Nota
Apenas outros agentes no mesmo mapa que estejam registrados para desvio serão considerados no cálculo de desvio.
Nota
O NavigationAgent deve receber um atributo target_position, mesmo se você estiver usando o agente apenas para desvio. Caso contrário, a safe_velocity recebida do sinal velocity_computed será sempre o vetor zero.
As seguintes propriedades do NavigationAgent são relevantes para o desvio:
A propriedade
heightestá disponível apenas em 3D. A altura, juntamente com a posição atual no eixo y global do agente, determina o posicionamento vertical do agente na simulação de desvio. Agentes que usam o desvio 2D ignorarão automaticamente outros agentes ou obstáculos que estejam abaixo ou acima deles.The property
radiuscontrols the radius of the avoidance circle, or sphere in 3D, around the agent. This area describes the agents body and not the avoidance maneuver distance.A propriedade
neighbor_distancecontrola o raio de busca do agente ao procurar outros agentes que devam ser evitados. Um valor menor reduz o custo de processamento.A propriedade
max_neighborscontrola quantos outros agentes são considerados no cálculo de desvio se todos tiverem raios sobrepostos. Um valor menor reduz o custo de processamento, mas um valor muito baixo pode fazer com que os agentes ignorem o desvio.As propriedades
time_horizon_agentsetime_horizon_obstaclescontrolam o tempo de previsão de desvio para outros agentes ou obstáculos em segundos. Quando os agentes calculam suas velocidades seguras, eles escolhem velocidades que podem ser mantidas por essa quantidade de segundos sem colidir com outro objeto de desvio. O tempo de previsão deve ser mantido o mais baixo possível, pois os agentes reduzirão suas velocidades para evitar colisões nesse intervalo de tempo.A propriedade
max_speedcontrola a velocidade máxima permitida para o cálculo de desvio dos agentes. Se o pai do agente se mover mais rápido do que esse valor, asafe_velocityde desvio pode não ser precisa o suficiente para evitar a colisão.A propriedade
use_3d_avoidancealterna o agente entre o desvio 2D (eixo xz) e o desvio 3D (eixo xyz) na próxima atualização. Note que o desvio 2D e o desvio 3D funcionam em simulações de desvio separadas, de modo que os agentes divididos entre eles não afetam uns aos outros.As propriedades
avoidance_layerseavoidance_masksão máscaras de bits semelhantes a, por exemplo, camadas de física. Os agentes só evitarão outros objetos de desvio que estejam em uma camada de desvio que corresponda a pelo menos um dos bits de sua própria máscara de desvio.A
avoidance_priorityfaz com que agentes com maior prioridade ignorem agentes com menor prioridade. Isso pode ser usado para dar mais importância a certos agentes na simulação de desvio, por exemplo, personagens não jogáveis importantes, sem alterar constantemente todas as suas camadas ou máscaras de desvio.
O desvio existe em seu próprio espaço e não possui informações das malhas de navegação ou de colisões de física. Nos bastidores, os agentes de desvio são apenas círculos com raios diferentes em um plano 2D plano ou esferas em um espaço 3D vazio. NavigationObstacles podem ser usados para adicionar algumas restrições de ambiente à simulação de desvio; consulte Usando NavigationObstacles.
Nota
O desvio não afeta a busca de caminhos. Ele deve ser visto como uma opção adicional para objetos em constante movimento que não podem ser gerados (re-baked) em uma malha de navegação de forma eficiente para se mover ao redor deles.
Nota
O desvio RVO faz suposições implícitas sobre o comportamento natural do agente. Por exemplo, que os agentes se movem em lados de passagem razoáveis que podem ser atribuídos quando eles se encontram. Isso significa que cenários de teste de desvio muito clínicos comumente falharão. Por exemplo, agentes movidos diretamente um contra o outro com velocidades opostas perfeitas falharão porque não conseguem ter seus lados de passagem atribuídos.
Usar la propriedade avoidance_enabled do NavigationAgent é a opção preferida para alternar o desvio. Os trechos de código a seguir podem ser usados para alternar o desvio em agentes, criar ou excluir callbacks de desvio ou alternar modos de desvio.
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() { }
}
Modelos de script de NavigationAgent
As seções a seguir fornecem modelos de script para nós comumente usados com NavigationAgents.
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;
}
}