Verwenden von NavigationAgents
NavigationsAgents sind Hilfs-Nodes, die Funktionalität für Wegfindung, Wegverfolgung und Agentenausweichen für einen von Node2D/3D erbenden Parent-Node kombinieren. Sie erleichtern Anfängern gemeinsame Aufrufe der NavigationServer-API im Auftrag des übergeordneten Node-Akteurs auf bequemere Weise.
2D und 3D-Versionen von NavigationAgents sind als NavigationAgent2D bzw. NavigationAgent3D verfügbar.
Neue NavigationAgent-Nodes werden automatisch der Standard-Navigations-Map auf der World2D/World3D hinzugefügt.
NavigationsAgent Nodes sind optional und keine zwingende Voraussetzung für die Nutzung des Navigationssystems. Ihre gesamte Funktionalität kann durch Skripte und direkte Aufrufe zur NavigationServer API ersetzt werden.
NavigationAgent Wegfindung
NavigationAgents fragen einen neuen Navigationspfad auf ihrer aktuellen Navigations-Map ab, wenn ihre target_position auf eine globale Position gesetzt wird.
Das Ergebnis der Wegfindung kann mit den folgenden Propertys beeinflusst werden.
Die Bitmaske
navigation_layerskann verwendet werden, um die Navigations-Meshes einzuschränken, die der Agent verwenden kann.Der
Wegfindung-Algorithmussteuert, wie die Wegfindung durch die Polygone des Navigation-Meshs bei der Wegfindung abläuft.path_postprocessinglegt fest, ob oder wie der rohe Pfadkorridor, der durch die Wegfindung gefunden wurde, verändert wird, bevor er zurückgegeben wird.Die
path_metadata_flagsermöglichen das Sammeln zusätzlicher Metadaten des Pfadpunktes, die vom Pfad zurückgegeben werden.The
simplify_pathandsimplify_epsilonproperties can be used to remove less critical points from the path.
Warnung
Durch die Deaktivierung von Pfad-Meta-Flags werden die entsprechenden Signalemissionen auf dem Agenten deaktiviert.
NavigationAgent Wegverfolgung
Nachdem eine Zielposition für den Agenten festgelegt wurde, kann die nächste Position, die auf dem Pfad zu verfolgen ist, mit der Funktion get_next_path_position() ermittelt werden.
Once the next path position is received, move the parent actor node of the agent towards this path position with your own movement code.
Bemerkung
Das Navigationssystem verschiebt niemals den übergeordneten Node eines NavigationAgent. Die Bewegung liegt vollständig in den Händen der Benutzer und ihrer benutzerdefinierten Skripte.
NavigationAgents haben ihre eigene interne Logik, um mit dem aktuellen Pfad fortzufahren und Aktualisierungen anzufordern.
Die Funktion get_next_path_position() ist für die Aktualisierung vieler interner Zustände und Propertys des Agenten verantwortlich. Die Funktion sollte wiederholt einmal in jedem physics_process aufgerufen werden, bis is_navigation_finished() mitteilt, dass der Pfad beendet ist. Die Funktion sollte nicht aufgerufen werden, nachdem die Zielposition oder das Pfadende erreicht wurde, da es den Agenten aufgrund der wiederholten Pfadaktualisierungen zum Jittern bringen kann. Prüfen Sie immer sehr früh im Skript mit is_navigation_finished(), ob der Pfad bereits beendet ist.
The following distance properties influence the path following behavior.
At
path_desired_distancefrom the next path position, the agent advances its internal path index to the subsequent next path position.At
target_desired_distancefrom the target path position, the agent considers the target position to be reached and the path at its end.At
path_max_distancefrom the ideal path to the next path position, the agent requests a new path because it was pushed too far off.
Die wichtigen Aktualisierungen werden alle mit der Funktion get_next_path_position() ausgelöst, wenn sie in _physics_process() aufgerufen wird.
NavigationAgents können mit process verwendet werden, sind aber immer noch auf eine einzige Aktualisierung beschränkt, die in physics_process stattfindet.
Skriptbeispiele für verschiedene Nodes, die häufig mit NavigationAgents verwendet werden, finden Sie weiter unten.
Häufige Probleme bei der Wegverfolgung
Beim Schreiben von Skripten für Agentenbewegungen gibt es einige häufige Benutzerprobleme und wichtige Hinweise zu beachten.
- Der Pfad wird leer zurückgegeben
Wenn ein Agent einen Pfad vor der Synchronisation der Navigations-Map abfragt, z.B. in einer
_ready()Funktion, könnte der Pfad leer zurückkommen. In diesem Fall wird die Funktionget_next_path_position()die gleiche Position wie der übergeordnete Node des Agenten zurückgeben und der Agent wird das Ende des Pfades als erreicht betrachten. Dies kann durch einen verzögerten Aufruf oder einen Callback behoben werden, z.B. durch Warten auf das Signal "Navigations-Map geändert".
- Der Agent zappelt zwischen zwei Positionen
Dies wird in der Regel durch sehr häufige Pfadaktualisierungen in jedem einzelnen Frame verursacht, entweder absichtlich oder aus Versehen (z.B. wenn die maximale Pfaddistanz zu kurz eingestellt ist). Die Wegfindung muss die nächstgelegene Position im Mesh finden, die für die Navigation gültig ist. Wird bei jedem Einzelbild ein neuer Pfad angefordert, kann es passieren, dass die ersten Pfadpositionen ständig vor und hinter der aktuellen Position des Agenten wechseln, so dass dieser zwischen den beiden Positionen hin und her zappelt.
- Der Agent bewegt sich manchmal rückwärts
Wenn sich ein Agent sehr schnell bewegt, kann es passieren, dass er über den path_desired_distance-Check hinausschießt, ohne jemals den Pfadindex zu erhöhen. Dies kann dazu führen, dass der Agent zu dem Wegpunkt zurückgeht, der nun hinter ihm liegt, bis er die Distanzprüfung besteht, um den Pfadindex zu erhöhen. Erhöhen Sie die gewünschten Entfernungen entsprechend Ihrer Agentengeschwindigkeit und Aktualisierungsrate, um dieses Problem zu beheben, und sorgen Sie für ein ausgewogeneres Mesh-Polygon-Layout mit nicht zu vielen Polygonkanten, die auf engem Raum zusammengepfercht sind.
- Der Agent schaut manchmal für die Dauer eines Frames rückwärts
Ähnlich wie bei Agenten, die zwischen zwei Positionen zappeln, wird dies normalerweise durch sehr häufige Pfadaktualisierungen in jedem einzelnen Frame verursacht. Abhängig vom Layout des Navigations-Meshs und insbesondere wenn ein Agent direkt über einer Kante oder einer Kantenverbindung des Navigations-Meshs platziert ist, kann es vorkommen, dass die Pfadpositionen etwas "hinter" der aktuellen Ausrichtung des Agenten liegen. Dies geschieht aufgrund von Präzisionsproblemen und kann nicht immer vermieden werden. Dies ist in der Regel nur dann ein sichtbares Problem, wenn die Akteure sofort in Richtung der aktuellen Pfadposition gedreht werden.
NavigationAgent-Ausweichen
In diesem Abschnitt wird erklärt, wie Sie die Navigations-Ausweichen für NavigationAgents nutzen können.
Damit NavigationAgents das Ausweich-Feature nutzen können, muss das Property enable_avoidance auf true gesetzt werden.
Das Signal velocity_computed des Nodes NavigationAgent muss verbunden sein, um das Ergebnis der sicheren Geschwindigkeitsberechnung zu erhalten.
Setzen Sie velocity auf dem NavigationAgent-Node in _physics_process(), um den Agenten mit der aktuellen Geschwindigkeit des Parent-Node zu aktualisieren.
Solange das Ausweich-Feature für den Agenten aktiviert ist, wird der Vektor safe_velocity zusammen mit dem velocity_computed-Signal in jedem Physik-Frame empfangen. Dieser Geschwindigkeitsvektor sollte verwendet werden, um den Parent-Node des NavigationAgent zu bewegen, um Kollisionen mit anderen ausweichenden Agenten oder Ausweich-Hindernissen zu vermeiden.
Bemerkung
Nur andere Agenten auf der gleichen Map, die selbst zum Ausweichen eingetragen sind, werden bei der Ausweichberechnung berücksichtigt.
Die folgenden Propertys von NavigationAgent sind für zum Ausweichen relevant:
Die Property
Höheist nur in 3D verfügbar. Die Höhe bestimmt zusammen mit der aktuellen globalen y-Achsenposition des Agenten die vertikale Platzierung des Agenten in der Ausweichsimulation. Agenten, die das 2D-Ausweichverfahren verwenden, ignorieren automatisch andere Agenten oder Hindernisse, die sich unter oder über ihnen befinden.Das Property
Radiussteuert die Größe des Ausweichkreises (in 3D-Fall einer Kugel) um den Agenten. Dieser Bereich beschreibt den Body des Agenten und nicht die Distanz des Ausweichmanövers.Die Property
neighbor_distancesteuert den Suchradius des Agenten bei der Suche nach anderen Agenten, denen ausgewichen werden soll. Ein kleinerer Wert verringert die Berechnungskosten.Die Property
max_neighborssteuert, wie viele andere Agenten bei der Ausweichberechnung berücksichtigt werden, wenn sie alle einen überlappenden Radius haben. Ein niedriger Wert reduziert die Verarbeitungskosten, aber ein zu niedriger Wert kann dazu führen, dass Agenten as Ausweichen ignorieren.Die Propertys
time_horizon_agentsundtime_horizon_obstaclessteuern die Ausweichvorhersagezeit für andere Agenten oder Hindernisse in Sekunden. Wenn Agenten ihre sicheren Geschwindigkeiten berechnen, wählen sie Geschwindigkeiten, die für diese Anzahl von Sekunden gehalten werden können, ohne mit einem anderen Ausweichobjekt zu kollidieren. Die Vorhersagezeit sollte so niedrig wie möglich gehalten werden, da die Agenten ihre Geschwindigkeit verlangsamen werden, um eine Kollision innerhalb dieses Zeitrahmens zu vermeiden.Die Property
max_speedsteuert die maximale Geschwindigkeit, die für die Ausweichberechnung des Agenten erlaubt ist. Wenn sich die Parents des Agenten schneller als dieser Wert bewegen, könnte die Ausweichberechnungsafe_velocitynicht genau genug sein, um eine Kollision zu vermeiden.Die Property
use_3d_avoidanceschaltet den Agenten bei der nächsten Aktualisierung zwischen der 2D-Ausweichen (xz-Achse) und der 3D-Ausweichen (xyz-Achse) um. Beachten Sie, dass das 2D-Ausweichen und das 3D-Ausweichen in separaten Ausweichsimulationen ablaufen, so dass sich Agenten, die zwischen diesen beiden Methoden wechseln, nicht gegenseitig beeinflussen.Die Propertys
avoidance_layersundavoidance_masksind Bitmasken, ähnlich wie z.B. bei physikalischen Ebenen. Agenten weichen nur anderen Ausweichobjekten aus, die sich auf einem Ausweich-Layer befinden, der mit mindestens einem ihrer eigenen Ausweichmasken-Bits übereinstimmt.The
avoidance_prioritymakes agents with a higher priority ignore agents with a lower priority. This can be used to give certain agents more importance in the avoidance simulation, e.g. important non-playable characters, without constantly changing their entire avoidance layers or mask.
Ausweichmanöver existieren in ihrem eigenen Raum und haben keine Informationen von Navigations-Meshes oder physikalischen Kollisionen. Unter der Haube sind Ausweichagenten einfach Kreise mit verschiedenen Radien auf einer flachen 2D-Ebene oder Kugeln in einem ansonsten leeren 3D-Raum. NavigationObstacles können verwendet werden, um einige Umgebungseinschränkungen zur Ausweichsimulation hinzuzufügen, siehe Verwenden von NavigationObstacles.
Bemerkung
Das Ausweichen hat keinen Einfluss auf die Wegfindung. Es sollte als zusätzliche Option für sich ständig bewegende Objekte betrachtet werden, die nicht effizient in ein Navigations-Mesh (um)gebacken werden können, um sich um sie herum zu bewegen.
Bemerkung
Das RVO-Ausweichen geht von impliziten Annahmen über das natürliche Verhalten von Agenten aus. Zum Beispiel, dass sich Agenten auf vernünftigen, zuordenbaren Seiten bewegen, wenn sie einander begegnen. Dies bedeutet, dass sehr klinische Ausweich-Szenarien in der Regel scheitern werden. So werden z.B. Agenten, die sich mit vollkommen entgegengesetzten Geschwindigkeiten direkt gegeneinander bewegen, scheitern, weil die Agenten ihre Ausweichseiten nicht zugewiesen bekommen können.
Die Verwendung der NavigationAgent-Property enable_avoidance ist die bevorzugte Option zum Ein- und Ausschalten des Ausweichmodus. Die folgenden Codeschnipsel können verwendet werden, um das Ausweichen bei Agenten zu aktivieren, Ausweich-Callbacks zu erstellen oder zu löschen oder den Ausweichmodus zu wechseln.
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-Skriptvorlagen
Die folgenden Abschnitte enthalten Skriptvorlagen für Nodes, die häufig mit NavigationAgents verwendet werden.
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 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):
# 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
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)
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;
}
}