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...
Utilizzo dei NavigationAgent
I NavigationsAgent sono nodi ausiliari che combinano funzionalità di ricerca del percorso, seguimento del percorso ed evitamento degli agenti per un nodo padre che eredita da Node2D/3D. Facilitano le chiamate comuni all'API del NavigationServer per conto del nodo padre, in modo più pratico per i principianti.
Le versioni 2D e 3D degli agenti di navigazione sono disponibili rispettivamente come NavigationAgent2D e NavigationAgent3D.
I nuovi nodi NavigationAgent si uniranno automaticamente alla mappa di navigazione predefinita sul World2D/World3D.
I nodi NavigationsAgent sono opzionali e non un requisito indispensabile per utilizzare il sistema di navigazione. La loro intera funzionalità può essere sostituita da script e chiamate dirette all'API del NavigationServer.
Suggerimento
Per utilizzi più avanzati, si consiglia di Utilizzare gli NavigationPathQueryObject al posto dei nodi NavigationAgent.
NavigationAgent Pathfinding
Gli agenti di navigazione richiedono un nuovo percorso di navigazione sulla loro mappa di navigazione attuale quando la loro target_position è impostata con una posizione globale.
Il risultato della ricerca del percorso può essere influenzato dalle seguenti proprietà.
La maschera di bit
navigation_layersserve per limitare le mesh di navigazione che l'agente può utilizzare.pathfinding_algorithmcontrolla il modo in cui il pathfinding si sposta attraverso i poligoni della mesh di navigazione nella ricerca del percorso.path_postprocessingimposta se e come il corridoio del percorso grezzo trovato dal pathfinding viene alterato prima di essere restituito.path_metadata_flagsconsentono la raccolta di metadati aggiuntivi sui punti restituiti dal percorso.Le proprietà
simplify_pathesimplify_epsilonservono per rimuovere i punti meno importanti dal percorso.
Avvertimento
Disabilitare i meta flag del percorso disabiliterà le emissioni di segnali correlati sull'agente.
NavigationAgent Pathfollowing
Dopo aver impostato una target_position per l'agente, è possibile recuperare la posizione successiva da seguire nel percorso con la funzione get_next_path_position().
Una volta ricevuta la posizione successiva nel percorso, spostare il nodo dell'attore padre dell'agente verso questa posizione nel percorso con il proprio codice di movimento.
Nota
Il sistema di navigazione non sposta mai il nodo padre di un agente. Lo spostamento è interamente nelle mani degli utenti e dei loro script personalizzati.
Gli agenti di navigazione hanno una propria logica interna per procedere con il percorso attuale e richiedere aggiornamenti.
La funzione get_next_path_position() è responsabile di aggiornare molti stati e proprietà interne dell'agente. La funzione si dovrebbe chiamare ripetutamente una volta ogni physics_process finché is_navigation_finished() non indica che il percorso è terminato. La funzione non si dovrebbe chiamare dopo aver raggiunto la posizione di destinazione o il termine del percorso, poiché ciò potrebbe far tremolare l'agente a causa dei ripetuti aggiornamenti del percorso. Verificare sempre molto presto nello script con is_navigation_finished() se il percorso è già terminato.
Le seguenti proprietà di distanza influenzano il comportamento di seguimento del percorso.
A
path_desired_distancedalla posizione successiva nel percorso, l'agente avanza il suo indice interno di percorso alla posizione successiva nel percorso.A
target_desired_distancedalla posizione di destinazione nel percorso, l'agente considera la posizione di destinazione raggiunta e il percorso alla sua fine.A
path_max_distancedal percorso ideale alla posizione successivo nel percorso, l'agente richiede un nuovo percorso perché è stato spostato troppo lontano.
Tutti gli aggiornamenti importanti vengono attivati con la funzione get_next_path_position() quando chiamata in _physics_process().
Gli agenti di navigazione si possono usare con process ma sono comunque limitati a un singolo aggiornamento che avviene in physics_process.
Di seguito sono riportati esempi di script per vari nodi comunemente utilizzati con gli agenti di navigazione.
Problemi comuni di seguimento di percorso
Quando si scrivono script per il movimento degli agenti, ci sono alcuni problemi comuni tra gli utenti e importanti avvertenze da tenere in considerazione.
- Il percorso viene restituito vuoto
Se un agente richiede un percorso prima della sincronizzazione della mappa di navigazione, ad esempio in una funzione
_ready(), il percorso potrebbe restituire un valore vuoto. In questo caso, la funzioneget_next_path_position()restituirà la stessa posizione del nodo padre dell'agente e l'agente considererà la fine del percorso raggiunta. È possibile risolvere ciò effettuando una chiamata differita o attraverso un callback, ad esempio attendendo il segnale di modifica della mappa di navigazione.
- L'agente è bloccato a oscillare tra due posizioni
Ciò è solitamente causato da aggiornamenti di percorso molto frequenti a ogni singolo frame, intenzionali o accidentali (ad esempio, la distanza massima del percorso è troppo breve). Il pathfinding deve trovare la posizione valida più vicina sulla mesh di navigazione. Se viene richiesto un nuovo percorso a ogni singolo frame, le prime posizioni del percorso potrebbero finire per scambiarsi costantemente davanti e dietro la posizione attuale dell'agente, facendolo oscillare tra le due posizioni.
- L'agente a volte torna indietro
Se un agente si muove molto velocemente, potrebbe superare la verifica di path_desired_distance senza mai avanzare l'indice di percorso. Ciò può portare l'agente a tornare indietro al punto nel percorso che ora si trova dietro di esso, finché non supera la verifica della distanza per poi aumentare l'indice di percorso. Aumentare le distanze desiderate, in base alla velocità dell'agente e la frequenza di aggiornamento, di solito risolve questo problema, così come un layout poligonale più bilanciato per la mesh di navigazione, con non troppi bordi poligonali ammassati in spazi ristretti.
- L'agente a volte guarda indietro per un frame
Simile a quanto accade con gli agenti bloccati a oscillare tra due posizioni, questo è solitamente causato da aggiornamenti di percorso molto frequenti a ogni singolo frame. A seconda del layout della mesh di navigazione, e soprattutto quando un agente è posizionato direttamente sopra un bordo o una connessione di bordo della mesh di navigazione, è possibile che le posizioni del percorso siano a volte leggermente "indietro" rispetto all'orientamento attuale degli attori. Ciò accade a causa di problemi di precisione e non è sempre possibile evitarlo. Ciò è solitamente un problema evidente solo se gli attori vengono ruotati istantaneamente per rivolgersi verso la posizione attuale nel percorso.
Evitamento di NavigationAgent
Questa sezione spiega come utilizzare la funzione di evitamento della navigazione specifica dei NavigationAgent.
Affinché i NavigationAgent possano utilizzare la funzionalità di evitamento, è necessario impostare la proprietà avoidance_enabled su true.
Il segnale velocity_computed del nodo NavigationAgent deve essere connesso per ricevere il risultato del calcolo della velocità sicura.
Imposta velocity del nodo NavigationAgent in _physics_process() per aggiornare l'agente con la velocità attuale del nodo genitore dell'agente.
Quando l'evitamento è abilitato sull'agente, il vettore safe_velocity verrà ricevuto con il segnale velocity_computed a ogni frame di fisica. Questo vettore di velocità si dovrebbe utilizzare per muovere il nodo padre del NavigationAgent al fine di evitare collisioni con altri agenti che utilizzano l'evitamento o con ostacoli di evitamento.
Nota
Nel calcolo dell'evitamento saranno considerati solo gli altri agenti presenti sulla stessa mappa che sono a loro volta registrati per l'evitamento.
Nota
Il nodo NavigationAgent deve essere dotato dell'attributo target_position, anche se lo si usa solo per l'evitamento. Altrimenti, la safe_velocity ricevuta dal segnale velocity_computed sarà sempre il vettore zero.
Le seguenti proprietà di NavigationAgent sono rilevanti per l'evitamento:
La proprietà
heightè disponibile solo in 3D. L'altezza, insieme alla posizione globale attuale dell'agente sull'asse y, determina il posizionamento verticale dell'agente nella simulazione di evitamento. Gli agenti che utilizzano l'evitamento 2D ignoreranno automaticamente gli altri agenti o gli ostacoli che si trovano sotto o sopra di loro.La proprietà
radiuscontrolla la dimensione del cerchio (o nel caso del 3D una sfera) di evitamento attorno all'agente. Quest'area descrive il corpo dell'agente e non la distanza della manovra di evitamento.La proprietà
neighbor_distancecontrolla il raggio di ricerca dell'agente quando cerca altri agenti da evitare. Un valore inferiore riduce i costi di elaborazione.La proprietà
max_neighborscontrolla quanti altri agenti sono considerati nel calcolo dell'evitamento se tutti hanno un raggio sovrapposto. Un valore inferiore riduce i costi di elaborazione, ma un valore troppo basso potrebbe comportare che gli agenti ignorino l'evitamento.Le proprietà
time_horizon_agentsetime_horizon_obstaclescontrollano il tempo di previsione dell'evitamento per altri agenti o ostacoli in secondi. Quando gli agenti calcolano le loro velocità sicure, scelgono velocità che è possibile mantenere per questo numero di secondi senza scontrarsi con un altro oggetto di evitamento. Il tempo di previsione dovrebbe essere mantenuto il più basso possibile, poiché gli agenti rallenteranno le loro velocità per evitare collisioni in tale intervallo di tempo.La proprietà
max_speedcontrolla la velocità massima consentita per il calcolo dell'evitamento degli agenti. Se i genitori degli agenti si muovono più velocemente di questo valore, lasafe_velocitydell'evitamento potrebbe non essere precisa abbastanza per evitare la collisione.La proprietà
use_3d_avoidancepassa l'agente tra l'evitamento 2D (asse xz) e l'evitamento 3D (asse xyz) al prossimo aggiornamento. Si noti che l'evitamento 2D e l'evitamento 3D sono eseguiti in simulazioni separate, quindi gli agenti divisi tra i due non si influenzano a vicenda.Le proprietà
avoidance_layerseavoidance_masksono maschere di bit simili, ad esempio, agli strati di fisica. Gli agenti eviteranno solo gli altri oggetti di evitamento che si trovano su uno strato di evitamento che corrisponde ad almeno uno dei bit della loro maschera di evitamento.La proprietà
avoidance_priorityfa sì che gli agenti con priorità più alta ignorino gli agenti con priorità inferiore. Questo può servire per dare maggiore importanza a determinati agenti nella simulazione, ad esempio personaggi importanti non giocanti, senza dover cambiare continuamente tutti i loro strati o maschere di evitamento.
L'evitamento avviene in uno spazio a sé stante e non riceve informazioni dalle mesh di navigazione o dalle collisioni fisiche. Dietro le quinte, gli agenti di evitamento sono solo cerchi con raggi diversi su un piano 2D o sfere in uno spazio 3D altrimenti vuoto. È possibile utilizzare gli NavigationObstacle per aggiungere alcuni vincoli ambientali alla simulazione di evitamento, consulta Utilizzo degli NavigationObstacle.
Nota
L'evitamento non influisce sulla ricerca del percorso. Dovrebbe essere considerato un'opzione in più per gli oggetti in costante movimento che non si possono (ri)preparare efficientemente su una mesh di navigazione per poterli aggirare.
Nota
RVO avoidance makes implicit assumptions about natural agent behavior. E.g. that agents move on reasonable passing sides that can be assigned when they encounter each other. This means that very clinical avoidance test scenarios will commonly fail. E.g. agents moved directly against each other with perfect opposite velocities will fail because the agents can not get their passing sides assigned.
Utilizzare la proprietà avoidance_enabled degli agenti di navigazione è l'opzione preferibile per attivare o disattivare l'evitamento. I seguenti frammenti di codice si possono usare per attivare o disattivare l'evitamento sugli agenti, creare o eliminare callback di evitamento o cambiare le modalità di evitamento.
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() { }
}
Modelli di script per NavigationAgent
Le sezioni seguenti forniscono schemi di script per i nodi comunemente utilizzati con i 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;
}
}