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...
Verwendung des NavigationServer
2D und 3D Versionen des NavigationServers sind als NavigationServer2D und NavigationServer3D verfügbar.
Kommunikation mit dem NavigationServer
Mit dem NavigationServer zu arbeiten bedeutet, Parameter für eine Abfrage vorzubereiten, die an den NavigationServer gesendet werden kann, um Daten zu aktualisieren oder abzufragen.
Um die internen NavigationServer-Objekte wie Karten, Regionen und Agenten zu referenzieren, werden RIDs als Identifikationsnummern verwendet. Jeder navigationsbezogene Node im Szenenbaum hat eine Funktion, um die RID für diesen Node zurückzugeben.
Threading und Synchronisierung
Der NavigationServer aktualisiert nicht jede Änderung sofort, sondern wartet bis zum Ende des Physik-Frames, um alle Änderungen zusammen zu synchronisieren.
Das Warten auf die Synchronisierung ist erforderlich, um Änderungen auf alle Maps, Regionen und Agenten anzuwenden. Die Synchronisation wird durchgeführt, weil einige Aktualisierungen wie die Neuberechnung der gesamten Navigations-Map sehr teuer sind und aktualisierte Daten von allen anderen Objekten erfordern. Außerdem verwendet der NavigationServer standardmäßig einen Threadpool für einige Funktionen wie die Ausweichberechnung zwischen Agenten.
Für die meisten get() Funktionen, die nur Daten vom NavigationServer abfragen, ohne Änderungen vorzunehmen, ist eine Wartezeit nicht erforderlich. Beachten Sie, dass nicht alle Daten Änderungen berücksichtigen, die im selben Frame gemacht wurden. Wenn z.B. ein Ausweichagent die Navigations-Map in diesem Frame geändert hat, wird die agent_get_map() Funktion immer noch die alte Map vor der Synchronisation zurückgeben. Die Ausnahme hiervon sind Nodes, die ihre Werte intern speichern, bevor sie die Aktualisierung an den NavigationServer senden. Wenn ein Getter auf einem Node für einen Wert verwendet wird, der im selben Frame aktualisiert wurde, wird er den bereits aktualisierten Wert zurückgeben, der auf dem Node gespeichert ist.
Der NavigationServer ist thread-safe, da er alle API-Aufrufe, die Änderungen vornehmen wollen, in eine Warteschlange stellt, die in der Synchronisierungsphase ausgeführt wird. Die Synchronisierung für den NavigationServer erfolgt in der Mitte des Physik-Frames, nachdem die Szeneneingabe von Skripten und Nodes abgeschlossen ist.
Bemerkung
Wichtig ist, dass die meisten NavigationServer-Änderungen nicht sofort, sondern erst nach dem nächsten Physik-Frame wirksam werden. Dies gilt auch für alle Änderungen, die durch navigationsbezogene Nodes im Szenenbaum oder durch Skripte vorgenommen werden.
Bemerkung
Alle Setter- und Löschfunktionen müssen synchronisiert werden.
Unterschiede zwischen 2D- und 3D-NavigationServern
NavigationServer2D and NavigationServer3D are equivalent in functionality for their dimension.
Technisch ist es möglich, die Tools zur Erstellung von Navigationsmeshes in einer Dimension für die andere Dimension zu verwenden, z.B. das Backen eines 2D-Navigationsmeshes mit dem 3D NavigationMesh bei Verwendung von flacher 3D-Quellgeometrie oder die Erstellung von flachen 3D-Navigationsmeshes mit den Polygonumriss-Zeichentools von NavigationRegion2D und NavigationPolygons.
Warten auf die Synchronisierung
Beim Start des Spiels, einer neuen Szene oder einer prozeduralen Navigationsänderung gibt jede Pfadabfrage an einen NavigationServer "leer" oder "falsch" zurück.
Die Navigations-Map ist zu diesem Zeitpunkt noch leer oder nicht aktualisiert. Alle Nodes aus dem Szenenbaum müssen zunächst ihre navigationsbezogenen Daten auf den NavigationServer hochladen. Jede hinzugefügte oder geänderte Map, Region oder Agent muss beim NavigationServer registriert werden. Danach benötigt der NavigationServer einen Physik-Frame zur Synchronisation, um die Maps, Regionen und Agenten zu aktualisieren.
Ein Workaround besteht darin, eine benutzerdefinierte Setup-Funktion zeitversetzt aufzurufen (damit alle Nodes bereit sind). Die Setup-Funktion nimmt alle Navigationsänderungen vor, z.B. das Hinzufügen von prozeduralen Elementen. Danach wartet die Funktion auf den nächsten Physik-Frame, bevor sie mit der Pfadabfrage fortfährt.
extends Node3D
func _ready():
# Use call deferred to make sure the entire scene tree nodes are setup
# else await on 'physics_frame' in a _ready() might get stuck.
custom_setup.call_deferred()
func custom_setup():
# Create a new navigation map.
var map: RID = NavigationServer3D.map_create()
NavigationServer3D.map_set_up(map, Vector3.UP)
NavigationServer3D.map_set_active(map, true)
# Create a new navigation region and add it to the map.
var region: RID = NavigationServer3D.region_create()
NavigationServer3D.region_set_transform(region, Transform3D())
NavigationServer3D.region_set_map(region, map)
# Create a procedural navigation mesh for the region.
var new_navigation_mesh: NavigationMesh = NavigationMesh.new()
var vertices: PackedVector3Array = PackedVector3Array([
Vector3(0, 0, 0),
Vector3(9.0, 0, 0),
Vector3(0, 0, 9.0)
])
new_navigation_mesh.set_vertices(vertices)
var polygon: PackedInt32Array = PackedInt32Array([0, 1, 2])
new_navigation_mesh.add_polygon(polygon)
NavigationServer3D.region_set_navigation_mesh(region, new_navigation_mesh)
# Wait for NavigationServer sync to adapt to made changes.
await get_tree().physics_frame
# Query the path from the navigation server.
var start_position: Vector3 = Vector3(0.1, 0.0, 0.1)
var target_position: Vector3 = Vector3(1.0, 0.0, 1.0)
var optimize_path: bool = true
var path: PackedVector3Array = NavigationServer3D.map_get_path(
map,
start_position,
target_position,
optimize_path
)
print("Found a path!")
print(path)
using Godot;
public partial class MyNode3D : Node3D
{
public override void _Ready()
{
// Use call deferred to make sure the entire scene tree nodes are setup
// else await on 'physics_frame' in a _Ready() might get stuck.
CallDeferred(MethodName.CustomSetup);
}
private async void CustomSetup()
{
// Create a new navigation map.
Rid map = NavigationServer3D.MapCreate();
NavigationServer3D.MapSetUp(map, Vector3.Up);
NavigationServer3D.MapSetActive(map, true);
// Create a new navigation region and add it to the map.
Rid region = NavigationServer3D.RegionCreate();
NavigationServer3D.RegionSetTransform(region, Transform3D.Identity);
NavigationServer3D.RegionSetMap(region, map);
// Create a procedural navigation mesh for the region.
var newNavigationMesh = new NavigationMesh()
{
Vertices =
[
new Vector3(0.0f, 0.0f, 0.0f),
new Vector3(9.0f, 0.0f, 0.0f),
new Vector3(0.0f, 0.0f, 9.0f),
],
};
int[] polygon = [0, 1, 2];
newNavigationMesh.AddPolygon(polygon);
NavigationServer3D.RegionSetNavigationMesh(region, newNavigationMesh);
// Wait for NavigationServer sync to adapt to made changes.
await ToSignal(GetTree(), SceneTree.SignalName.PhysicsFrame);
// Query the path from the navigation server.
var startPosition = new Vector3(0.1f, 0.0f, 0.1f);
var targetPosition = new Vector3(1.0f, 0.0f, 1.0f);
Vector3[] path = NavigationServer3D.MapGetPath(map, startPosition, targetPosition, optimize: true);
GD.Print("Found a path!");
GD.Print((Variant)path);
}
}
Server-Ausweich-Callbacks
Wenn RVO-Ausweichagenten für Ausweich-Callbacks registriert sind, sendet der NavigationServer ihre velocity_computed-Signale kurz vor der Synchronisation mit dem PhysicsServer.
Weitere Informationen zu NavigationAgents finden Sie unter Verwenden von NavigationAgents.
Die vereinfachte Ausführungsreihenfolge für NavigationAgents, die Ausweichmanöver verwenden:
Physik-Frame beginnt.
_physics_process(delta).Die Property``Velocity`` ist auf dem NavigationAgent-Node eingestellt.
Der Agent sendet Geschwindigkeit und Position an den NavigationServer.
Der NavigationServer wartet auf die Synchronisierung.
Der NavigationServer synchronisiert und berechnet die Ausweichgeschwindigkeiten für alle registrierten Ausweichagenten.
Der Navigationsserver sendet einen sicheren Geschwindigkeitsvektor mit Signalen für jeden registrierten Ausweichagenten.
Agenten empfangen das Signal und bewegen ihren Parent z.B. mit
move_and_slideoderlinear_velocity.PhysicsServer synchronisiert.
Physik-Frame endet.
Daher ist das Bewegen eines Physik-Body-Akteurs in der Callback-Funktion mit der sicheren Geschwindigkeit vollkommen thread- und physik-safe, da alles innerhalb desselben Physik-Frames geschieht, bevor der PhysicsServer die Änderungen festlegt und seine eigenen Berechnungen durchführt.