Up to date
This page is up to date for Godot 4.2
.
If you still find outdated information, please open an issue.
Verwenden von Navigations-Meshes¶
2D- und 3D-Versionen des Navigations-Mesh sind als NavigationPolygon bzw. NavigationMesh verfügbar.
Bemerkung
Ein Navigations-Mesh beschreibt nur einen durchquerbaren Bereich für die zentrale Position eines Agenten. Alle Radiuswerte, die ein Agent haben kann, werden ignoriert. Wenn Sie möchten, dass die Wegfindung die (Kollisions-) Größe eines Agenten berücksichtigt, müssen Sie das Navigations-Mesh entsprechend verkleinern.
Die Navigation funktioniert unabhängig von anderen Teilen der Engine wie Rendering oder Physik. Navigations-Meshes sind die einzigen Dinge, die bei der Wegfindung berücksichtigt werden, d.h. visuelle Elemente und Collision Shapes werden vom Navigationssystem komplett ignoriert. Wenn Sie andere Daten (wie z.B. das Bildmaterial) bei der Wegfindung berücksichtigen wollen, müssen Sie Ihre Navigations-Meshes entsprechend anpassen. Der Prozess der Berücksichtigung von Navigationsbeschränkungen in Navigations-Meshes wird gemeinhin als "Navigations-Mesh-Backen" bezeichnet.
Wenn Sie beim Verfolgen von Navigationspfaden auf Clipping- oder Kollisionsprobleme stoßen, denken Sie immer daran, dass Sie dem Navigationssystem über ein geeignetes Mesh mitteilen müssen, was Sie vorhaben. Das Navigationssystem selbst wird nie wissen, ob es sich um einen Baum, einen Felsen, eine Wand oder ein visuelles Mesh handelt, denn es weiß nur: "mir wurde gesagt, dass ich diesen Weg sicher gehen kann, weil er auf einem Navigations-Mesh liegt".
Das Backen eines Navigations-Meshes kann entweder durch die Verwendung einer NavigationRegion2D oder NavigationRegion3D, oder durch die direkte Verwendung der NavigationServer2D- und NavigationServer3D-API erreicht werden.
Backen eines Navigations-Meshes mit einer NavigationRegion¶
Das Backen des Navigations-Meshs wird durch den NavigationRegion-Node zugänglicher gemacht. Beim Backen mit einem NavigationRegion-Node werden die einzelnen Schritte des Parsens, Backens und der Regionsaktualisierung in einer Funktion zusammengefasst.
Die Nodes sind in 2D und 3D als NavigationRegion2D bzw. NavigationRegion3D verfügbar.
Wenn ein NavigationRegion2D-Node im Editor ausgewählt wird, erscheinen in der oberen Leiste des Editors die Backoptionen sowie die Polygonzeichenwerkzeuge.
Damit die Region funktioniert, muss eine NavigationPolygon-Ressource hinzugefügt werden.
Die Propertys zum Analysieren und Backen eines Navigations-Mesh sind dann Teil der verwendeten Ressource und können im Ressourceninspektor gefunden werden.
Das Ergebnis des Parsens der Quellgeometrie kann mit den folgenden Propertys beeinflusst werden.
Der
parsed_geometry_type
, der filtert, ob visuelle Objekte oder physikalische Objekte oder beide aus dem SceneTree geparst werden sollen. Für weitere Details darüber, welche Objekte geparst werden und wie, siehe den Abschnitt über das Parsen der Quellgeometrie weiter unten.Die
collision_mask
filtert, welche physischen Kollisionsobjekte einbezogen werden, wenn derparsed_geometry_type
statische Collider enthält.Der
source_geometry_mode
, der festlegt, auf welchen Nodes das Parsen beginnen soll und wie der SceneTree durchlaufen werden soll.Der
source_geometry_group_name
wird verwendet, wenn nur eine bestimmte Node-Gruppe geparst werden soll. Hängt vom gewähltensource_geometry_mode
ab.
Nachdem die Quellgeometrie hinzugefügt wurde, kann das Ergebnis des Backens mit den folgenden Propertys gesteuert werden.
Der Parameter
cell_size
legt die Größe des Rasters fest und sollte mit der Größe der Navigations-Map übereinstimmen.Der
agent_radius
verkleinert das gebackene Mesh, um genügend Spielraum für die Größe des Agenten (Kollisionen) zu haben.
Das Backen der NavigationRegion2D kann auch zur Laufzeit mit Skripten verwendet werden.
var on_thread: bool = true
bake_navigation_polygon(on_thread)
Zum schnellen Testen des 2D-Backens mit Default-Einstellungen:
Fügen Sie ein NavigationRegion2D hinzu.
Fügen Sie eine NavigationPolygon-Ressource zur NavigationRegion2D hinzu.
Fügen Sie ein Polygon2D unterhalb der NavigationRegion2D ein.
Zeichnen Sie 1 NavigationPolygon-Kontur mit dem ausgewählten NavigationRegion2D-Zeichenwerkzeug.
Zeichnen Sie 1 Polygon2D-Umriss innerhalb des NavigationPolygon-Umrisses mit dem ausgewählten Polygon2D-Zeichenwerkzeug.
Klicken Sie auf den Editor Back-Button und ein Navigations-Mesh sollte erscheinen.
Wenn ein NavigationRegion3D-Node im Editor ausgewählt wird, werden in der oberen Leiste des Editors Backoptionen angezeigt.
Damit die Region funktioniert, muss eine NavigationMesh-Ressource hinzugefügt werden.
Die Propertys zum Analysieren und Backen eines Navigations-Mesh sind dann Teil der verwendeten Ressource und können im Ressourceninspektor gefunden werden.
Das Ergebnis des Parsens der Quellgeometrie kann mit den folgenden Propertys beeinflusst werden.
Der
parsed_geometry_type
, der filtert, ob visuelle Objekte oder physikalische Objekte oder beide aus dem SceneTree geparst werden sollen. Für weitere Details darüber, welche Objekte geparst werden und wie, siehe den Abschnitt über das Parsen der Quellgeometrie weiter unten.Die
collision_mask
filtert, welche physischen Kollisionsobjekte einbezogen werden, wenn derparsed_geometry_type
statische Collider enthält.Der
source_geometry_mode
, der festlegt, auf welchen Nodes das Parsen beginnen soll und wie der SceneTree durchlaufen werden soll.Der
source_geometry_group_name
wird verwendet, wenn nur eine bestimmte Node-Gruppe geparst werden soll. Hängt vom gewähltensource_geometry_mode
ab.
Nachdem die Quellgeometrie hinzugefügt wurde, kann das Ergebnis des Backens mit den folgenden Propertys gesteuert werden.
Die
cell_size
undcell_height
legen die Größe des Rasterisierungs-Voxel-Rasters fest und sollten der Größe der Navigations-Map entsprechen.Der
agent_radius
verkleinert das gebackene Mesh, um genügend Spielraum für die Größe des Agenten (Kollisionen) zu haben.Die
agent_height
schließt Bereiche aus dem Mesh aus, in die der Agent nicht mehr hineinpasst.Die
agent_max_climb
undagent_max_slope
entfernen Bereiche, in denen der Höhenunterschied zwischen benachbarten Voxeln zu groß ist, oder deren Oberfläche zu steil ist.
Warnung
Eine zu kleine cell_size
oder cell_height
kann so viele Voxel erzeugen, dass das Spiel einfriert oder sogar abstürzt.
Das Backen der NavigationRegion3D kann auch zur Laufzeit mit Skripten verwendet werden.
var on_thread: bool = true
bake_navigation_mesh(on_thread)
Zum schnellen Testen des 3D-Backens mit Default-Einstellungen:
Fügen Sie ein NavigationRegion3D hinzu.
Fügen Sie eine NavigationMesh-Ressource zur NavigationRegion3D hinzu.
Fügen Sie ein MeshInstance3D unterhalb der NavigationRegion3D hinzu.
Fügen Sie der MeshInstance3D ein PlaneMesh hinzu.
Klicken Sie auf den Editor Back-Button und ein Navigations-Mesh sollte erscheinen.
Backen eines Navigations-Meshes mit dem NavigationServer¶
Die NavigationServer2D und NavigationServer3D haben API-Funktionen, um jeden Schritt des Navigations-Mesh-Backprozesses einzeln aufzurufen.
Die Funktion
parse_source_geometry_data()
kann verwendet werden, um Quellgeometrie in eine wiederverwendbare und serialisierbare Ressource zu zerlegen.Mit
bake_from_source_geometry_data()
kann ein Navigations-Mesh aus bereits geparsten Daten gebacken werden, um z.B. Performance-Probleme mit (redundantem) Parsing zur Laufzeit zu vermeiden.Die Funktion
bake_from_source_geometry_data_async()
ist die gleiche, aber sie backt das Navigations-Mesh zeitversetzt mit Threads und blockiert nicht den Hauptthread.
Im Vergleich zu einer NavigationRegion bietet der NavigationServer eine feinere Kontrolle über den Backprozess des Navigations-Meshs. Im Gegenzug ist er komplexer in der Anwendung, bietet aber auch mehr erweiterte Optionen.
Einige weitere Vorteile des NavigationServers gegenüber einer NavigationRegion sind:
Der Server kann die Quellgeometrie ohne Backen analysieren, z.B. um sie für eine spätere Verwendung zwischenzuspeichern.
Der Server ermöglicht es, den Root-Node auszuwählen, an dem das Parsen der Quellgeometrie manuell gestartet werden soll.
Der Server kann prozedural erzeugte Quellgeometriedaten akzeptieren und daraus backen.
Der Server kann mehrere Meshes nacheinander backen und dabei dieselben Geometriedaten (wieder)verwenden.
Um Navigationsmeshes mit dem NavigationServer zu backen, ist eine Quellgeometrie erforderlich. Quellgeometrie sind Geometriedaten, die beim Backen von Navigations-Meshes berücksichtigt werden sollten. Sowohl 2D- als auch 3D-Navigations-Meshes werden durch Backen aus der Quellgeometrie erstellt.
2D- und 3D-Versionen der Quellgeometrieressourcen sind als NavigationMeshSourceGeometryData2D bzw. NavigationMeshSourceGeometryData3D verfügbar.
Die Quellgeometrie kann aus visuellen Meshes, aus physikalischen Kollisionen oder aus prozedural erstellten Arrays von Daten wie Umrissen (2D) und Dreiecksflächen (3D) analysiert werden. Der Einfachheit halber wird die Quellgeometrie in der Regel direkt aus Node-Setups im SceneTree geparst. Für das (Neu-)Erstellen von Meshes zur Laufzeit ist zu beachten, dass das Parsen der Geometrie immer auf dem Haupt-Thread stattfindet.
Bemerkung
Der SceneTree ist nicht thread-sicher. Das Parsen der Quellgeometrie aus dem SceneTree kann nur im Haupt-Thread durchgeführt werden.
Warnung
Die Daten von visuellen Meshes und Polygonen müssen von der GPU empfangen werden, was den RenderingServer in diesem Prozess abwürgt. Für das (Neu-)Backen zur Laufzeit sollten bevorzugt Physik-Shapes als geparste Quellgeometrie verwendet werden.
Die Quellgeometrie wird in den Ressourcen gespeichert, so dass die erstellte Geometrie für mehrere Backvorgänge wiederverwendet werden kann. So können z.B. mehrere Navigations-Meshes für unterschiedliche Agentengrößen aus derselben Ausgangsgeometrie erstellt werden. Dies ermöglicht auch das Speichern der Quellgeometrie auf der Festplatte, so dass sie später geladen werden kann, z.B. um den Overhead des erneuten Parsens zur Laufzeit zu vermeiden.
Die Geometriedaten sollten im Allgemeinen sehr einfach gehalten werden. So viele Kanten wie nötig, aber so wenige wie möglich. Besonders in 2D sollte doppelte und verschachtelte Geometrie vermieden werden, da sie die Berechnung von Polygonlöchern erzwingt, was zu gespiegelten Polygonen führen kann. Ein Beispiel für verschachtelte Geometrie wäre ein kleineres StaticBody2D-Shape, das vollständig innerhalb der Grenzen eines anderen StaticBody2D-Shapes platziert wird.
Allgemeine Probleme beim Backen eines Navigations-Meshes¶
Bei der Erstellung oder dem Backen von Navigations-Meshes gibt es einige typische Benutzerprobleme und wichtige Hinweise zu beachten.
- Das Backen von Navigationsmeshes führt zu Problemen bei der Bildwiederholrate während der Laufzeit
Das Backen des Navigations-Meshes wird standardmäßig in einem Hintergrund-Thread ausgeführt. Solange die Plattform Threads unterstützt, ist das eigentliche Backen nur selten die Ursache für Performance-Probleme (vorausgesetzt, die Geometrie ist ausreichend groß und komplex für Neubacken zur Laufzeit).
Die häufigste Ursache für Performance-Probleme zur Laufzeit ist der Parsing-Schritt für Quellgeometrie, der Nodes und den SceneTree umfasst. Der SceneTree ist nicht thread-sicher, so dass alle Nodes auf dem Hauptthread geparst werden müssen. Einige Nodes mit vielen Daten können sehr schwer und langsam zur Laufzeit zu parsen sein, z.B. hat eine TileMap ein oder mehrere Polygone für jede einzelne Zelle und TileMapLayer zu parsen. Nodes, die Meshes enthalten, müssen die Daten vom RenderingServer anfordern, was den Rendering-Prozess verzögert.
Um die Performance zu verbessern, sollten Sie optimierte Shapes verwenden, z.B. Kollisions-Shape statt detaillierter visueller Meshes, und so viel Geometrie wie möglich im Vorfeld zusammenführen und vereinfachen. Wenn alles nichts hilft, analysieren Sie den SceneTree nicht und fügen Sie die Quellgeometrie prozedural mit Skripten hinzu. Wenn nur reine Datenarrays als Quellgeometrie verwendet werden, kann der gesamte Backprozess in einem Hintergrund-Thread durchgeführt werden.
- Das Navigations-Mesh erzeugt unbeabsichtigte Löcher in 2D.
Das Backen von Navigationsmeshe in 2D erfolgt durch Polygon-Clipping-Operationen auf der Grundlage von Umrisspfaden. Polygone mit "Löchern" sind ein notwendiges Übel, um komplexere 2D-Polygone zu erstellen, können aber für Benutzer mit vielen komplexen Shapes unberechenbar werden.
Um unerwartete Probleme bei der Berechnung von Polygonlöchern zu vermeiden, sollten Sie es vermeiden, Umrisse innerhalb anderer Umrisse desselben Typs (überfahrbar/Hindernis) zu verschachteln. Dies gilt auch für Shapes, die von Nodes geparst wurden. Wenn z.B. ein kleineres StaticBody2D-Shape innerhalb eines größeren StaticBody2D-Shapes platziert wird, kann dies dazu führen, dass das resultierende Polygon gespiegelt wird.
- Das Navigation Mesh erscheint innerhalb der Geometrie in 3D.
Die Backen von Navigations-Meshes in 3D kennt das Konzept des "Innen" nicht. Die zur Rasterung der Geometrie verwendeten Voxelzellen sind entweder belegt oder nicht. Entfernen Sie die Geometrie, die sich auf dem Boden innerhalb der anderen Geometrie befindet. Wenn das nicht möglich ist, fügen Sie eine kleinere "Dummy"-Geometrie mit so wenig Dreiecken wie möglich hinzu, damit die Zellen mit etwas belegt sind.
Skriptvorlagen für Navigations-Meshes¶
Das folgende Skript verwendet den NavigationServer, um Quellgeometrie aus dem Szenenbaum zu analysieren, ein Navigations-Mesh zu erstellen und eine Navigationsregion mit dem aktualisierten Navigations-Mesh zu aktualisieren.
extends Node2D
var navigation_mesh: NavigationPolygon
var source_geometry : NavigationMeshSourceGeometryData2D
var callback_parsing : Callable
var callback_baking : Callable
var region_rid: RID
func _ready() -> void:
navigation_mesh = NavigationPolygon.new()
navigation_mesh.agent_radius = 10.0
source_geometry = NavigationMeshSourceGeometryData2D.new()
callback_parsing = on_parsing_done
callback_baking = on_baking_done
region_rid = NavigationServer2D.region_create()
# Enable the region and set it to the default navigation map.
NavigationServer2D.region_set_enabled(region_rid, true)
NavigationServer2D.region_set_map(region_rid, get_world_2d().get_navigation_map())
# Some mega-nodes like TileMap are often not ready on the first frame.
# Also the parsing needs to happen on the main-thread.
# So do a deferred call to avoid common parsing issues.
parse_source_geometry.call_deferred()
func parse_source_geometry() -> void:
source_geometry.clear()
var root_node: Node2D = self
# Parse the obstruction outlines from all child nodes of the root node by default.
NavigationServer2D.parse_source_geometry_data(
navigation_mesh,
source_geometry,
root_node,
callback_parsing
)
func on_parsing_done() -> void:
# If we did not parse a TileMap with navigation mesh cells we may now only
# have obstruction outlines so add at least one traversable outline
# so the obstructions outlines have something to "cut" into.
source_geometry.add_traversable_outline(PackedVector2Array([
Vector2(0.0, 0.0),
Vector2(500.0, 0.0),
Vector2(500.0, 500.0),
Vector2(0.0, 500.0)
]))
# Bake the navigation mesh on a thread with the source geometry data.
NavigationServer2D.bake_from_source_geometry_data_async(
navigation_mesh,
source_geometry,
callback_baking
)
func on_baking_done() -> void:
# Update the region with the updated navigation mesh.
NavigationServer2D.region_set_navigation_polygon(region_rid, navigation_mesh)
extends Node3D
var navigation_mesh: NavigationMesh
var source_geometry : NavigationMeshSourceGeometryData3D
var callback_parsing : Callable
var callback_baking : Callable
var region_rid: RID
func _ready() -> void:
navigation_mesh = NavigationMesh.new()
navigation_mesh.agent_radius = 0.5
source_geometry = NavigationMeshSourceGeometryData3D.new()
callback_parsing = on_parsing_done
callback_baking = on_baking_done
region_rid = NavigationServer3D.region_create()
# Enable the region and set it to the default navigation map.
NavigationServer3D.region_set_enabled(region_rid, true)
NavigationServer3D.region_set_map(region_rid, get_world_3d().get_navigation_map())
# Some mega-nodes like GridMap are often not ready on the first frame.
# Also the parsing needs to happen on the main-thread.
# So do a deferred call to avoid common parsing issues.
parse_source_geometry.call_deferred()
func parse_source_geometry() -> void:
source_geometry.clear()
var root_node: Node3D = self
# Parse the geometry from all mesh child nodes of the root node by default.
NavigationServer3D.parse_source_geometry_data(
navigation_mesh,
source_geometry,
root_node,
callback_parsing
)
func on_parsing_done() -> void:
# Bake the navigation mesh on a thread with the source geometry data.
NavigationServer3D.bake_from_source_geometry_data_async(
navigation_mesh,
source_geometry,
callback_baking
)
func on_baking_done() -> void:
# Update the region with the updated navigation mesh.
NavigationServer3D.region_set_navigation_mesh(region_rid, navigation_mesh)
Das folgende Skript verwendet den NavigationServer, um eine Navigationsregion mit prozedural erzeugten Mesh-Daten zu aktualisieren.
extends Node2D
var navigation_mesh: NavigationPolygon
var region_rid: RID
func _ready() -> void:
navigation_mesh = NavigationPolygon.new()
region_rid = NavigationServer2D.region_create()
# Enable the region and set it to the default navigation map.
NavigationServer2D.region_set_enabled(region_rid, true)
NavigationServer2D.region_set_map(region_rid, get_world_2d().get_navigation_map())
# Add vertices for a convex polygon.
navigation_mesh.vertices = PackedVector2Array([
Vector2(0.0, 0.0),
Vector2(100.0, 0.0),
Vector2(100.0, 100.0),
Vector2(0.0, 100.0)
])
# Add indices for the polygon.
navigation_mesh.add_polygon(
PackedInt32Array([0, 1, 2, 3])
)
NavigationServer2D.region_set_navigation_polygon(region_rid, navigation_mesh)
extends Node3D
var navigation_mesh: NavigationMesh
var region_rid: RID
func _ready() -> void:
navigation_mesh = NavigationMesh.new()
region_rid = NavigationServer3D.region_create()
# Enable the region and set it to the default navigation map.
NavigationServer3D.region_set_enabled(region_rid, true)
NavigationServer3D.region_set_map(region_rid, get_world_3d().get_navigation_map())
# Add vertices for a convex polygon.
navigation_mesh.vertices = PackedVector3Array([
Vector3(-1.0, 0.0, 1.0),
Vector3(1.0, 0.0, 1.0),
Vector3(1.0, 0.0, -1.0),
Vector3(-1.0, 0.0, -1.0),
])
# Add indices for the polygon.
navigation_mesh.add_polygon(
PackedInt32Array([0, 1, 2, 3])
)
NavigationServer3D.region_set_navigation_mesh(region_rid, navigation_mesh)