Merge pull request #6896 from dalexeev/gds-var-colon-style

Fix GDScript code style regarding colon (docs)
This commit is contained in:
Rémi Verschelde
2023-03-06 10:43:54 +01:00
22 changed files with 305 additions and 305 deletions
@@ -73,7 +73,7 @@ Your scene should look like this:
Main script
~~~~~~~~~~~
Add a script to ``Main``. At the top of the script, we use
Add a script to ``Main``. At the top of the script, we use
``@export var mob_scene: PackedScene`` to allow us to choose the Mob scene we want
to instance.
@@ -154,7 +154,7 @@ to instance.
};
#endif // MAIN_H
// This code goes in `main.cpp`.
#include "main.hpp"
@@ -243,7 +243,7 @@ new game:
}
Now connect the ``timeout()`` signal of each of the Timer nodes (``StartTimer``,
``ScoreTimer`` , and ``MobTimer``) to the main script. ``StartTimer`` will start
``ScoreTimer``, and ``MobTimer``) to the main script. ``StartTimer`` will start
the other two timers. ``ScoreTimer`` will increment the score by 1.
.. tabs::
@@ -238,8 +238,8 @@ tree structures.
extends Object
class_name TreeNode
var _parent : TreeNode = null
var _children : = [] setget
var _parent: TreeNode = null
var _children: = [] setget
func _notification(p_what):
match p_what:
@@ -141,7 +141,7 @@ If one is creating a large level, which circumstances are most appropriate?
Should they create the level as one static space? Or should they load the
level in pieces and shift the world's content as needed?
Well, the simple answer is , "when the performance requires it." The
Well, the simple answer is, "when the performance requires it." The
dilemma associated with the two options is one of the age-old programming
choices: does one optimize memory over speed, or vice versa?
@@ -3,38 +3,38 @@
Connecting NavigationMeshes
===========================
Different NavigationMeshes are automatically merged by the NavigationServer
Different NavigationMeshes are automatically merged by the NavigationServer
when at least two vertex positions of one edge exactly overlap.
To connect over arbitrary distances see :ref:`doc_navigation_using_navigationlinks`.
.. image:: img/navigation_vertex_merge.png
The same is true for multiple NavigationPolygon resources. As long as their
The same is true for multiple NavigationPolygon resources. As long as their
outline points overlap exactly the NavigationServer will merge them.
NavigationPolygon outlines must be from different NavigationPolygon resources to connect.
Overlapping or intersecting outlines on the same NavigationPolygon
will fail the navigation mesh creation. Overlapping or intersecting
outlines from different NavigationPolygons will often fail to create the
Overlapping or intersecting outlines on the same NavigationPolygon
will fail the navigation mesh creation. Overlapping or intersecting
outlines from different NavigationPolygons will often fail to create the
navigation region edge connections on the NavigationServer and should be avoided.
.. image:: img/navigation_vertex_merge2.png
.. warning::
Exactly means exactly for the vertex position merge. Small float errors
Exactly means exactly for the vertex position merge. Small float errors
that happen quite regularly with imported meshes will prevent a successful vertex merge.
Alternatively ``NavigationMesh``s are not merged but still considered as ``connected`` by
the NavigationServer when their edges are nearly parallel and within distance
to each other. The connection distance is defined by the ``edge_connection_margin`` for each
navigation map. In many cases NavigationMesh edges cannot properly connect when they partly overlap.
Alternatively ``NavigationMesh``s are not merged but still considered as ``connected`` by
the NavigationServer when their edges are nearly parallel and within distance
to each other. The connection distance is defined by the ``edge_connection_margin`` for each
navigation map. In many cases NavigationMesh edges cannot properly connect when they partly overlap.
Better avoid any navigation mesh overlap at all time for a consistent merge behavior.
.. image:: img/navigation_edge_connection.png
If navigation debug is enabled and the NavigationServer active the established navigation mesh connections will be visualized.
If navigation debug is enabled and the NavigationServer active the established navigation mesh connections will be visualized.
See :ref:`doc_navigation_debug_tools` for more info about navigation debug options.
The default 2D ``edge_connection_margin`` can be changed in the ProjectSettings under ``navigation/2d/default_edge_connection_margin``.
@@ -45,18 +45,18 @@ The edge connection margin value of any navigation map can also be changed at ru
.. tabs::
.. code-tab:: gdscript GDScript
extends Node2D
# 2D margins are designed to work with "pixel" values
var default_2d_map_rid : RID = get_world_2d().get_navigation_map()
var default_2d_map_rid: RID = get_world_2d().get_navigation_map()
NavigationServer2D.map_set_edge_connection_margin(default_2d_map_rid, 50.0)
.. tabs::
.. code-tab:: gdscript GDScript
extends Node3D
# 3D margins are designed to work with 3D unit values
var default_3d_map_rid : RID = get_world_3d().get_navigation_map()
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
NavigationServer3D.map_set_edge_connection_margin(default_3d_map_rid, 0.5)
.. note::
@@ -5,7 +5,7 @@ Support different actor types
.. image:: img/nav_actor_sizes.png
To support different actor types due to e.g. their sizes each type requires its own
To support different actor types due to e.g. their sizes each type requires its own
navigation map and navigation mesh baked with an appropriated agent radius and height.
The same approach can be used to distinguish between e.g. landwalking, swimming or flying agents.
@@ -17,10 +17,10 @@ The same approach can be used to distinguish between e.g. landwalking, swimming
.. code-tab:: gdscript GDScript
# create navigation mesh resources for each actor size
var navigation_mesh_standard_size : NavigationMesh = NavigationMesh.new()
var navigation_mesh_small_size : NavigationMesh = NavigationMesh.new()
var navigation_mesh_huge_size : NavigationMesh = NavigationMesh.new()
var navigation_mesh_standard_size: NavigationMesh = NavigationMesh.new()
var navigation_mesh_small_size: NavigationMesh = NavigationMesh.new()
var navigation_mesh_huge_size: NavigationMesh = NavigationMesh.new()
# set appropriated agent parameters
navigation_mesh_standard_size.agent_radius = 0.5
navigation_mesh_standard_size.agent_height = 1.8
@@ -28,38 +28,38 @@ The same approach can be used to distinguish between e.g. landwalking, swimming
navigation_mesh_small_size.agent_height = 0.7
navigation_mesh_huge_size.agent_radius = 1.5
navigation_mesh_huge_size.agent_height = 2.5
# get the root node for the baking to parse geometry
var root_node : Node3D = get_node("NavigationMeshBakingRootNode")
var root_node: Node3D = get_node("NavigationMeshBakingRootNode")
# bake the navigation geometry for each agent size
NavigationMeshGenerator.bake(navigation_mesh_standard_size, root_node)
NavigationMeshGenerator.bake(navigation_mesh_small_size, root_node)
NavigationMeshGenerator.bake(navigation_mesh_huge_size, root_node)
# create different navigation maps on the NavigationServer
var navigation_map_standard : RID = NavigationServer3D.map_create()
var navigation_map_small : RID = NavigationServer3D.map_create()
var navigation_map_huge : RID = NavigationServer3D.map_create()
var navigation_map_standard: RID = NavigationServer3D.map_create()
var navigation_map_small: RID = NavigationServer3D.map_create()
var navigation_map_huge: RID = NavigationServer3D.map_create()
# create a region for each map
var navigation_map_standard_region : RID = NavigationServer3D.region_create()
var navigation_map_small_region : RID = NavigationServer3D.region_create()
var navigation_map_huge_region : RID = NavigationServer3D.region_create()
var navigation_map_standard_region: RID = NavigationServer3D.region_create()
var navigation_map_small_region: RID = NavigationServer3D.region_create()
var navigation_map_huge_region: RID = NavigationServer3D.region_create()
# set navigation mesh for each region
NavigationServer3D.region_set_navigation_mesh(navigation_map_standard_region, navigation_mesh_standard_size)
NavigationServer3D.region_set_navigation_mesh(navigation_map_small_region, navigation_mesh_small_size)
NavigationServer3D.region_set_navigation_mesh(navigation_map_huge_region, navigation_mesh_huge_size)
# add regions to maps
navigation_map_standard_region.region_set_map(navigation_map_standard_region, navigation_map_standard)
navigation_map_small_region.region_set_map(navigation_map_small_region, navigation_map_small)
navigation_map_huge_region.region_set_map(navigation_map_huge_region, navigation_map_huge)
# wait a physics frame for sync
await get_tree().physics_frame
# query paths for each size
var path_standard_agent = NavigationServer3D.map_get_path(navigation_map_standard, start_pos, end_pos, true)
var path_small_agent = NavigationServer3D.map_get_path(navigation_mesh_small_size, start_pos, end_pos, true)
@@ -98,10 +98,10 @@ NavigationServer2D and a NavigationAgent2D for path movement.
extends CharacterBody2D
var movement_speed : float = 200.0
var movement_target_position : Vector2 = Vector2(60.0,180.0)
var movement_speed: float = 200.0
var movement_target_position: Vector2 = Vector2(60.0,180.0)
@onready var navigation_agent : NavigationAgent2D = $NavigationAgent2D
@onready var navigation_agent: NavigationAgent2D = $NavigationAgent2D
func _ready():
# These values need to be adjusted for the actor's speed
@@ -119,17 +119,17 @@ NavigationServer2D and a NavigationAgent2D for path movement.
# Now that the navigation map is no longer empty, set the movement target.
set_movement_target(movement_target_position)
func set_movement_target(movement_target : Vector2):
func set_movement_target(movement_target: Vector2):
navigation_agent.set_target_location(movement_target)
func _physics_process(delta):
if navigation_agent.is_navigation_finished():
return
var current_agent_position : Vector2 = global_transform.origin
var next_path_position : Vector2 = navigation_agent.get_next_location()
var current_agent_position: Vector2 = global_transform.origin
var next_path_position: Vector2 = navigation_agent.get_next_location()
var new_velocity : Vector2 = next_path_position - current_agent_position
var new_velocity: Vector2 = next_path_position - current_agent_position
new_velocity = new_velocity.normalized()
new_velocity = new_velocity * movement_speed
@@ -106,10 +106,10 @@ a NavigationAgent3D for path movement.
extends CharacterBody3D
var movement_speed : float = 2.0
var movement_target_position : Vector3 = Vector3(-3.0,0.0,2.0)
var movement_speed: float = 2.0
var movement_target_position: Vector3 = Vector3(-3.0,0.0,2.0)
@onready var navigation_agent : NavigationAgent3D = $NavigationAgent3D
@onready var navigation_agent: NavigationAgent3D = $NavigationAgent3D
func _ready():
# These values need to be adjusted for the actor's speed
@@ -127,17 +127,17 @@ a NavigationAgent3D for path movement.
# Now that the navigation map is no longer empty, set the movement target.
set_movement_target(movement_target_position)
func set_movement_target(movement_target : Vector3):
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
if navigation_agent.is_navigation_finished():
return
var current_agent_position : Vector3 = global_transform.origin
var next_path_position : Vector3 = navigation_agent.get_next_path_position()
var current_agent_position: Vector3 = global_transform.origin
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var new_velocity : Vector3 = next_path_position - current_agent_position
var new_velocity: Vector3 = next_path_position - current_agent_position
new_velocity = new_velocity.normalized()
new_velocity = new_velocity * movement_speed
@@ -3,7 +3,7 @@
Using Agent Avoidance
=====================
This section is about how to use agent avoidance with the NavigationServer and
This section is about how to use agent avoidance with the NavigationServer and
documents how agent avoidance is implemented in Godot.
For avoidance with NavigationAgents see :ref:`doc_navigation_using_navigationagents`.
@@ -14,7 +14,7 @@ while the agents still follow their original velocity as best as possible.
Avoidance in Godot is implemented with the help of the RVO library (Reciprocal Velocity Obstacle).
RVO places agents on a flat RVO map and gives each agent a ``radius`` and a ``position``.
Agents with overlapping radius compute a ``safe_velocity`` from their
current ``velocity``. The ``safe_velocity`` then needs to replace the original
current ``velocity``. The ``safe_velocity`` then needs to replace the original
submitted ``velocity`` to move the actor behind the agent with custom movement code.
.. note::
@@ -32,12 +32,12 @@ Pathfinding is map and region navmesh based while avoidance is purely map and ag
.. tabs::
.. code-tab:: gdscript GDScript
extends Node3D
var new_agent_rid : RID = NavigationServer3D.agent_create()
var default_3d_map_rid : RID = get_world_3d().get_navigation_map()
var new_agent_rid: RID = NavigationServer3D.agent_create()
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
NavigationServer3D.agent_set_map(new_agent_rid, default_3d_map_rid)
NavigationServer3D.agent_set_radius(new_agent_rid, 0.5)
NavigationServer3D.agent_set_position(new_agent_rid, global_transform.origin)
@@ -46,27 +46,27 @@ To receive safe_velocity signals for avoidance for the agent a callback needs to
.. tabs::
.. code-tab:: gdscript GDScript
extends Node3D
var agent_rid : RID = NavigationServer3D.agent_create()
var agent_rid: RID = NavigationServer3D.agent_create()
NavigationServer3D.agent_set_callback(agent_rid, self.on_safe_velocity_computed)
func on_safe_velocity_computed(safe_velocity : Vector3):
func on_safe_velocity_computed(safe_velocity: Vector3):
# do your avoidance movement
After the current and new calculated velocity needs to be passed to the NavigationServer each physics frame to trigger the safe_velocity callback when the avoidance processing is finished.
.. tabs::
.. code-tab:: gdscript GDScript
func _physics_process(delta):
NavigationServer3D.agent_set_velocity(current_velocity)
NavigationServer3D.agent_set_target_velocity(new_velocity)
.. warning::
If _process() is used instead of _physics_process() at a higher framerate
than physics the agent velocity should not be updated more than ones each
If _process() is used instead of _physics_process() at a higher framerate
than physics the agent velocity should not be updated more than ones each
physics frame e.g. by tracking the Engine.get_physics_frames().
@@ -126,7 +126,7 @@ used to create or delete avoidance callbacks for the agent RID.
extends NavigationAgent2D
var agent : RID = get_rid()
var agent: RID = get_rid()
# Enable
NavigationServer2D::get_singleton()->agent_set_callback(agent, self._avoidance_done)
# Disable
@@ -137,7 +137,7 @@ used to create or delete avoidance callbacks for the agent RID.
extends NavigationAgent3D
var agent : RID = get_rid()
var agent: RID = get_rid()
# Enable
NavigationServer3D::get_singleton()->agent_set_callback(agent, self._avoidance_done)
# Disable
@@ -159,11 +159,11 @@ This script adds basic navigation movement to a Node3D with a NavigationAgent3D
extends Node3D
# script on agent parent node, connect the agent 'velocity_computed' signal for collision avoidance
@export var movement_speed : float = 4.0
@onready var navigation_agent : NavigationAgent3D = get_node("NavigationAgent3D")
var movement_delta : float
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
var movement_delta: float
func set_movement_target(movement_target : Vector3):
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
@@ -171,12 +171,12 @@ This script adds basic navigation movement to a Node3D with a NavigationAgent3D
return
movement_delta = movement_speed * delta
var next_path_position : Vector3 = navigation_agent.get_next_path_position()
var current_agent_position : Vector3 = global_transform.origin
var new_velocity : Vector3 = (next_path_position - current_agent_position).normalized() * movement_delta
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var current_agent_position: Vector3 = global_transform.origin
var new_velocity: Vector3 = (next_path_position - current_agent_position).normalized() * movement_delta
navigation_agent.set_velocity(new_velocity)
func _on_NavigationAgent3D_velocity_computed(safe_velocity : Vector3):
func _on_NavigationAgent3D_velocity_computed(safe_velocity: Vector3):
# Move Node3D with the computed `safe_velocity` to avoid dynamic obstacles.
global_transform.origin = global_transform.origin.move_toward(global_transform.origin + safe_velocity, movement_delta)
@@ -191,11 +191,11 @@ This script adds basic navigation movement to a CharacterBody3D with a Navigatio
extends CharacterBody3D
# script on agent parent node, connect the agent 'velocity_computed' signal for collision avoidance
@export var movement_speed : float = 4.0
@onready var navigation_agent : NavigationAgent3D = get_node("NavigationAgent3D")
var movement_delta : float
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
var movement_delta: float
func set_movement_target(movement_target : Vector3):
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
@@ -203,12 +203,12 @@ This script adds basic navigation movement to a CharacterBody3D with a Navigatio
return
movement_delta = movement_speed * delta
var next_path_position : Vector3 = navigation_agent.get_next_path_position()
var current_agent_position : Vector3 = global_transform.origin
var new_velocity : Vector3 = (next_path_position - current_agent_position).normalized() * movement_delta
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var current_agent_position: Vector3 = global_transform.origin
var new_velocity: Vector3 = (next_path_position - current_agent_position).normalized() * movement_delta
navigation_agent.set_velocity(new_velocity)
func _on_NavigationAgent3D_velocity_computed(safe_velocity : Vector3):
func _on_NavigationAgent3D_velocity_computed(safe_velocity: Vector3):
# Move CharacterBody3D with the computed `safe_velocity` to avoid dynamic obstacles.
velocity = safe_velocity
move_and_slide()
@@ -224,20 +224,20 @@ This script adds basic navigation movement to a RigidBody3D with a NavigationAge
extends RigidBody3D
# script on agent parent node, connect the agent 'velocity_computed' signal for collision avoidance
@onready var navigation_agent : NavigationAgent3D = get_node("NavigationAgent3D")
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
func set_movement_target(movement_target : Vector3):
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
if navigation_agent.is_navigation_finished():
return
var next_path_position : Vector3 = navigation_agent.get_next_path_position()
var current_agent_position : Vector3 = global_transform.origin
var new_velocity : Vector3 = (next_path_position - current_agent_position).normalized() * velocity
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var current_agent_position: Vector3 = global_transform.origin
var new_velocity: Vector3 = (next_path_position - current_agent_position).normalized() * velocity
navigation_agent.set_velocity(new_velocity)
func _on_NavigationAgent3D_velocity_computed(safe_velocity : Vector3):
func _on_NavigationAgent3D_velocity_computed(safe_velocity: Vector3):
# Move RigidBody3D with the computed `safe_velocity` to avoid dynamic obstacles.
set_linear_velocity(safe_velocity)
@@ -15,53 +15,53 @@ If two regions have not a single compatible layer they will not be merged by the
If a region has not a single compatible navigation layer with the ``navigation_layers`` parameter of a path query this regions navigation mesh will be skipped in pathfinding.
See :ref:`doc_navigation_using_navigationpaths` for more information on querying the NavigationServer for paths.
NavigationLayers are a single ``int`` value that is used as a ``bitmask``.
Many navigation related nodes have ``set_navigation_layer_value()`` and
``get_navigation_layer_value()`` functions to set and get a layer number directly
NavigationLayers are a single ``int`` value that is used as a ``bitmask``.
Many navigation related nodes have ``set_navigation_layer_value()`` and
``get_navigation_layer_value()`` functions to set and get a layer number directly
without the need for more complex bitwise operations.
In scripts the following helper functions can be used to work with the navigation_layers bitmask.
.. tabs::
.. code-tab:: gdscript GDScript
func change_layers():
var region : NavigationRegion3D = get_node("NavigationRegion3D")
var region: NavigationRegion3D = get_node("NavigationRegion3D")
# enables 4-th layer for this region
region.navigation_layers = enable_bitmask_inx(region.navigation_layers, 4)
# disables 1-rst layer for this region
region.navigation_layers = disable_bitmask_inx(region.navigation_layers, 1)
var agent : NavigationAgent3D = get_node("NavigationAgent3D")
var agent: NavigationAgent3D = get_node("NavigationAgent3D")
# make future path queries of this agent ignore regions with 4-th layer
agent.navigation_layers = disable_bitmask_inx(agent.navigation_layers, 4)
var path_query_navigation_layers : int = 0
var path_query_navigation_layers: int = 0
path_query_navigation_layers = enable_bitmask_inx(path_query_navigation_layers, 2)
# get a path that only considers 2-nd layer regions
var path : PoolVector3Array = NavigationServer3D.map_get_path(
var path: PoolVector3Array = NavigationServer3D.map_get_path(
map,
start_position,
target_position,
true,
path_query_navigation_layers
)
static func is_bitmask_inx_enabled(_bitmask : int, _index : int) -> bool:
static func is_bitmask_inx_enabled(_bitmask: int, _index: int) -> bool:
return _bitmask & (1 << _index) != 0
static func enable_bitmask_inx(_bitmask : int, _index : int) -> int:
static func enable_bitmask_inx(_bitmask: int, _index: int) -> int:
return _bitmask | (1 << _index)
static func disable_bitmask_inx(_bitmask : int, _index : int) -> int:
static func disable_bitmask_inx(_bitmask: int, _index: int) -> int:
return _bitmask & ~(1 << _index)
Changing navigation layers for path queries is a performance friendly alternative to
enabling / disabling entire navigation regions. Compared to region changes a
navigation path query with different navigation layers does not
Changing navigation layers for path queries is a performance friendly alternative to
enabling / disabling entire navigation regions. Compared to region changes a
navigation path query with different navigation layers does not
trigger large scale updates on the NavigationServer.
Changing the navigation layers of NavigationAgent nodes will have an immediate
effect on the next path query. Changing the navigation layers of
regions will have an immediate effect on the region but any new region
Changing the navigation layers of NavigationAgent nodes will have an immediate
effect on the next path query. Changing the navigation layers of
regions will have an immediate effect on the region but any new region
connect or disconnect will only be in effect after the next physics_frame.
@@ -13,7 +13,7 @@ A map can be joined by avoidance agents to process collision avoidance between t
.. note::
Different NavigationMaps are completely isolated from each other but navigation regions
Different NavigationMaps are completely isolated from each other but navigation regions
and avoidance agents can switch between different maps once every server synchronization.
Default navigation maps
@@ -29,27 +29,27 @@ The 3D default navigation ``map`` can be obtained with ``get_world_3d().get_navi
.. code-tab:: gdscript GDScript
extends Node2D
var default_2d_navigation_map_rid : RID = get_world_2d().get_navigation_map()
var default_2d_navigation_map_rid: RID = get_world_2d().get_navigation_map()
.. tabs::
.. code-tab:: gdscript GDScript
extends Node3D
var default_3d_navigation_map_rid : RID = get_world_3d().get_navigation_map()
var default_3d_navigation_map_rid: RID = get_world_3d().get_navigation_map()
Creating new navigation maps
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The NavigationServer can create and support as many navigation maps as are required for specific gameplay.
Additional navigation maps are created and maintained by using the NavigationServer API
Additional navigation maps are created and maintained by using the NavigationServer API
directly e.g. to support different avoidance agent or actor locomotion types.
For example uses of different navigation maps see :ref:`doc_navigation_different_actor_types` and :ref:`doc_navigation_different_actor_locomotion`.
Each navigation map synchronizes queued changes to its navigation regions and avoidance agents individually.
A navigation map that has not received changes will consume little to no processing time.
Each navigation map synchronizes queued changes to its navigation regions and avoidance agents individually.
A navigation map that has not received changes will consume little to no processing time.
Navigation regions and avoidance agents can only be part of a single navigations map but they can switch maps at any time.
.. note::
@@ -60,16 +60,16 @@ Navigation regions and avoidance agents can only be part of a single navigations
.. code-tab:: gdscript GDScript
extends Node2D
var new_navigation_map : RID = NavigationServer2D.map_create()
var new_navigation_map: RID = NavigationServer2D.map_create()
NavigationServer2D.map_set_active(true)
.. tabs::
.. code-tab:: gdscript GDScript
extends Node3D
var new_navigation_map : RID = NavigationServer3D.map_create()
var new_navigation_map: RID = NavigationServer3D.map_create()
NavigationServer3D.map_set_active(true)
.. note::
@@ -91,18 +91,18 @@ If the navigation mesh resource is already prepared, the region can be updated w
func update_navigation_mesh():
# use bake and update function of region
var on_thread : bool = true
var on_thread: bool = true
bake_navigation_mesh(on_thread)
# or use the NavigationMeshGenerator Singleton
var _navigationmesh : NavigationMesh = navigation_mesh
var _navigationmesh: NavigationMesh = navigation_mesh
NavigationMeshGenerator.bake(_navigationmesh, self)
# remove old resource first to trigger a full update
navigation_mesh = null
navigation_mesh = _navigationmesh
# or use NavigationServer API to update region with prepared navigation mesh
var region_rid : RID = get_region_rid()
var region_rid: RID = get_region_rid()
NavigationServer3D.region_set_navigation_mesh(region_rid, navigation_mesh)
.. note::
@@ -140,7 +140,7 @@ navigationmesh from outline data the shapes cannot overlap.
extends NavigationRegion2D
var new_navigation_polygon : NavigationPolygon = get_navigation_polygon()
var new_navigation_polygon: NavigationPolygon = get_navigation_polygon()
func _ready():
@@ -149,7 +149,7 @@ navigationmesh from outline data the shapes cannot overlap.
new_navigation_polygon.make_polygons_from_outlines()
set_navigation_polygon(new_navigation_polygon)
func parse_2d_collisionshapes(root_node : Node2D):
func parse_2d_collisionshapes(root_node: Node2D):
for node in root_node.get_children():
@@ -158,9 +158,9 @@ navigationmesh from outline data the shapes cannot overlap.
if node is CollisionPolygon2D:
var new_collision_outline : PackedVector2Array = PackedVector2Array()
var collisionpolygon_transform : Transform2D = node.get_global_transform()
var collisionpolygon : CollisionPolygon2D = node.get_polygon()
var new_collision_outline: PackedVector2Array = PackedVector2Array()
var collisionpolygon_transform: Transform2D = node.get_global_transform()
var collisionpolygon: CollisionPolygon2D = node.get_polygon()
for vertex in collisionpolygon:
new_collision_outline.append(collisionpolygon_transform.xform(vertex))
@@ -177,13 +177,13 @@ The following script creates a new 2D navigation region and fills it with proced
extends Node2D
var new_2d_region_rid : RID = NavigationServer2D.region_create()
var new_2d_region_rid: RID = NavigationServer2D.region_create()
var default_2d_map_rid : RID = get_world_2d().get_navigation_map()
var default_2d_map_rid: RID = get_world_2d().get_navigation_map()
NavigationServer2D.region_set_map(new_2d_region_rid, default_2d_map_rid)
var new_navigation_polygon : NavigationPolygon = NavigationPolygon.new()
var new_outline : PackedVector2Array = PackedVector2Array([
var new_navigation_polygon: NavigationPolygon = NavigationPolygon.new()
var new_outline: PackedVector2Array = PackedVector2Array([
Vector2(0.0, 0.0),
Vector2(50.0, 0.0),
Vector2(50.0, 50.0),
@@ -204,13 +204,13 @@ The following script creates a new 3D navigation region and fills it with proced
extends Node3D
var new_3d_region_rid : RID = NavigationServer3D.region_create()
var new_3d_region_rid: RID = NavigationServer3D.region_create()
var default_3d_map_rid : RID = get_world_3d().get_navigation_map()
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
NavigationServer3D.region_set_map(new_3d_region_rid, default_3d_map_rid)
var new_navigation_mesh : NavigationMesh = NavigationMesh.new()
var new_plane_mesh : PlaneMesh = PlaneMesh.new()
var new_navigation_mesh: NavigationMesh = NavigationMesh.new()
var new_plane_mesh: PlaneMesh = PlaneMesh.new()
new_plane_mesh.size = Vector2(10.0, 10.0)
new_navigation_mesh.create_from_mesh(new_plane_mesh)
@@ -230,10 +230,10 @@ The following script creates a new 3D navmesh from the mesh of a GridMap item, c
set_bake_navigation(true)
# get mesh from grid item, bake and set a new navigation mesh for the library
var gridmap_item_list : PackedInt32Array = mesh_library.get_item_list()
var gridmap_item_list: PackedInt32Array = mesh_library.get_item_list()
for item in gridmap_item_list:
var item_mesh : Mesh = mesh_library.get_item_mesh(item)
var new_item_navigation_mesh : NavigationMesh = NavigationMesh.new()
var item_mesh: Mesh = mesh_library.get_item_mesh(item)
var new_item_navigation_mesh: NavigationMesh = NavigationMesh.new()
new_item_navigation_mesh.create_from_mesh(item_mesh)
mesh_library.set_item_navigation_mesh(item, new_item_navigation_mesh)
mesh_library.set_item_navigation_mesh_transform(item, Transform3D())
@@ -242,9 +242,9 @@ The following script creates a new 3D navmesh from the mesh of a GridMap item, c
clear()
# add procedual cells using the first item
var _position : Vector3i = Vector3i(global_transform.origin)
var _item : int = 0
var _orientation : int = 0
var _position: Vector3i = Vector3i(global_transform.origin)
var _item: int = 0
var _orientation: int = 0
for i in range(0,10):
for j in range(0,10):
_position.x = i
@@ -3,31 +3,31 @@
Using NavigationObstacles
=========================
NavigationObstacles are used to set an avoidance radius around objects
that, due to their constant movement, cannot be efficiently (re)baked
to a 2D NavigationPolygon or 3D NavigationMesh.
NavigationObstacles are used to set an avoidance radius around objects
that, due to their constant movement, cannot be efficiently (re)baked
to a 2D NavigationPolygon or 3D NavigationMesh.
2D and 3D versions of NavigationObstacles nodes are available as
:ref:`NavigationObstacle2D<class_NavigationObstacle2D>` and
2D and 3D versions of NavigationObstacles nodes are available as
:ref:`NavigationObstacle2D<class_NavigationObstacle2D>` and
:ref:`NavigationObstacle3D<class_NavigationObstacle3D>` respectively.
NavigationObstacles are not intended for any kind of static geometry
NavigationObstacles are not intended for any kind of static geometry
or temporary barriers that may change their position occasionally.
Those changes should be (re)baked so actors can follow the outlines
of these objects at higher detail with navigation paths. The obstacle avoidance
Those changes should be (re)baked so actors can follow the outlines
of these objects at higher detail with navigation paths. The obstacle avoidance
should be seen as a last resort option intended for objects that are constantly moving.
To use NavigationObstacles for avoidance, place a NavigationObstacle2D/3D node
below a Node2D/3D inheriting parent node. While the obstacle node has an
option to ``estimate_radius`` from child collisions, prefer to set a
more reliable manual ``radius`` value. If estimated, the obstacle will use
a radius that encapsulates the entire parent node which can result in a very large
To use NavigationObstacles for avoidance, place a NavigationObstacle2D/3D node
below a Node2D/3D inheriting parent node. While the obstacle node has an
option to ``estimate_radius`` from child collisions, prefer to set a
more reliable manual ``radius`` value. If estimated, the obstacle will use
a radius that encapsulates the entire parent node which can result in a very large
radius value if the parent is not a circle shape but e.g. a long rectangle shape.
.. note::
The obstacle ``radius`` is the area that will be strictly avoided whenever possible.
Do not set it too large. Agents start to avoid way before
Do not set it too large. Agents start to avoid way before
this radius depending on parameters and velocity.
@@ -37,12 +37,12 @@ Obstacles can be placed directly on the NavigationMap with the NavigationServer
.. tabs::
.. code-tab:: gdscript GDScript
extends Node3D
# create a new "obstacle" agent and place it on the default map``
var new_agent_rid : RID = NavigationServer3D.agent_create()
var default_3d_map_rid : RID = get_world_3d().get_navigation_map()
var new_agent_rid: RID = NavigationServer3D.agent_create()
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
NavigationServer3D.agent_set_map(new_agent_rid, default_3d_map_rid)
NavigationServer3D.agent_set_radius(new_agent_rid, 0.5)
NavigationServer3D.agent_set_position(new_agent_rid, global_transform.origin)
@@ -3,30 +3,30 @@
Using NavigationPathQueryObjects
================================
``NavigationPathQueryObjects`` can be used together with ``NavigationServer.query_path()``
``NavigationPathQueryObjects`` can be used together with ``NavigationServer.query_path()``
to obtain a heavily **customized** navigation path including optional **meta data** about the path.
This requires more setup compared to obtaining a normal NavigationPath but lets you tailor
This requires more setup compared to obtaining a normal NavigationPath but lets you tailor
the pathfinding and provided path data to the different needs of a project.
NavigationPathQueryObjects consist of a pair of objects, a ``NavigationPathQueryParameters`` object holding the customization options
NavigationPathQueryObjects consist of a pair of objects, a ``NavigationPathQueryParameters`` object holding the customization options
for the query and a ``NavigationPathQueryResult`` that receives (regular) updates with the resulting path and meta data from the query.
2D and 3D versions of ``NavigationPathQueryParameters`` are available as
:ref:`NavigationPathQueryParameters2D<class_NavigationPathQueryParameters2D>` and
2D and 3D versions of ``NavigationPathQueryParameters`` are available as
:ref:`NavigationPathQueryParameters2D<class_NavigationPathQueryParameters2D>` and
:ref:`NavigationPathQueryParameters3D<class_NavigationPathQueryParameters3D>` respectively.
2D and 3D versions of ``NavigationPathQueryResult`` are available as
:ref:`NavigationPathQuerResult2D<class_NavigationPathQueryResult2D>` and
2D and 3D versions of ``NavigationPathQueryResult`` are available as
:ref:`NavigationPathQuerResult2D<class_NavigationPathQueryResult2D>` and
:ref:`NavigationPathQueryResult3D<class_NavigationPathQueryResult3D>` respectively.
Both parameters and result are used as a pair with the ``NavigationServer.query_path()`` function.
For the available customization options and their use see the class doc of the parameters.
While not a strict requirement, both objects are intended to be created once in advance, stored in a
persistent variable for the agent and reused for every followup path query with updated parameters.
This reuse avoids performance implications from frequent object creation if a project
While not a strict requirement, both objects are intended to be created once in advance, stored in a
persistent variable for the agent and reused for every followup path query with updated parameters.
This reuse avoids performance implications from frequent object creation if a project
has a large quantity of simultaneous agents that regularly update their paths.
.. tabs::
@@ -43,7 +43,7 @@ has a large quantity of simultaneous agents that regularly update their paths.
# update result object
NavigationServer2D.query_path(query_parameters, query_result)
var path : PackedVector2Array = query_result.get_path()
var path: PackedVector2Array = query_result.get_path()
.. tabs::
.. code-tab:: gdscript GDScript
@@ -59,4 +59,4 @@ has a large quantity of simultaneous agents that regularly update their paths.
# update result object
NavigationServer3D.query_path(query_parameters, query_result)
var path : PackedVector3Array = query_result.get_path()
var path: PackedVector3Array = query_result.get_path()
@@ -6,7 +6,7 @@ Using NavigationPaths
Obtaining a Navigationpath
--------------------------
Navigation paths can be directly queried from the NavigationServer and do not require any
Navigation paths can be directly queried from the NavigationServer and do not require any
additional nodes or objects as long as the navigation map has a navigationmesh to work with.
To obtain a 2D path, use ``NavigationServer2D.map_get_path(map, from, to, optimize, navigation_layers)``.
@@ -15,19 +15,19 @@ To obtain a 3D path, use ``NavigationServer3D.map_get_path(map, from, to, optimi
For more customizable navigation path queries that require additional setup see :ref:`doc_navigation_using_navigationpathqueryobjects`.
One of the required parameters for the query is the RID of the navigation map.
Each game ``World`` has a default navigation map automatically created.
The default navigation maps can be retrieved with ``get_world_2d().get_navigation_map()`` from
One of the required parameters for the query is the RID of the navigation map.
Each game ``World`` has a default navigation map automatically created.
The default navigation maps can be retrieved with ``get_world_2d().get_navigation_map()`` from
any Node2D inheriting node or ``get_world_3d().get_navigation_map()`` from any Node3D inheriting node.
The second and third parameters are the starting position and the target position as Vector2 for 2D or Vector3 for 3D.
If the ``optimized`` parameter is ``true``, path positions will be shortened along polygon
corners with an additional funnel algorithm pass. This works well for free movement
on navigationmeshes with unequal sized polygons as the path will hug around corners
along the polygon corridor found by the A* algorithm. With small cells the A* algorithm
If the ``optimized`` parameter is ``true``, path positions will be shortened along polygon
corners with an additional funnel algorithm pass. This works well for free movement
on navigationmeshes with unequal sized polygons as the path will hug around corners
along the polygon corridor found by the A* algorithm. With small cells the A* algorithm
creates a very narrow funnel corridor that can create ugly corner paths when used with grids.
If the ``optimized`` parameter is ``false``, path positions will be placed at the center of each polygon edge.
If the ``optimized`` parameter is ``false``, path positions will be placed at the center of each polygon edge.
This works well for pure grid movement on navmeshes with equal sized polygons as the path will go through the center of the grid cells.
Outside of grids due to polygons often covering large open areas with a single, long edge this can create paths with unnecessary long detours.
@@ -37,10 +37,10 @@ Outside of grids due to polygons often covering large open areas with a single,
extends Node2D
# basic query for a navigation path in 2D using the default navigation map
var default_2d_map_rid : RID = get_world_2d().get_navigation_map()
var start_position : Vector2 = Vector2(0.0, 0.0)
var target_position : Vector2 = Vector2(5.0, 0.0)
var path : PackedVector2Array = NavigationServer2D.map_get_path(
var default_2d_map_rid: RID = get_world_2d().get_navigation_map()
var start_position: Vector2 = Vector2(0.0, 0.0)
var target_position: Vector2 = Vector2(5.0, 0.0)
var path: PackedVector2Array = NavigationServer2D.map_get_path(
default_2d_map_rid,
start_position,
target_position,
@@ -52,10 +52,10 @@ Outside of grids due to polygons often covering large open areas with a single,
extends Node3D
# basic query for a navigation path in 3D using the default navigation map
var default_3d_map_rid : RID = get_world_3d().get_navigation_map()
var start_position : Vector3 = Vector3(0.0, 0.0, 0.0)
var target_position : Vector3 = Vector3(5.0, 0.0, 3.0)
var path : PackedVector3Array = NavigationServer3D.map_get_path(
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
var start_position: Vector3 = Vector3(0.0, 0.0, 0.0)
var target_position: Vector3 = Vector3(5.0, 0.0, 3.0)
var path: PackedVector3Array = NavigationServer3D.map_get_path(
default_3d_map_rid,
start_position,
target_position,
@@ -71,46 +71,46 @@ All index between are the pathpoints that an actor should follow to reach the ta
.. note::
If the target position is on a different navigation mesh that is not merged or connected
If the target position is on a different navigation mesh that is not merged or connected
the navigation path will lead to the closest possible position on the starting position navigation mesh.
The following script moves a Node3D inheriting node along a navigation path using
The following script moves a Node3D inheriting node along a navigation path using
the default navigation map by setting the target position with ``set_movement_target()``.
.. tabs::
.. code-tab:: gdscript GDScript
var movement_speed : float = 4.0
var movement_delta : float
var path_point_margin : float = 0.5
var default_3d_map_rid : RID = get_world_3d().get_navigation_map()
var movement_speed: float = 4.0
var movement_delta: float
var path_point_margin: float = 0.5
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
var current_path_index: int = 0
var current_path_point: Vector3
var current_path: PackedVector3Array
func set_movement_target(target_position: Vector3):
var start_position: Vector3 = global_transform.origin
var current_path_index : int = 0
var current_path_point : Vector3
var current_path : PackedVector3Array
func set_movement_target(target_position : Vector3):
var start_position : Vector3 = global_transform.origin
current_path = NavigationServer3D.map_get_path(
default_3d_map_rid,
start_position,
target_position,
true
)
if not current_path.is_empty():
current_path_index = 0
current_path_point = current_path[0]
func _physics_process(delta):
if current_path.is_empty():
return
movement_delta = move_speed * delta
if global_transform.origin.distance_to(current_path_point) <= path_point_margin:
current_path_index += 1
if current_path_index >= current_path.size():
@@ -118,9 +118,9 @@ the default navigation map by setting the target position with ``set_movement_ta
current_path_index = 0
current_path_point = global_transform.origin
return
current_path_point = current_path[current_path_index]
var new_velocity : Vector3 = (current_path_point - global_transform.origin).normalized() * movement_delta
var new_velocity: Vector3 = (current_path_point - global_transform.origin).normalized() * movement_delta
global_transform.origin = global_transform.origin.move_toward(global_transform.origin + new_velocity, movement_delta)
@@ -6,7 +6,7 @@ Using NavigationRegions
NavigationRegions are the visual Node representation of a ``region`` of the navigation ``map`` on the NavigationServer.
Each NavigationRegion node holds a resource for the navigationmesh data.
Both 2D and 3D version are available as :ref:`NavigationRegion2D<class_NavigationRegion2D>`
Both 2D and 3D version are available as :ref:`NavigationRegion2D<class_NavigationRegion2D>`
and :ref:`NavigationRegion3D<class_NavigationRegion3D>` respectively.
Individual NavigationRegions upload their 2D NavigationPolygon or 3D NavigationMesh resource data to the NavigationServer.
@@ -16,13 +16,13 @@ To create a navigation region using the SceneTree add a ``NavigationRegion2D`` o
All regions require a navigationmesh resource to function. See :ref:`doc_navigation_using_navigationmeshes` to learn how to create and apply navigationmeshes.
NavigationRegions will automatically push ``global_transform`` changes to the region on the NavigationServer which makes them suitable for moving platforms.
The NavigationServer will attempt to connect navmeshes of individual regions when they are close enough. For more detail see :ref:`doc_navigation_connecting_navmesh`.
The NavigationServer will attempt to connect navmeshes of individual regions when they are close enough. For more detail see :ref:`doc_navigation_connecting_navmesh`.
To connect NavigationRegions over arbitrary distances see :ref:`doc_navigation_using_navigationlinks` to learn how to create and use ``NavigationLinks``.
.. warning::
While changing the transform of a NavigationRegion node does update the region position on the
NavigationServer changing the scale does not. A navigationmesh resource has no scale and needs
While changing the transform of a NavigationRegion node does update the region position on the
NavigationServer changing the scale does not. A navigationmesh resource has no scale and needs
to be fully updated when source geometry changes scale.
Regions can be enabled / disabled and if disabled will not contribute to future pathfinding queries.
@@ -42,8 +42,8 @@ The region RID can then be obtained from NavigationRegion Nodes with ``get_regio
.. code-tab:: gdscript GDScript
extends NavgiationRegion3D
var navigationserver_region_rid : RID = get_region_rid()
var navigationserver_region_rid: RID = get_region_rid()
New regions can also be created with the NavigationServer API and added to any existing map.
@@ -53,18 +53,18 @@ If regions are created with the NavigationServer API directly they need to be as
.. code-tab:: gdscript GDScript
extends Node2D
var new_2d_region_rid : RID = NavigationServer2D.region_create()
var default_2d_map_rid : RID = get_world_2d().get_navigation_map()
var new_2d_region_rid: RID = NavigationServer2D.region_create()
var default_2d_map_rid: RID = get_world_2d().get_navigation_map()
NavigationServer2D.region_set_map(new_2d_region_rid, default_2d_map_rid)
.. tabs::
.. code-tab:: gdscript GDScript
extends Node3D
var new_3d_region_rid : RID = NavigationServer3D.region_create()
var default_3d_map_rid : RID = get_world_3d().get_navigation_map()
var new_3d_region_rid: RID = NavigationServer3D.region_create()
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
NavigationServer3D.region_set_map(new_3d_region_rid, default_3d_map_rid)
.. note::
@@ -3,8 +3,8 @@
Using NavigationServer
======================
2D and 3D version of the NavigationServer are available as
:ref:`NavigationServer2D<class_NavigationServer2D>` and
2D and 3D version of the NavigationServer are available as
:ref:`NavigationServer2D<class_NavigationServer2D>` and
:ref:`NavigationServer3D<class_NavigationServer3D>` respectively.
Both 2D and 3D use the same NavigationServer with NavigationServer3D being the primary server. The NavigationServer2D is a frontend that converts 2D positions into 3D positions and back.
@@ -21,21 +21,21 @@ Every navigation related node in the SceneTree has a function that returns the R
Threading and Synchronisation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The NavigationServer does not update every change immediately but waits until
The NavigationServer does not update every change immediately but waits until
the end of the ``physics_frame`` to synchronise all the changes together.
Waiting for synchronisation is required to apply changes to all maps, regions and agents.
Synchronisation is done because some updates like a recalculation of the entire navigation map are very expensive and require updated data from all other objects.
Also the NavigationServer uses a ``threadpool`` by default for some functionality like avoidance calculation between agents.
Waiting for synchronisation is required to apply changes to all maps, regions and agents.
Synchronisation is done because some updates like a recalculation of the entire navigation map are very expensive and require updated data from all other objects.
Also the NavigationServer uses a ``threadpool`` by default for some functionality like avoidance calculation between agents.
Waiting is not required for most ``get()`` functions that only request data from the NavigationServer without making changes.
Note that not all data will account for changes made in the same frame.
Waiting is not required for most ``get()`` functions that only request data from the NavigationServer without making changes.
Note that not all data will account for changes made in the same frame.
E.g. if an avoidance ``agent`` changed the navigation ``map`` this frame the ``agent_get_map()`` function will still return the old map before the synchronisation.
The exception to this are nodes that store their values internally before sending the update to the NavigationServer.
The exception to this are nodes that store their values internally before sending the update to the NavigationServer.
When a getter on a node is used for a value that was updated in the same frame it will return the already updated value stored on the node.
The NavigationServer is ``thread-safe`` as it places all API calls that want to make changes in a queue to be executed in the synchronisation phase.
Synchronisation for the NavigationServer happens in the middle of the physics frame after scene input from scripts and nodes are all done.
Synchronisation for the NavigationServer happens in the middle of the physics frame after scene input from scripts and nodes are all done.
.. note::
The important takeaway is that most NavigationServer changes take effect after the next physics frame and not immediately.
@@ -69,22 +69,22 @@ The following functions will be executed in the synchronisation phase only:
2D and 3D NavigationServer differences
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NavigationServer2D and NavigationServer3D are equivalent in functionality
NavigationServer2D and NavigationServer3D are equivalent in functionality
for their dimension and both use the same NavigationServer behind the scene.
Strictly technical a NavigationServer2D is a myth.
The NavigationServer2D is a frontend to facilitate conversions of Vector2(x, y) to
Vector3(x, 0.0, z) and back for the NavigationServer3D API. 2D uses a flat 3D mesh
Strictly technical a NavigationServer2D is a myth.
The NavigationServer2D is a frontend to facilitate conversions of Vector2(x, y) to
Vector3(x, 0.0, z) and back for the NavigationServer3D API. 2D uses a flat 3D mesh
pathfinding and the NavigationServer2D facilitates the conversions.
When a guide uses just NavigationServer without the 2D or 3D suffix it usually works for both servers
When a guide uses just NavigationServer without the 2D or 3D suffix it usually works for both servers
by exchange Vector2(x, y) with Vector3(x, 0.0, z) or reverse.
Technically it is possible to use the tools for creating navigationmesh for the other
dimension, e..g. baking 2D navigationmesh with the 3D NavigationMesh when using
flat 3D source geometry or creating 3D flat navigationmesh with the
Technically it is possible to use the tools for creating navigationmesh for the other
dimension, e..g. baking 2D navigationmesh with the 3D NavigationMesh when using
flat 3D source geometry or creating 3D flat navigationmesh with the
polygon outline drawtools of NavigationRegion2D and NavigationPolygons.
Any RID created with the NavigationServer2D API works on the NavigationServer3D API
Any RID created with the NavigationServer2D API works on the NavigationServer3D API
as well and both 2D and 3D avoidance agents can exist on the same map.
.. note::
@@ -115,58 +115,58 @@ Afterwards the function waits for the next physics_frame before continuing with
.. code-tab:: gdscript GDScript
extends Node3D
func _ready():
# use call deferred to make sure the entire SceneTree 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()
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()
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 = PoolVector3Array([
var new_navigation_mesh: NavigationMesh = NavigationMesh.new()
var vertices: PackedVector3Array = PoolVector3Array([
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])
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(
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 Avoidance Callbacks
~~~~~~~~~~~~~~~~~~~~~~~~~~
If RVO avoidance agents are registered for avoidance callbacks the NavigationServer dispatches
If RVO avoidance agents are registered for avoidance callbacks the NavigationServer dispatches
their ``safe_velocity`` signals just before the PhysicsServer synchronisation.
To learn more about NavigationAgents see :ref:`doc_navigation_using_navigationagents`.
@@ -185,5 +185,5 @@ The simplified order of execution for NavigationAgents that use avoidance:
- PhysicsServer synchronises.
- physics frame ends.
Therefore moving a physicsbody actor in the callback function with the safe_velocity is perfectly thread- and physics-safe
Therefore moving a physicsbody actor in the callback function with the safe_velocity is perfectly thread- and physics-safe
as all happens inside the same physics_frame before the PhysicsServer commits to changes and does its own calculations.
@@ -16,16 +16,16 @@ The following two scripts will be used as references throughout this page.
extends Node
var my_field : String = "foo"
var my_field: String = "foo"
func print_node_name(node : Node) -> void:
func print_node_name(node: Node) -> void:
print(node.get_name())
func print_array(arr : Array) -> void:
func print_array(arr: Array) -> void:
for element in arr:
print(element)
func print_n_times(msg : String, n : int) -> void:
func print_n_times(msg: String, n: int) -> void:
for i in range(n):
print(msg)
+4 -4
View File
@@ -212,9 +212,9 @@ Attach a script to it named ``bot_stats.gd`` (or just create a new script, and t
extends Resource
@export var health : int
@export var sub_resource : Resource
@export var strings : PackedStringArray
@export var health: int
@export var sub_resource: Resource
@export var strings: PackedStringArray
# Make sure that every parameter has a default value.
# Otherwise, there will be problems with creating and editing
@@ -265,7 +265,7 @@ Now, create a :ref:`CharacterBody3D <class_CharacterBody3D>`, name it ``Bot``, a
extends CharacterBody3D
@export var stats : Resource
@export var stats: Resource
func _ready():
# Uses an implicit, duck-typed interface for any 'health'-compatible resources.
+1 -1
View File
@@ -69,7 +69,7 @@ Putting the above together we can use the following code as a base:
.. code-block:: gdscript
func enable_passthrough() -> bool:
var xr_interface : XRInterface = XRServer.primary_interface
var xr_interface: XRInterface = XRServer.primary_interface
if xr_interface and xr_interface.is_passthrough_supported():
return xr_interface.start_passthrough()
else:
+7 -7
View File
@@ -9,10 +9,10 @@ Introduction to the XR system in Godot
Godot provides a modular XR system that abstracts many of the different XR platform specifics away from the user.
At the core sits the :ref:`XRServer <class_xrserver>` which acts as a central interface to the XR system that allows users to discover interfaces and interact with the components of the XR system.
Each supported XR platform is implemented as an :ref:`XRInterface <class_xrinterface>`. Supported interfaces register themselves with the :ref:`XRServer <class_xrserver>` and can be queried with the ``find_interface`` method on the :ref:`XRServer <class_xrserver>`. When the desired interface is found it can be initialised by calling ``initialize`` on the interface.
Each supported XR platform is implemented as an :ref:`XRInterface <class_xrinterface>`. Supported interfaces register themselves with the :ref:`XRServer <class_xrserver>` and can be queried with the ``find_interface`` method on the :ref:`XRServer <class_xrserver>`. When the desired interface is found it can be initialised by calling ``initialize`` on the interface.
.. warning::
A registered interface means nothing more than that the interface is available, if the interface is not supported by the host system, initialization may fail and return ``false``. This can have many reasons and sadly the reasons differ from platform to platform. It can be because the user hasn't installed the required software, or that the user simply hasn't plugged in their headset. You as a developer must thus react properly on an interface failing to initialize.
A registered interface means nothing more than that the interface is available, if the interface is not supported by the host system, initialization may fail and return ``false``. This can have many reasons and sadly the reasons differ from platform to platform. It can be because the user hasn't installed the required software, or that the user simply hasn't plugged in their headset. You as a developer must thus react properly on an interface failing to initialize.
Due to the special requirements for output in XR, especially for head mounted devices that supply different images to each eye, the :ref:`XRServer <class_xrserver>` in Godot will override various features in the rendering system. For stand-alone devices this means the final output is handled by the :ref:`XRInterface <class_xrinterface>` and Godot's usual output system is disabled. For desktop XR devices that work as a second screen it is possible to dedicate a separate :ref:`Viewport <class_viewport>` to handle the XR output, leaving the main Godot window available for displaying alternative content.
@@ -23,8 +23,8 @@ Due to the special requirements for output in XR, especially for head mounted de
There are three XR specific node types that you will find in nearly all XR applications:
- :ref:`XROrigin3D <class_xrorigin3d>` represents, for all intents and purposes, the center point of your play space. That is an oversimplified statement but we'll go into more detail later. All objects tracked in physical space by the XR platform are positioned in relation to this point.
- :ref:`XRCamera3D <class_xrcamera3d>` represents the (stereo) camera that is used when rendering output for the XR device. The positioning of this node is controlled by the XR system and updated automatically using the tracking information provided by the XR platform.
- :ref:`XRController3D <class_xrcontroller3d>` represents a controller used by the player, commonly there are two, one held in each hand. These nodes give access to various states on these controllers and send out signals when the player presses buttons on them. The positioning of this node is controlled by the XR system and updated automatically using the tracking information provided by the XR platform.
- :ref:`XRCamera3D <class_xrcamera3d>` represents the (stereo) camera that is used when rendering output for the XR device. The positioning of this node is controlled by the XR system and updated automatically using the tracking information provided by the XR platform.
- :ref:`XRController3D <class_xrcontroller3d>` represents a controller used by the player, commonly there are two, one held in each hand. These nodes give access to various states on these controllers and send out signals when the player presses buttons on them. The positioning of this node is controlled by the XR system and updated automatically using the tracking information provided by the XR platform.
There are other XR related nodes and there is much more to say about these three nodes, but we'll get into that later on.
@@ -58,7 +58,7 @@ The default settings will get us started and we will go into detail in another s
Setting up the XR scene
-----------------------
Every XR application needs at least an :ref:`XROrigin3D <class_xrorigin3d>` and an :ref:`XRCamera3D <class_xrcamera3d>` node. Most will have two :ref:`XRController3D <class_xrcontroller3d>`, one for the left hand and one for the right. Keep in mind that the camera and controller nodes should be children of the origin node. Add these nodes to a new scene and rename the controller nodes to ``LeftHand`` and ``RightHand``, your scene should look something like this:
Every XR application needs at least an :ref:`XROrigin3D <class_xrorigin3d>` and an :ref:`XRCamera3D <class_xrcamera3d>` node. Most will have two :ref:`XRController3D <class_xrcontroller3d>`, one for the left hand and one for the right. Keep in mind that the camera and controller nodes should be children of the origin node. Add these nodes to a new scene and rename the controller nodes to ``LeftHand`` and ``RightHand``, your scene should look something like this:
.. image:: img/xr_basic_scene.png
@@ -78,7 +78,7 @@ Next we need to add a script to our root node. Add the following code into this
extends Node3D
var interface : XRInterface
var interface: XRInterface
func _ready():
interface = XRServer.find_interface("OpenXR")
@@ -91,7 +91,7 @@ Next we need to add a script to our root node. Add the following code into this
This code fragment assumes we are using OpenXR, if you wish to use any of the other interfaces you can change the ``find_interface`` call.
If you run your project at this point in time, everything will work but you will be in a dark world. So to finish off our starting point add a :ref:`DirectionalLight3D <class_directionallight3d>` and a :ref:`WorldEnvironment <class_worldenvironment>` node to your scene.
If you run your project at this point in time, everything will work but you will be in a dark world. So to finish off our starting point add a :ref:`DirectionalLight3D <class_directionallight3d>` and a :ref:`WorldEnvironment <class_worldenvironment>` node to your scene.
You may wish to also add a mesh instance as a child to each controller node just to temporarily visualise them.
Make sure you configure a sky in your world environment.
+18 -18
View File
@@ -81,10 +81,10 @@ We determine where the player is right now, and attempt to move our character bo
var current_velocity = $CharacterBody3D.velocity
# Remember where our player body currently is
var org_player_body : Vector3 = $CharacterBody3D.global_transform.origin
var org_player_body: Vector3 = $CharacterBody3D.global_transform.origin
# Determine where our player body should be
var player_body_location : Vector3 = $XRCamera3D.transform * $XRCamera3D/Neck.transform.origin
var player_body_location: Vector3 = $XRCamera3D.transform * $XRCamera3D/Neck.transform.origin
player_body_location.y = 0.0
player_body_location = global_transform * player_body_location
@@ -130,8 +130,8 @@ This function should obtain the necessary input and return the rotational speed
func _copy_player_rotation_to_character_body():
# We only copy our forward direction to our character body, we ignore tilt
var camera_forward : Vector3 = -$XRCamera3D.global_transform.basis.z
var body_forward : Vector3 = Vector3(camera_forward.x, 0.0, camera_forward.z)
var camera_forward: Vector3 = -$XRCamera3D.global_transform.basis.z
var body_forward: Vector3 = Vector3(camera_forward.x, 0.0, camera_forward.z)
$CharacterBody3D.global_transform.basis = Basis.looking_at(body_forward, Vector3.UP)
@@ -142,7 +142,7 @@ This function should obtain the necessary input and return the rotational speed
# We are going to rotate the origin around the player
var player_position = $CharacterBody3D.global_transform.origin - global_transform.origin
t1.origin = -player_position
t2.origin = player_position
rot = rot.rotated(Vector3(0.0, 1.0, 0.0), _get_rotational_input() * delta)
@@ -169,7 +169,7 @@ Just like with the rotation the inputs differ from project to project so we are
This function should obtain the necessary input and return a directional vector scaled to the required velocity.
.. note:
Just like with rotation we're keeping it simple. Here too it is advisable to look at adding comfort settings.
Just like with rotation we're keeping it simple. Here too it is advisable to look at adding comfort settings.
.. code-block:: gdscript
@@ -181,14 +181,14 @@ This function should obtain the necessary input and return a directional vector
func _process_movement_on_input(delta):
# Remember where our player body currently is
var org_player_body : Vector3 = $CharacterBody3D.global_transform.origin
var org_player_body: Vector3 = $CharacterBody3D.global_transform.origin
# We start with applying gravity
$CharacterBody3D.velocity.y -= gravity * delta
# Now we add in our movement
var input : Vector2 = _get_movement_input()
var movement : Vector3 = ($CharacterBody3D.global_transform.basis * Vector3(input.x, 0, input.y))
var input: Vector2 = _get_movement_input()
var movement: Vector3 = ($CharacterBody3D.global_transform.basis * Vector3(input.x, 0, input.y))
$CharacterBody3D.velocity.x = movement.x
$CharacterBody3D.velocity.z = movement.z
@@ -197,7 +197,7 @@ This function should obtain the necessary input and return a directional vector
# And now apply the actual movement to our origin
global_transform.origin += $CharacterBody3D.global_transform.origin - org_player_body
func _physics_process(delta):
var is_colliding = _process_on_physical_movement(delta)
if !is_colliding:
@@ -237,19 +237,19 @@ This will ensure that the players location stays in sync with the character body
var current_velocity = velocity
# Start by rotating the player to face the same way our real player is
var camera_basis : Basis = origin_node.transform.basis * camera_node.transform.basis
var forward : Vector2 = Vector2(camera_basis.z.x, camera_basis.z.z)
var angle : float = forward.angle_to(Vector2(0.0, 1.0))
var camera_basis: Basis = origin_node.transform.basis * camera_node.transform.basis
var forward: Vector2 = Vector2(camera_basis.z.x, camera_basis.z.z)
var angle: float = forward.angle_to(Vector2(0.0, 1.0))
# Rotate our character body
# Rotate our character body
transform.basis = transform.basis.rotated(Vector3.UP, angle)
# Reverse this rotation our origin node
origin_node.transform = Transform3D().rotated(Vector3.UP, -angle) * origin_node.transform
# Now apply movement, first move our player body to the right location
var org_player_body : Vector3 = global_transform.origin
var player_body_location : Vector3 = origin_node.transform * camera_node.transform * neck_position_node.transform.origin
var org_player_body: Vector3 = global_transform.origin
var player_body_location: Vector3 = origin_node.transform * camera_node.transform * neck_position_node.transform.origin
player_body_location.y = 0.0
player_body_location = global_transform * player_body_location
@@ -262,7 +262,7 @@ This will ensure that the players location stays in sync with the character body
# Return our value
velocity = current_velocity
if (player_body_location - global_transform.origin).length() > 0.01:
# We'll talk more about what we'll do here later on
return true
@@ -379,7 +379,7 @@ Our solutions up above would allow us to add this logic into the code at the end
Further improvements to the code presented could be:
- allowing controller input as long as this distance is still small,
- still applying gravity to the player even when controller input is disabled.
- still applying gravity to the player even when controller input is disabled.
Further suggestions for improvements
------------------------------------