Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Verwendung des NavigationServer

2D und 3D Versionen des NavigationServers sind als NavigationServer2D und NavigationServer3D verfügbar.

Sowohl 2D als auch 3D verwenden denselben NavigationServer, wobei der NavigationServer3D der primäre Server ist. Der NavigationServer2D ist ein Frontend, das 2D-Positionen in 3D-Positionen und zurück konvertiert. Daher ist es durchaus möglich (wenn auch etwas umständlich), ausschließlich die NavigationServer3D-API für die 2D-Navigation zu verwenden.

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.

Die folgenden Funktionen werden nur in der Synchronisationsphase ausgeführt:

  • map_set_active()

  • map_set_up()

  • map_set_cell_size()

  • map_set_edge_connection_margin()

  • region_set_map()

  • region_set_transform()

  • region_set_enter_cost()

  • region_set_travel_cost()

  • region_set_navigation_layers()

  • region_set_navigation_mesh()

  • agent_set_map()

  • agent_set_neighbor_dist()

  • agent_set_max_neighbors()

  • agent_set_time_horizon()

  • agent_set_radius()

  • agent_set_max_speed()

  • agent_set_velocity()

  • agent_set_target_velocity()

  • agent_set_position()

  • agent_set_ignore_y()

  • agent_set_callback()

  • free()

Unterschiede zwischen 2D- und 3D-NavigationServern

NavigationServer2D und NavigationServer3D sind in ihrer Funktionalität für ihre Dimension gleichwertig und verwenden beide den gleichen NavigationServer hinter der Bühne.

Streng technisch gesehen ist ein NavigationServer2D ein Mythos. Der NavigationServer2D ist ein Frontend, das die Konvertierung von Vector2(x, y) nach Vector3(x, 0.0, z) und zurück für die NavigationServer3D API erleichtert. 2D benutzt eine flache 3D Mesh-Wegfindung und der NavigationServer2D erleichtert die Konvertierungen. Wenn ein Guide nur NavigationServer ohne das 2D oder 3D Suffix benutzt, funktioniert es normalerweise für beide Server, indem Vector2(x, y) mit Vector3(x, 0.0, z) oder umgekehrt ausgetauscht wird.

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.

Jede mit der NavigationServer2D-API erstellte RID funktioniert auch mit der NavigationServer3D-API, und sowohl 2D- als auch 3D-Ausweichagenten können auf derselben Map existieren.

Bemerkung

Regionen, die in 2D und 3D erstellt wurden, verschmelzen ihre Navigations-Meshes, wenn sie auf derselben Map platziert werden und die Verschmelzungsbedingungen gelten. Der NavigationServer unterscheidet nicht zwischen NavigationRegion2D und NavigationRegion3D-Nodes, da beide Regionen auf dem Server sind. Standardmäßig werden diese Nodes auf verschiedenen Navigations-Maps registriert, so dass die Zusammenführung nur dann erfolgen kann, wenn die Maps manuell geändert werden, z. B. mit Skripten.

Akteure mit aktivierter Ausweichfunktion meiden sowohl 2D- als auch 3D-Ausweichagenten, wenn sie sich auf der gleichen Map befinden.

Warnung

Es ist nicht möglich, NavigationServer2D zu verwenden, während 3D auf einem Godot Custom Build deaktiviert ist.

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 / yield on 'physics_frame' in a _ready() might get stuck
    call_deferred("custom_setup")

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, Transform())
    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 navigationserver
    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)

Server-Ausweich-Callbacks

Wenn RVO-Vermeidungsagenten 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).

  • set_velocity() auf NavigationAgent-Node.

  • 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_slide oder linear_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.