From 486d76d00c329b402928fee35b90462d6a12795b Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 29 Apr 2024 21:20:12 +0200 Subject: [PATCH] Cleanups. --- .../10_physics/01_physics_introduction.md | 54 +++---- 03_usage/10_physics/02_rigid_body.md | 12 +- 03_usage/10_physics/03_using_area_2d.md | 24 ++-- .../10_physics/04_using_kinematic_body_2d.md | 39 ++--- 03_usage/10_physics/05_ray_casting.md | 24 ++-- 03_usage/10_physics/06_ragdoll_system.md | 27 ++-- .../10_physics/07_kinematic_character_2d.md | 15 +- 03_usage/10_physics/08_soft_body.md | 9 +- 03_usage/10_physics/09_collision_shapes_3d.md | 15 +- ...physics_interpolation_quick_start_guide.md | 3 +- .../02_physics_interpolation_introduction.md | 39 ++--- .../03_using_physics_interpolation.md | 23 ++- .../04_advanced_physics_interpolation.md | 27 ++-- .../01_navigation_introduction_2d.md | 125 +++++----------- .../02_navigation_introduction_3d.md | 129 +++++------------ .../03_navigation_using_navigationservers.md | 56 ++++---- .../04_navigation_using_navigationmaps.md | 50 +++---- .../05_navigation_using_navigationregions.md | 46 +++--- .../06_navigation_using_navigationmeshes.md | 85 +++++------ .../07_navigation_using_navigationpaths.md | 51 ++++--- ...gation_using_navigationpathqueryobjects.md | 34 ++--- .../09_navigation_using_navigationagents.md | 133 +++++++++--------- ...10_navigation_using_navigationobstacles.md | 48 +++---- .../11_navigation_using_navigationlinks.md | 39 +++-- .../12_navigation_using_navigationlayers.md | 25 ++-- .../13_navigation_debug_tools.md | 66 ++++----- .../14_navigation_connecting_navmesh.md | 36 ++--- .../15_navigation_different_actor_types.md | 13 +- ...6_navigation_different_actor_locomotion.md | 15 +- ..._navigation_different_actor_area_access.md | 10 +- .../18_navigation_optimizing_performance.md | 38 +++-- .../19_real_time_navigation_3d.md | 22 +-- 32 files changed, 530 insertions(+), 802 deletions(-) diff --git a/03_usage/10_physics/01_physics_introduction.md b/03_usage/10_physics/01_physics_introduction.md index f3506e4..7f1ce37 100644 --- a/03_usage/10_physics/01_physics_introduction.md +++ b/03_usage/10_physics/01_physics_introduction.md @@ -1,7 +1,6 @@ -Physics introduction -==================== +# Physics introduction In game development, you often need to know when two objects in the game intersect or come into contact. This is known as **collision detection**. @@ -24,8 +23,7 @@ Note: and collision shape has a direct equivalent in 3D and in most cases they work in much the same way. -Collision objects ------------------ +## Collision objects Pandemonium offers four kinds of physics bodies, extending `CollisionObject2D`: @@ -52,15 +50,13 @@ The other three bodies extend `PhysicsBody2D`: A body that provides collision detection, but no physics. All movement and collision response must be implemented in code. -Physics material -~~~~~~~~~~~~~~~~ +### Physics material Static bodies and rigid bodies can be configured to use a `physics material ( PhysicsMaterial )`. This allows adjusting the friction and bounce of an object, and set if it's absorbent and/or rough. -Collision shapes -~~~~~~~~~~~~~~~~ +### Collision shapes A physics body can hold any number of `Shape2D` objects as children. These shapes are used to define the object's collision bounds @@ -82,8 +78,7 @@ These nodes allow you to draw the shape directly in the editor workspace. ![](img/player_coll_shape.png) -Physics process callback -~~~~~~~~~~~~~~~~~~~~~~~~ +### Physics process callback The physics engine may spawn multiple threads to improve performance, so it can use up to a full frame to process physics. Because of this, the value @@ -107,8 +102,7 @@ Note: -Collision layers and masks -~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Collision layers and masks One of the most powerful, but frequently misunderstood, collision features is the collision layer system. This system allows you to build up complex @@ -135,8 +129,7 @@ be assigned in Project Settings -> Layer Names. ![](img/physics_layer_names.png) -GUI example -^^^^^^^^^^^ +#### GUI example You have four node types in your game: Walls, Player, Enemy, and Coin. Both Player and Enemy should collide with Walls. The Player node should detect @@ -153,8 +146,7 @@ interact with. For example, the Player's settings would look like this: -Code example -^^^^^^^^^^^^ +#### Code example In function calls, layers are specified as a bitmask. Where a function enables all layers by default, the layer mask will be given as `0x7fffffff`. Your code @@ -182,8 +174,7 @@ would be as follows: ``` -Area2D ------- +## Area2D Area nodes provide **detection** and **influence**. They can detect when objects overlap and emit signals when bodies enter or exit. Areas can also @@ -200,8 +191,7 @@ There are three main uses for `Area2D`: By default, areas also receive mouse and touchscreen input. -StaticBody2D ------------- +## StaticBody2D A static body is one that is not moved by the physics engine. It participates in collision detection, but does not move in response to the collision. However, @@ -217,8 +207,7 @@ Example uses for `StaticBody2D`: - Conveyor belts - Walls and other obstacles -RigidBody2D ------------ +## RigidBody2D This is the node that implements simulated 2D physics. You do not control a `RigidBody2D` directly. Instead, you apply forces @@ -238,8 +227,7 @@ A sleeping body acts like a static body, and its forces are not calculated by the physics engine. The body will wake up when forces are applied, either by a collision or via code. -Rigid body modes -~~~~~~~~~~~~~~~~ +### Rigid body modes A rigid body can be set to one of four modes: @@ -248,8 +236,7 @@ A rigid body can be set to one of four modes: - **Character** - Similar to "Rigid" mode, but the body cannot rotate. - **Kinematic** - The body behaves like a `KinematicBody2D` and must be moved by code. -Using RigidBody2D -~~~~~~~~~~~~~~~~~ +### Using RigidBody2D One of the benefits of using a rigid body is that a lot of behavior can be had "for free" without writing any code. For example, if you were making an @@ -300,8 +287,7 @@ Note: force to it, or by disabling the `can_sleep` property. Be aware that this can have a negative effect on performance. -Contact reporting -~~~~~~~~~~~~~~~~~ +### Contact reporting By default, rigid bodies do not keep track of contacts, because this can require a huge amount of memory if many bodies are in the scene. To enable @@ -314,8 +300,7 @@ Contact monitoring via signals can be enabled via the `contact_monitor` property. See `RigidBody2D` for the list of available signals. -KinematicBody2D ---------------- +## KinematicBody2D `KinematicBody2D` bodies detect collisions with other bodies, but are not affected by physics properties like gravity or friction. @@ -328,15 +313,13 @@ These methods move the body along a given vector, and it will instantly stop if a collision is detected with another body. After the body has collided, any collision response must be coded manually. -Kinematic collision response -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Kinematic collision response After a collision, you may want the body to bounce, to slide along a wall, or to alter the properties of the object it hit. The way you handle collision response depends on which method you used to move the KinematicBody2D. -`move_and_collide` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### `move_and_collide` When using `move_and_collide()`, the function returns a `KinematicCollision2D` object, which contains @@ -374,8 +357,7 @@ gdscript GDScript velocity = velocity.bounce(collision_info.normal) ``` -`move_and_slide` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### `move_and_slide` Sliding is a common collision response; imagine a player moving along walls in a top-down game or running up and down slopes in a platformer. While it's diff --git a/03_usage/10_physics/02_rigid_body.md b/03_usage/10_physics/02_rigid_body.md index a25c768..d6c5188 100644 --- a/03_usage/10_physics/02_rigid_body.md +++ b/03_usage/10_physics/02_rigid_body.md @@ -1,16 +1,13 @@ -Using RigidBody -=============== +# Using RigidBody -What is a rigid body? ---------------------- +## What is a rigid body? A rigid body is one that is directly controlled by the physics engine in order to simulate the behavior of physical objects. In order to define the shape of the body, it must have one or more `Shape` objects assigned. Note that setting the position of these shapes will affect the body's center of mass. -How to control a rigid body ---------------------------- +## How to control a rigid body A rigid body's behavior can be altered by setting its properties, such as mass and weight. A physics material needs to be added to the rigid body to adjust its friction and bounce, @@ -25,8 +22,7 @@ As an example, consider a rigid body that you want to rotate so that it points t The fact that you can't use `set_global_transform()` or `look_at()` methods doesn't mean that you can't have full control of a rigid body. Instead, you can control it by using the `integrate_forces()` callback. In this method, you can add *forces*, apply *impulses*, or set the *velocity* in order to achieve any movement you desire. -The "look at" method --------------------- +## The "look at" method As described above, using the Spatial node's `look_at()` method can't be used each frame to follow a target. Here is a custom `look_at()` method that will work reliably with rigid bodies: diff --git a/03_usage/10_physics/03_using_area_2d.md b/03_usage/10_physics/03_using_area_2d.md index a260f33..c320ed9 100644 --- a/03_usage/10_physics/03_using_area_2d.md +++ b/03_usage/10_physics/03_using_area_2d.md @@ -1,10 +1,8 @@ -Using Area2D -============ +# Using Area2D -Introduction ------------- +## Introduction Pandemonium offers a number of collision objects to provide both collision detection and response. Trying to decide which one to use for your project can be confusing. @@ -16,16 +14,14 @@ Note: This document assumes you're familiar with Pandemonium's various physics bodies. Please read `doc_physics_introduction` first. -What is an area? ----------------- +## What is an area? An Area2D defines a region of 2D space. In this space you can detect other `CollisionObject2D` nodes overlapping, entering, and exiting. Areas also allow for overriding local physics properties. We'll explore each of these functions below. -Area properties ---------------- +## Area properties Areas have many properties you can use to customize their behavior. @@ -45,8 +41,7 @@ apply an audio effect when the player moves through. Note that Area2D extends `CollisionObject2D`, so it also provides properties inherited from that class, such as `input_pickable`. -Overlap detection ------------------ +## Overlap detection Perhaps the most common use of Area2D nodes is for contact and overlap detection. When you need to know that two objects have touched, but don't need physical @@ -89,8 +84,7 @@ Some other usage examples: See the `doc_your_first_2d_game` for an example of using Area2D in a game. -Area influence --------------- +## Area influence The second major use for area nodes is to alter physics. By default, the area won't do this, but you can enable this with the *Space Override* property. When @@ -112,16 +106,14 @@ The physics properties that can be overridden are: - *Linear Damp* - How quickly objects stop moving - linear velocity lost per second. - *Angular Damp* - How quickly objects stop spinning - angular velocity lost per second. -Point gravity -~~~~~~~~~~~~~ +### Point gravity The *Gravity Point* property allows you to create an "attractor". Gravity in the area will be calculated towards a point, given by the *Gravity Vec* property. Values are relative to the Area2D, so for example using `(0, 0)` will attract objects to the center of the area. -Examples -~~~~~~~~ +### Examples The example project attached below has three areas demonstrating physics override. diff --git a/03_usage/10_physics/04_using_kinematic_body_2d.md b/03_usage/10_physics/04_using_kinematic_body_2d.md index baee630..4e3a4b9 100644 --- a/03_usage/10_physics/04_using_kinematic_body_2d.md +++ b/03_usage/10_physics/04_using_kinematic_body_2d.md @@ -1,10 +1,8 @@ -Using KinematicBody2D -===================== +# Using KinematicBody2D -Introduction ------------- +## Introduction Pandemonium offers several collision objects to provide both collision detection and response. Trying to decide which one to use for your project can be confusing. @@ -17,8 +15,7 @@ Note: This document assumes you're familiar with Pandemonium's various physics bodies. Please read `doc_physics_introduction` first. -What is a kinematic body? -------------------------- +## What is a kinematic body? `KinematicBody2D` is for implementing bodies that are controlled via code. Kinematic bodies detect collisions with other bodies when moving, but are not affected by @@ -31,8 +28,7 @@ Tip: but you must calculate the movement in code. The physics engine will not move a `KinematicBody2D`. -Movement and collision ----------------------- +## Movement and collision When moving a `KinematicBody2D`, you should not set its `position` property directly. Instead, you use the `move_and_collide()` or `move_and_slide()` methods. @@ -46,8 +42,7 @@ Warning: The two movement methods serve different purposes, and later in this tutorial, you'll see examples of how they work. -`move_and_collide` -~~~~~~~~~~~~~~~~~~~~ +### `move_and_collide` This method takes one parameter: a `Vector2` indicating the body's relative movement. Typically, this is your velocity vector multiplied by the @@ -59,8 +54,7 @@ method will return a `KinematicCollision2D` object. and the colliding object. Using this data, you can calculate your collision response. -`move_and_slide` -~~~~~~~~~~~~~~~~~~ +### `move_and_slide` The `move_and_slide()` method is intended to simplify the collision response in the common case where you want one body to slide along the other. @@ -100,8 +94,7 @@ When this parameter is `true`, the body can push `RigidBody2D` nodes, ignoring their mass, but won't detect collisions with them. If it's `false` the body will collide with rigid bodies and stop. -`move_and_slide_with_snap` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### `move_and_slide_with_snap` This method adds some additional functionality to `move_and_slide()` by adding the `snap` parameter. As long as this vector is in contact with the ground, the @@ -110,8 +103,7 @@ snapping when jumping, for example. You can do this either by setting `snap` to `Vector2.ZERO` or by using `move_and_slide()` instead. -Detecting collisions --------------------- +## Detecting collisions When using `move_and_collide()` the function returns a `KinematicCollision2D` directly, and you can use this in your code. @@ -141,8 +133,7 @@ Note: See `KinematicCollision2D` for details on what collision data is returned. -Which movement method to use? ------------------------------ +## Which movement method to use? A common question from new Pandemonium users is: "How do you decide which movement function to use?" Often, the response is to use `move_and_slide()` because @@ -184,14 +175,12 @@ collides up to five times by default. At the end of the process, the function returns the character's new velocity that we can store in our `velocity` variable, and use on the next frame. -Examples --------- +## Examples To see these examples in action, download the sample project: :download:`using_kinematic2d.zip (files/using_kinematic2d.zip )`. -Movement and walls -~~~~~~~~~~~~~~~~~~ +### Movement and walls If you've downloaded the sample project, this example is in "BasicMovement.tscn". @@ -254,8 +243,7 @@ Note that we removed `delta` from the velocity calculation. collision object. This is useful for a great many game types, and may be all you need to get the behavior you want. -Bouncing/reflecting -~~~~~~~~~~~~~~~~~~~ +### Bouncing/reflecting What if you don't want a sliding collision response? For this example ("BounceandCollide.tscn" in the sample project), we have a character shooting bullets and we want the bullets to @@ -341,8 +329,7 @@ the Wall to demonstrate this. ![](img/k2d_bullet_bounce.gif) -Platformer movement -~~~~~~~~~~~~~~~~~~~ +### Platformer movement Let's try one more popular example: the 2D platformer. `move_and_slide()` is ideal for quickly getting a functional character controller up and running. diff --git a/03_usage/10_physics/05_ray_casting.md b/03_usage/10_physics/05_ray_casting.md index ffeb493..b707bb0 100644 --- a/03_usage/10_physics/05_ray_casting.md +++ b/03_usage/10_physics/05_ray_casting.md @@ -1,10 +1,8 @@ -Ray-casting -=========== +# Ray-casting -Introduction ------------- +## Introduction One of the most common tasks in game development is casting a ray (or custom shaped object) and checking what it hits. This enables complex @@ -21,8 +19,7 @@ is. Many times, though, ray-casting needs to be a more interactive process so a way to do this by code must exist. -Space ------ +## Space In the physics world, Pandemonium stores all the low level collision and physics information in a *space*. The current 2d space (for 2D Physics) @@ -34,8 +31,7 @@ The resulting space `RID` can be used in `PhysicsServer` and `Physics2DServer` respectively for 3D and 2D. -Accessing space ---------------- +## Accessing space Pandemonium physics runs by default in the same thread as game logic, but may be set to run on a separate thread to work more efficiently. Due to @@ -77,8 +73,7 @@ gdscript GDScript var space_state = get_world().direct_space_state ``` -Raycast query -------------- +## Raycast query For performing a 2D raycast query, the method `Physics2DDirectSpaceState.intersect_ray()` @@ -120,8 +115,7 @@ data: The data is similar in 3D space, using Vector3 coordinates. -Collision exceptions --------------------- +## Collision exceptions A common use case for ray casting is to enable a character to gather data about the world around it. One problem with this is that the same character @@ -147,8 +141,7 @@ gdscript GDScript The exceptions array can contain objects or RIDs. -Collision Mask --------------- +## Collision Mask While the exceptions method works fine for excluding the parent body, it becomes very inconvenient if you need a large and/or dynamic list of exceptions. In @@ -171,8 +164,7 @@ gdscript GDScript See `doc_physics_introduction_collision_layer_code_example` for details on how to set the collision mask. -3D ray casting from screen --------------------------- +## 3D ray casting from screen Casting a ray from screen to 3D physics space is useful for object picking. There is not much need to do this because diff --git a/03_usage/10_physics/06_ragdoll_system.md b/03_usage/10_physics/06_ragdoll_system.md index 5386fec..1980a22 100644 --- a/03_usage/10_physics/06_ragdoll_system.md +++ b/03_usage/10_physics/06_ragdoll_system.md @@ -1,10 +1,8 @@ -Ragdoll system -============== +# Ragdoll system -Introduction ------------- +## Introduction Since version 3.1, Pandemonium supports ragdoll physics. Ragdolls rely on physics simulation to create realistic procedural animation. They are used for death animations in many games. @@ -13,11 +11,9 @@ In this tutorial, we will be using the Platformer3D demo to set up a ragdoll. Note: You can download the Platformer3D demo on `GitHub ( https://github.com/Relintai/pandemonium_engine-demo-projects/tree/master/3d/platformer )` or using the `Asset Library ( https://pandemoniumengine.org/asset-library/asset/125 )`. -Setting up the ragdoll ----------------------- +## Setting up the ragdoll -Creating physical bones -~~~~~~~~~~~~~~~~~~~~~~~ +### Creating physical bones Like many other features in the engine, there is a node to set up a ragdoll: the `PhysicalBone` node. To simplify the setup, you can generate `PhysicalBone` nodes with the "Create physical skeleton" feature in the skeleton node. @@ -31,8 +27,7 @@ Click it and select the `Create physical skeleton` option. Pandemonium will gene Some of the generated bones aren't necessary: the `MASTER` bone for example. So we're going to clean up the skeleton by removing them. -Cleaning up the skeleton -~~~~~~~~~~~~~~~~~~~~~~~~ +### Cleaning up the skeleton Each `PhysicalBone` the engine needs to simulate has a performance cost, so you want to remove every bone that is too small to make a difference in the simulation, as well as all utility bones. @@ -40,15 +35,13 @@ For example, if we take a humanoid, you do not want to have physical bones for e Remove these physical bones: `MASTER`, `waist`, `neck`, `headtracker`. This gives us an optimized skeleton and makes it easier to control the ragdoll. -Collision shape adjustment -~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Collision shape adjustment The next task is adjusting the collision shape and the size of physical bones to match the part of the body that each bone should simulate. ![](img/ragdoll_shape_adjust.gif) -Joints adjustment -~~~~~~~~~~~~~~~~~ +### Joints adjustment Once you adjusted the collision shapes, your ragdoll is almost ready. You just want to adjust the pin joints to get a better simulation. `PhysicalBone` nodes have an unconstrained pin joint assigned to them by default. To change the pin joint, select the `PhysicalBone` and change the constraint type in the `Joint` section. There, you can change the constraint's orientation and its limits. @@ -58,8 +51,7 @@ This is the final result: ![](img/ragdoll_result.png) -Simulating the ragdoll ----------------------- +## Simulating the ragdoll The ragdoll is now ready to use. To start the simulation and play the ragdoll animation, you need to call the `physical_bones_start_simulation` method. Attach a script to the skeleton node and call the method in the `ready` method: @@ -76,8 +68,7 @@ You can also limit the simulation to only a few bones. To do so, pass the bone n ![](img/ ragdoll_sim_part.gif) -Collision layer and mask -~~~~~~~~~~~~~~~~~~~~~~~~ +### Collision layer and mask Make sure to set up your collision layers and masks properly so the `KinematicBody`'s capsule doesn't get in the way of the physics simulation: diff --git a/03_usage/10_physics/07_kinematic_character_2d.md b/03_usage/10_physics/07_kinematic_character_2d.md index 43e8d2f..32427a3 100644 --- a/03_usage/10_physics/07_kinematic_character_2d.md +++ b/03_usage/10_physics/07_kinematic_character_2d.md @@ -1,10 +1,8 @@ -Kinematic character (2D) -======================== +# Kinematic character (2D) -Introduction -~~~~~~~~~~~~ +### Introduction Yes, the name sounds strange. "Kinematic Character". What is that? The reason for the name is that, when physics engines came out, they were called @@ -44,8 +42,7 @@ Basically, the old-school way of handling collisions (which is not necessarily simpler under the hood, but well hidden and presented as a nice and simple API). -Physics process -~~~~~~~~~~~~~~~ +### Physics process To manage the logic of a kinematic body or character, it is always advised to use physics process, because it's called before physics step and its execution is @@ -64,8 +61,7 @@ gdscript GDScript ``` -Scene setup -~~~~~~~~~~~ +### Scene setup To have something to test, here's the scene (from the tilemap tutorial): :download:`kbscene.zip (files/kbscene.zip )`. We'll be creating a new scene @@ -95,8 +91,7 @@ map scene the main one, so it runs when pressing play. ![](img/kbinstance.png) -Moving the kinematic character -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Moving the kinematic character Go back to the character scene, and open the script, the magic begins now! Kinematic body will do nothing by default, but it has a diff --git a/03_usage/10_physics/08_soft_body.md b/03_usage/10_physics/08_soft_body.md index 00af8cf..910480e 100644 --- a/03_usage/10_physics/08_soft_body.md +++ b/03_usage/10_physics/08_soft_body.md @@ -1,13 +1,11 @@ -Using SoftBody -============== +# Using SoftBody Soft bodies (or *soft-body dynamics*) simulate movement, changing shape and other physical properties of deformable objects. This can for example be used to simulate cloth or to create more realistic characters. -Basic set-up -~~~~~~~~~~~~ +### Basic set-up A `SoftBody` node is used for soft body simulations. @@ -29,8 +27,7 @@ Play the scene to view the simulation. Tip: To improve the simulation's result, increase the `Simulation Precision`, this will give significant improvement at the cost of performance. -Cloak simulation -~~~~~~~~~~~~~~~~ +### Cloak simulation Let's make a cloak in the Platformer3D demo. diff --git a/03_usage/10_physics/09_collision_shapes_3d.md b/03_usage/10_physics/09_collision_shapes_3d.md index 4f4edea..c257b97 100644 --- a/03_usage/10_physics/09_collision_shapes_3d.md +++ b/03_usage/10_physics/09_collision_shapes_3d.md @@ -1,7 +1,6 @@ -Collision shapes (3D) -===================== +# Collision shapes (3D) This guide explains: @@ -23,8 +22,7 @@ Note: When you add multiple collision shapes to a single PhysicsBody, you don't have to worry about them overlapping. They won't "collide" with each other. -Primitive collision shapes --------------------------- +## Primitive collision shapes Pandemonium provides the following primitive collision shape types: @@ -41,8 +39,7 @@ We recommend favoring primitive shapes for dynamic objects such as RigidBodies and KinematicBodies as their behavior is the most reliable. They often provide better performance as well. -Convex collision shapes ------------------------ +## Convex collision shapes `Convex collision shapes` are a compromise between primitive collision shapes and concave collision shapes. They can @@ -73,8 +70,7 @@ viewport. The editor exposes two generation modes: of performance. For objects with medium complexity, it will likely be faster than using a single concave collision shape. -Concave or trimesh collision shapes ------------------------------------ +## Concave or trimesh collision shapes `Concave collision shapes`, also called trimesh collision shapes, can take any form, from a few triangles to thousands of @@ -128,8 +124,7 @@ See also: automatically. See `doc_importing_scenes_import_hints` in the documentation for more information. -Performance caveats -------------------- +## Performance caveats You aren't limited to a single collision shape per PhysicsBody. Still, we recommend keeping the number of shapes as low as possible to improve diff --git a/03_usage/10_physics/interpolation/01_physics_interpolation_quick_start_guide.md b/03_usage/10_physics/interpolation/01_physics_interpolation_quick_start_guide.md index 74d595e..965f747 100644 --- a/03_usage/10_physics/interpolation/01_physics_interpolation_quick_start_guide.md +++ b/03_usage/10_physics/interpolation/01_physics_interpolation_quick_start_guide.md @@ -1,7 +1,6 @@ -Quick start guide -================= +# Quick start guide - Turn on physics interpolation: `ProjectSettings.physics/common/physics_interpolation( ProjectSettings_property_physics/common/physics_interpolation )` - Make sure you move objects and run your game logic in `physics_process()` rather than `process()`. This includes moving objects directly *and indirectly* (by e.g. moving a parent, or using another mechanism to automatically move nodes). diff --git a/03_usage/10_physics/interpolation/02_physics_interpolation_introduction.md b/03_usage/10_physics/interpolation/02_physics_interpolation_introduction.md index 16f9c09..4135921 100644 --- a/03_usage/10_physics/interpolation/02_physics_interpolation_introduction.md +++ b/03_usage/10_physics/interpolation/02_physics_interpolation_introduction.md @@ -1,10 +1,8 @@ -Introduction -============ +# Introduction -Physics ticks and rendered frames -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Physics ticks and rendered frames One key concept to understand in Pandemonium is the distinction between physics ticks (sometimes referred to as iterations or physics frames), and rendered frames. The physics proceeds at a fixed tick rate (set in `ProjectSettings.physics/common/physics_fps( ProjectSettings_property_physics/common/physics_fps )`), which defaults to 60 ticks per second. @@ -18,18 +16,15 @@ This problem is easier to understand if we consider an extreme scenario. If you This jump can be seen in other combinations of tick / frame rate as glitches, or jitter, caused by this staircasing effect due to the discrepancy between physics tick time and rendered frame time. -What can we do about frames and ticks being out of sync? --------------------------------------------------------- +## What can we do about frames and ticks being out of sync? -Lock the tick / frame rate together? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Lock the tick / frame rate together? The most obvious solution is to get rid of the problem, by ensuring there is a physics tick that coincides with every frame. This used to be the approach on old consoles and fixed hardware computers. If you know that every player will be using the same hardware, you can ensure it is fast enough to calculate ticks and frames at e.g. 50 FPS, and you will be sure it will work great for everybody. However, modern games are often no longer made for fixed hardware. You will often be planning to release on desktop computers, mobiles and more, all of which have huge variations in performance, as well as different monitor refresh rates. We need to come up with a better way of dealing with the problem. -Adapt the tick rate? -^^^^^^^^^^^^^^^^^^^^ +#### Adapt the tick rate? Instead of designing the game at a fixed physics tick rate, we could allow the tick rate to scale according to the end users hardware. We could for example use a fixed tick rate that works for that hardware, or even vary the duration of each physics tick to match a particular frame duration. @@ -37,8 +32,7 @@ This works, but there is a problem. Physics (*and game logic*, which is often al This can make quality assurance difficult with hard to reproduce bugs, especially in AAA games where problems of this sort can be very costly. This can also be problematic for multiplayer games for competitive integrity, as running the game at certain tick rates may be more advantageous than others. -Lock the tick rate, but use interpolation to smooth frames in between physics ticks -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Lock the tick rate, but use interpolation to smooth frames in between physics ticks This has become one of the most popular approaches to dealing with the problem. It is supported by Pandemonium 3.5 and later in 3D (although it is optional and disabled by default). @@ -52,8 +46,7 @@ Why do we need the previous position *(in fact the entire transform, including r ![](img/fti_graph_interpolated.png) -Linear interpolation -^^^^^^^^^^^^^^^^^^^^ +#### Linear interpolation The simplest way to achieve this is linear interpolation, or lerping, which you may have used before. @@ -62,8 +55,7 @@ Let us consider only the position, and a situation where we know that the previo Note: Although the maths is explained here, you do not have to worry about the details, as this step will be performed for you. Under the hood, Pandemonium may use more complex forms of interpolation, but linear interpolation is the easiest in terms of explanation. -The physics interpolation fraction -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### The physics interpolation fraction If our physics ticks are happening 10 times per second (for this example), what happens if our rendered frame takes place at time 0.12 seconds? We can do some math to figure out where the object would be to obtain a smooth motion between the two ticks. @@ -76,8 +68,7 @@ First of all, we have to calculate how far through the physics tick we want the This is called the **physics interpolation fraction**, and is handily calculated for you by Pandemonium. It can be retrieved on any frame by calling `Engine.get_physics_interpolation_fraction( Engine_method_get_physics_interpolation_fraction )`. -Calculating the interpolated position -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Calculating the interpolated position Once we have the interpolation fraction, we can insert it into a standard linear interpolation equation. The X coordinate would thus be: @@ -102,22 +93,19 @@ Let's break that down: Note: Although this example interpolates the position, the same thing can be done with the rotation and scale of objects. It is not necessary to know the details as Pandemonium will do all this for you. -Smoothed transformations between physics ticks? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Smoothed transformations between physics ticks? Putting all this together shows that it should be possible to have a nice smooth estimation of the transform of objects between the current and previous physics tick. But wait, you may have noticed something. If we are interpolating between the current and previous ticks, we are not estimating the position of the object *now*, we are estimating the position of the object in the past. To be exact, we are estimating the position of the object *between 1 and 2 ticks* into the past. -In the past -^^^^^^^^^^^ +#### In the past What does this mean? This scheme does work, but it does mean we are effectively introducing a delay between what we see on the screen, and where the objects *should* be. In practice, most people won't notice this delay, or rather, it is typically not *objectionable*. There are already significant delays involved in games, we just don't typically notice them. The most significant effect is there can be a slight delay to input, which can be a factor in fast twitch games. In some of these fast input situations, you may wish to turn off physics interpolation and use a different scheme, or use a high tick rate, which mitigates these delays. -Why look into the past? Why not predict the future? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Why look into the past? Why not predict the future? There is an alternative to this scheme, which is: instead of interpolating between the previous and current tick, we use maths to *extrapolate* into the future. We try to predict where the object *will be*, rather than show it where it was. This can be done and may be offered as an option in future, but there are some significant downsides: @@ -126,8 +114,7 @@ There is an alternative to this scheme, which is: instead of interpolating betwe - Providing the movement speed is slow, these incorrect predictions may not be too much of a problem. - When a prediction was incorrect, the object may have to jump or snap back onto the corrected path. This can be visually jarring. -Fixed timestep interpolation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Fixed timestep interpolation In Pandemonium this whole system is referred to as physics interpolation, but you may also hear it referred to as **"fixed timestep interpolation"**, as it is interpolating between objects moved with a fixed timestep (physics ticks per second). In some ways the second term is more accurate, because it can also be used to interpolate objects that are not driven by physics. diff --git a/03_usage/10_physics/interpolation/03_using_physics_interpolation.md b/03_usage/10_physics/interpolation/03_using_physics_interpolation.md index 00783ff..a4d41a7 100644 --- a/03_usage/10_physics/interpolation/03_using_physics_interpolation.md +++ b/03_usage/10_physics/interpolation/03_using_physics_interpolation.md @@ -1,13 +1,13 @@ -Using physics interpolation -=========================== +# Using physics interpolation + + How do we incorporate physics interpolation into a Pandemonium game? Are there any caveats? We have tried to make the system as easy to use as possible, and many existing games will work with few changes. That said there are some situations which require special treatment, and these will be described. -Turn on the physics interpolation setting -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Turn on the physics interpolation setting The first step is to turn on physics interpolation in `ProjectSettings.physics/common/physics_interpolation( ProjectSettings_property_physics/common/physics_interpolation )`. You can now run your game. @@ -18,8 +18,7 @@ Tip: To convert an existing game to use interpolation, it is highly recommended that you temporarily set `ProjectSettings.physics/common/physics_fps( ProjectSettings_property_physics/common/physics_fps )` to a low value such as 10, which will make interpolation problems more obvious. -Move (almost) all game logic from _process to _physics_process -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Move (almost) all game logic from _process to _physics_process The most fundamental requirement for physics interpolation (which you may be doing already) is that you should be moving and performing game logic on your objects within `physics_process` (which runs at a physics tick) rather than `process` (which runs on a rendered frame). This means your scripts should typically be doing the bulk of their processing within `physics_process`, including responding to input and AI. @@ -32,16 +31,14 @@ Tip: This is only a *soft-rule*. There are some occasions where you might want to teleport objects outside of the physics tick (for instance when starting a level, or respawning objects). Still, in general, you should be applying transforms from the physics tick. -Ensure that all indirect movement happens during physics ticks -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Ensure that all indirect movement happens during physics ticks Consider that in Pandemonium, Nodes can be moved not just directly in your own scripts, but also by automatic methods such as tweening, animation, and navigation. All these methods should also have their timing set to operate on the physics tick rather than each frame ("idle"), **if** you are using them to move objects (*these methods can also be used to control properties that are not interpolated*). Note: Also consider that nodes can be moved not just by moving themselves, but also by moving parent nodes in the `SceneTree( SceneTree )`. The movement of parents should therefore also only occur during physics ticks. -Choose a physics tick rate -^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Choose a physics tick rate When using physics interpolation, the rendering is decoupled from physics, and you can choose any value that makes sense for your game. You are no longer limited to values that are multiples of the user's monitor refresh rate (for stutter-free gameplay if the target FPS is reached). @@ -58,8 +55,7 @@ As a rough guide: Note: You can always change the tick rate as you develop, it is as simple as changing the project setting. -Call reset_physics_interpolation() when teleporting objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Call reset_physics_interpolation() when teleporting objects Most of the time, interpolation is what you want between two physics ticks. However, there is one situation in which it may *not* be what you want. That is when you are initially placing objects, or moving them to a new location. Here, you don't want a smooth motion between the two - you want an instantaneous move. @@ -69,8 +65,7 @@ Even if you forget to call this, it is not usually a problem in most situations .. important:: You should call `reset_physics_interpolation()` *after* setting the new position, rather than before. Otherwise, you will still see the unwanted streaking motion. -Testing and debugging tips --------------------------- +## Testing and debugging tips Even if you intend to run physics at 60 TPS, in order to thoroughly test your interpolation and get the smoothest gameplay, it is highly recommended to temporarily set the physics tick rate to a low value such as 10 TPS. diff --git a/03_usage/10_physics/interpolation/04_advanced_physics_interpolation.md b/03_usage/10_physics/interpolation/04_advanced_physics_interpolation.md index 4b75691..eaa46d7 100644 --- a/03_usage/10_physics/interpolation/04_advanced_physics_interpolation.md +++ b/03_usage/10_physics/interpolation/04_advanced_physics_interpolation.md @@ -5,8 +5,7 @@ Advanced physics interpolation Although the previous instructions will give satisfactory results in a lot of games, in some cases you will want to go a stage further to get the best possible results and the smoothest possible experience. -Exceptions to automatic physics interpolation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Exceptions to automatic physics interpolation Even with physics interpolation active, there may be some local situations where you would benefit from disabling automatic interpolation for a `Node( Node )` (or branch of the `SceneTree( SceneTree )`), and have the finer control of performing interpolation manually. @@ -14,15 +13,13 @@ This is possible using the `Node.physics_interpolation_mode( Node_property_physi The most common situation where you may want to perform your own interpolation is Cameras. -Cameras -^^^^^^^ +#### Cameras In many cases, a `Camera( Camera )` can use automatic interpolation just like any other node. However, for best results, especially at low physics tick rates, it is recommended that you take a manual approach to Camera interpolation. This is because viewers are very sensitive to Camera movement. For instance, a Camera that realigns slightly every 1/10th of a second (at 10tps tick rate) will often be noticeable. You can get a much smoother result by moving the Camera each frame in `process`, and following an interpolated target manually. -Manual Camera interpolation -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Manual Camera interpolation **Ensure the Camera is using global coordinate space** @@ -36,15 +33,13 @@ There are two ways of doing this: 2) Call `Spatial.set_as_toplevel( Spatial_method_set_as_toplevel )` and set this to `true`, which will make the Camera ignore the transform of its parent. -Typical example -^^^^^^^^^^^^^^^ +#### Typical example A typical example of a custom approach is to use the `look_at` function in the Camera every frame in `process()` to look at a target node (such as the player). But there is a problem. If we use the traditional `get_global_transform()` on a Camera "target" Node, this transform will only focus the Camera on the target *at the current physics tick*. This is *not* what we want, as the Camera will jump about on each physics tick as the target moves. Even though the Camera may be updated each frame, this does not help give smooth motion if the *target* is only changing each physics tick. -get_global_transform_interpolated() -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### get_global_transform_interpolated() What we really want to focus the Camera on, is not the position of the target on the physics tick, but the *interpolated* position, i.e. the position at which the target will be rendered. @@ -55,8 +50,7 @@ We can do this using the `Spatial.get_global_transform_interpolated( Spatial_met Note: Aside from exceptions like the Camera, in most cases, your game logic should be in `physics_process()`. In game logic you should be calling `get_global_transform()` or `get_transform()`, which will give the current physics transform (in global or local space respectively), which is usually what you will want for gameplay code. -Example manual Camera script -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Example manual Camera script Here is an example of a simple fixed Camera which follows an interpolated target: @@ -89,8 +83,7 @@ Here is an example of a simple fixed Camera which follows an interpolated target look_at(_target_pos, Vector3(0, 1, 0)) ``` -Mouse look -^^^^^^^^^^ +#### Mouse look Mouse look is a very common way of controlling Cameras. But there is a problem. Unlike keyboard input which can be sampled periodically on the physics tick, mouse move events can come in continuously. The Camera will be expected to react and follow these mouse movements on the next frame, rather than waiting until the next physics tick. @@ -103,14 +96,12 @@ Sometimes, especially with Cameras, you will want to use a combination of interp There are many permutations and variations of Camera types, but it should be clear that in many cases, disabling automatic physics interpolation and handling this yourself can give a better result. -Disabling interpolation on other nodes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +#### Disabling interpolation on other nodes Although Cameras are the most common example, there are a number of cases when you may wish other nodes to control their own interpolation, or be non-interpolated. Consider for example, a player in a top view game whose rotation is controlled by mouse look. Disabling physics rotation allows the player rotation to match the mouse in real-time. -MultiMeshes -^^^^^^^^^^^ +#### MultiMeshes Although most visual Nodes follow the single Node single visual instance paradigm, MultiMeshes can control several instances from the same Node. Therefore, they have some extra functions for controlling interpolation functionality on a *per-instance* basis. You should explore these functions if you are using interpolated MultiMeshes. diff --git a/03_usage/11_navigation/01_navigation_introduction_2d.md b/03_usage/11_navigation/01_navigation_introduction_2d.md index 8e215e5..73b36be 100644 --- a/03_usage/11_navigation/01_navigation_introduction_2d.md +++ b/03_usage/11_navigation/01_navigation_introduction_2d.md @@ -1,19 +1,19 @@ -.. _doc_navigation_overview_2d: -2D Navigation Overview -====================== +# 2D Navigation Overview Pandemonium provides multiple objects, classes and servers to facilitate grid-based or mesh-based navigation and pathfinding for 2D and 3D games. The following section provides a quick overview over all available navigation related objects in Pandemonium for 2D scenes and their primary use. Pandemonium provides the following objects and classes for 2D navigation: -- :ref:`Astar2D` - ``Astar2D`` objects provide an option to find the shortest path in a graph of weighted **points**. +### `Astar2D` + + `Astar2D` objects provide an option to find the shortest path in a graph of weighted **points**. The AStar2D class is best suited for cellbased 2D gameplay that does not require actors to reach any possible position within an area but only predefined, distinct positions. -- :ref:`NavigationServer2D` +### `NavigationServer2D` + ``NavigationServer2D`` provides a powerful server API to find the shortest path between two positions on a area defined by a navigation mesh. The NavigationServer is best suited for 2D realtime gameplay that does require actors to reach any possible position within an navmesh defined area. @@ -38,14 +38,16 @@ Pandemonium provides the following objects and classes for 2D navigation: The following SceneTree Nodes are available as helpers to work with the NavigationServer2D API. -- :ref:`NavigationRegion2D` Node +### `NavigationRegion2D` Node + A Node that holds a NavigationPolygon resource that defines a navigation mesh for the NavigationServer2D. - The region can be enabled / disabled. - The use in pathfinding can be further restricted through the navigationlayers bitmask. - Regions can join their navigation meshes by proximity for a combined navigation mesh. -- :ref:`NavigationLink2D` Node +### `NavigationLink2D` Node + A Node that connects two positions on navigation mesh over arbitrary distances for pathfinding. - The link can be enabled / disabled. @@ -54,11 +56,12 @@ The following SceneTree Nodes are available as helpers to work with the Navigati Links tell the pathfinding that a connection exists and at what cost. The actual agent handling and movement needs to happen in custom scripts. -- :ref:`NavigationAgent2D` Node +### `NavigationAgent2D` Node + An optional helper Node to facilitate common NavigationServer2D API calls for pathfinding and avoidance for a Node2D inheriting parent Node. -- :ref:`NavigationObstacle2D` Node +### `NavigationObstacle2D` Node A Node that acts as an agent with avoidance radius, to work it needs to be added under a Node2D inheriting parent Node. Obstacles are intended as a last resort option for constantly moving objects that cannot be re(baked) to a navigation mesh efficiently. This node also only works if RVO processing @@ -66,55 +69,56 @@ The following SceneTree Nodes are available as helpers to work with the Navigati The 2D navigation meshes are defined with the following resources: -- :ref:`NavigationPolygon` Resource +### `NavigationPolygon` Resource + A resource that holds 2D navigation mesh data and provides polygon drawtools to define navigation areas inside the Editor as well as at runtime. - The NavigationRegion2D Node uses this resource to define its navigation area. - The NavigationServer2D uses this resource to update navmesh of individual regions. - The TileSet Editor creates and uses this resource internally when defining tile navigation areas. -.. seealso:: +### See also: You can see how 2D navigation works in action using the - `2D Navigation Polygon (https://github.com/Relintai/pandemonium_engine-demo-projects/tree/master/2d/navigation>`__ - and `Grid-based Navigation with AStarGrid2D (https://github.com/Relintai/pandemonium_engine-demo-projects/tree/master/2d/navigation_astar>`__ + `2D Navigation Polygon (https://github.com/Relintai/pandemonium_engine-demo-projects/tree/master/2d/navigation>` + and `Grid-based Navigation with AStarGrid2D (https://github.com/Relintai/pandemonium_engine-demo-projects/tree/master/2d/navigation_astar> demo projects. -Setup for 2D scene ------------------- +## Setup for 2D scene The following steps show the basic setup for a minimum viable navigation in 2D that uses the NavigationServer2D and a NavigationAgent2D for path movement. -#. Add a NavigationRegion2D Node to the scene. +1. Add a NavigationRegion2D Node to the scene. -#. Click on the region node and add a new NavigationPolygon Resource to the region node. +2. Click on the region node and add a new NavigationPolygon Resource to the region node. - .. image:: img/nav_2d_min_setup_step1.png +![](img/nav_2d_min_setup_step1.png) -#. Define the moveable navigation area with the NavigationPolygon draw tool. +3. Define the moveable navigation area with the NavigationPolygon draw tool. - .. image:: img/nav_2d_min_setup_step2.png +![](img/nav_2d_min_setup_step2.png) - .. note:: + Note: The navigation mesh defines the area where an actor can stand and move with its center. Leave enough margin between the navpolygon edges and collision objects to not get path following actors repeatedly stuck on collision. -#. Add a CharacterBody2D node in the scene with a basic collision shape and a sprite or mesh +4. Add a CharacterBody2D node in the scene with a basic collision shape and a sprite or mesh for visuals. -#. Add a NavigationAgent2D node below the character node. +5. Add a NavigationAgent2D node below the character node. - .. image:: img/nav_2d_min_setup_step3.webp +![](img/nav_2d_min_setup_step3.webp) -#. Add the following script to the CharacterBody2D node. We make sure to set a movement target +6. Add the following script to the CharacterBody2D node. We make sure to set a movement target after the scene has fully loaded and the NavigationServer had time to sync. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript + +``` extends CharacterBody2D var movement_speed: float = 200.0 @@ -154,70 +158,9 @@ NavigationServer2D and a NavigationAgent2D for path movement. velocity = new_velocity move_and_slide() +``` - .. code-tab:: csharp C# - - using Pandemonium; - - public partial class MyCharacterBody2D : CharacterBody2D - { - private NavigationAgent2D _navigationAgent; - - private float _movementSpeed = 200.0f; - private Vector2 _movementTargetPosition = new Vector2(70.0f, 226.0f); - - public Vector2 MovementTarget - { - get { return _navigationAgent.TargetPosition; } - set { _navigationAgent.TargetPosition = value; } - } - - public override void _Ready() - { - base._Ready(); - - _navigationAgent = GetNode("NavigationAgent2D"); - - // These values need to be adjusted for the actor's speed - // and the navigation layout. - _navigationAgent.PathDesiredDistance = 4.0f; - _navigationAgent.TargetDesiredDistance = 4.0f; - - // Make sure to not await during _Ready. - Callable.From(ActorSetup).CallDeferred(); - } - - public override void _PhysicsProcess(double delta) - { - base._PhysicsProcess(delta); - - if (_navigationAgent.IsNavigationFinished()) - { - return; - } - - Vector2 currentAgentPosition = GlobalTransform.Origin; - Vector2 nextPathPosition = _navigationAgent.GetNextPathPosition(); - - Vector2 newVelocity = (nextPathPosition - currentAgentPosition).Normalized(); - newVelocity *= _movementSpeed; - - Velocity = newVelocity; - - MoveAndSlide(); - } - - private async void ActorSetup() - { - // Wait for the first physics frame so the NavigationServer can sync. - await ToSignal(GetTree(), SceneTree.SignalName.PhysicsFrame); - - // Now that the navigation map is no longer empty, set the movement target. - MovementTarget = _movementTargetPosition; - } - } - -.. note:: +Note: On the first frame the NavigationServer map has not synchronized region data and any path query will return empty. Await one frame to pause scripts until the NavigationServer had time to sync. diff --git a/03_usage/11_navigation/02_navigation_introduction_3d.md b/03_usage/11_navigation/02_navigation_introduction_3d.md index a1505f4..ab312fe 100644 --- a/03_usage/11_navigation/02_navigation_introduction_3d.md +++ b/03_usage/11_navigation/02_navigation_introduction_3d.md @@ -1,21 +1,21 @@ -.. _doc_navigation_overview_3d: -3D Navigation Overview -====================== +# 3D Navigation Overview Pandemonium provides multiple objects, classes and servers to facilitate grid-based or mesh-based navigation and pathfinding for 2D and 3D games. The following section provides a quick overview over all available navigation related objects in Pandemonium for 3D scenes and their primary use. Pandemonium provides the following objects and classes for 3D navigation: -- :ref:`Astar3D` - ``Astar3D`` objects provide an option to find the shortest path in a graph of weighted **points**. +### `Astar3D` + + `Astar3D` objects provide an option to find the shortest path in a graph of weighted **points**. The AStar3D class is best suited for cellbased 3D gameplay that does not require actors to reach any possible position within an area but only predefined, distinct positions. -- :ref:`NavigationServer3D` - ``NavigationServer3D`` provides a powerful server API to find the shortest path between two positions +### `NavigationServer3D` + + `NavigationServer3D` provides a powerful server API to find the shortest path between two positions on a area defined by a navigation mesh. The NavigationServer is best suited for 3D realtime gameplay that does require actors to reach any @@ -41,14 +41,16 @@ Pandemonium provides the following objects and classes for 3D navigation: The following SceneTree Nodes are available as helpers to work with the NavigationServer3D API. -- :ref:`NavigationRegion3D` Node +### `NavigationRegion3D` Node + A Node that holds a Navigation Mesh resource that defines a navigation mesh for the NavigationServer3D. - The region can be enabled / disabled. - The use in pathfinding can be further restricted through the navigationlayers bitmask. - Regions can join their navigation meshes by proximity for a combined navigation mesh. -- :ref:`NavigationLink3D` Node +### `NavigationLink3D` Node + A Node that connects two positions on navigation mesh over arbitrary distances for pathfinding. - The link can be enabled / disabled. @@ -57,11 +59,13 @@ The following SceneTree Nodes are available as helpers to work with the Navigati Links tell the pathfinding that a connection exists and at what cost. The actual agent handling and movement needs to happen in custom scripts. -- :ref:`NavigationAgent3D` Node +### `NavigationAgent3D` Node + An optional helper Node to facilitate common NavigationServer3D API calls for pathfinding and avoidance for a Node3D inheriting parent Node. -- :ref:`NavigationObstacle3D` Node +### `NavigationObstacle3D` Node + A Node that acts as an agent with avoidance radius, to work it needs to be added under a Node3D inheriting parent Node. Obstacles are intended as a last resort option for constantly moving objects that cannot be re(baked) to a navigation mesh efficiently. This node also only works if RVO processing @@ -69,7 +73,8 @@ The following SceneTree Nodes are available as helpers to work with the Navigati The 3D navigation meshes are defined with the following resources: -- :ref:`NavigationMesh` Resource +### `NavigationMesh` Resource + A resource that holds 3D navigation mesh data and provides 3D geometry baking options to define navigation areas inside the Editor as well as at runtime. @@ -77,49 +82,48 @@ The 3D navigation meshes are defined with the following resources: - The NavigationServer3D uses this resource to update navmesh of individual regions. - The GridMap Editor uses this resource when specific navigation meshes are defined for each gridcell. -.. seealso:: +### See also You can see how 3D navigation works in action using the `3D Navigation demo project `__. -Setup for 3D scene ------------------- +## Setup for 3D scene The following steps show how to setup a minimum viable navigation in 3D that uses the NavigationServer3D and a NavigationAgent3D for path movement. -#. Add a NavigationRegion3D Node to the scene. +1. Add a NavigationRegion3D Node to the scene. -#. Click on the region node and add a new :ref:`NavigationMesh` Resource to +2. Click on the region node and add a new `NavigationMesh` Resource to the region node. - .. image:: img/nav_3d_min_setup_step1.png +![](img/nav_3d_min_setup_step1.png) -#. Add a new MeshInstance3D node as a child of the region node. +3. Add a new MeshInstance3D node as a child of the region node. -#. Select the MeshInstance3D node and add a new PlaneMesh and increase the xy size to 10. +4. Select the MeshInstance3D node and add a new PlaneMesh and increase the xy size to 10. -#. Select the region node again and press the "Bake Navmesh" button on the top bar. +5. Select the region node again and press the "Bake Navmesh" button on the top bar. - .. image:: img/nav_3d_min_setup_step2.png +![](img/nav_3d_min_setup_step2.png) -#. Now a transparent navigation mesh appeared that hovers some distance on top the planemesh. +6. Now a transparent navigation mesh appeared that hovers some distance on top the planemesh. - .. image:: img/nav_3d_min_setup_step3.png +![](img/nav_3d_min_setup_step3.png) -#. Add a CharacterBody3D node in the scene with a basic collision shape and some mesh for visuals. +7. Add a CharacterBody3D node in the scene with a basic collision shape and some mesh for visuals. -#. Add a NavigationAgent3D node below the character node. +8. Add a NavigationAgent3D node below the character node. - .. image:: img/nav_3d_min_setup_step4.webp +![](img/nav_3d_min_setup_step4.webp) -#. Add a script to the CharacterBody3D node with the following content. We make sure to set a +9. Add a script to the CharacterBody3D node with the following content. We make sure to set a movement target after the scene has fully loaded and the NavigationServer had time to sync. Also, add a Camera3D and some light and environment to see something. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends CharacterBody3D var movement_speed: float = 2.0 @@ -159,70 +163,9 @@ a NavigationAgent3D for path movement. velocity = new_velocity move_and_slide() +``` - .. code-tab:: csharp C# - - using Pandemonium; - - public partial class MyCharacterBody3D : CharacterBody3D - { - private NavigationAgent3D _navigationAgent; - - private float _movementSpeed = 2.0f; - private Vector3 _movementTargetPosition = new Vector3(-3.0f, 0.0f, 2.0f); - - public Vector3 MovementTarget - { - get { return _navigationAgent.TargetPosition; } - set { _navigationAgent.TargetPosition = value; } - } - - public override void _Ready() - { - base._Ready(); - - _navigationAgent = GetNode("NavigationAgent3D"); - - // These values need to be adjusted for the actor's speed - // and the navigation layout. - _navigationAgent.PathDesiredDistance = 0.5f; - _navigationAgent.TargetDesiredDistance = 0.5f; - - // Make sure to not await during _Ready. - Callable.From(ActorSetup).CallDeferred(); - } - - public override void _PhysicsProcess(double delta) - { - base._PhysicsProcess(delta); - - if (_navigationAgent.IsNavigationFinished()) - { - return; - } - - Vector3 currentAgentPosition = GlobalTransform.Origin; - Vector3 nextPathPosition = _navigationAgent.GetNextPathPosition(); - - Vector3 newVelocity = (nextPathPosition - currentAgentPosition).Normalized(); - newVelocity *= _movementSpeed; - - Velocity = newVelocity; - - MoveAndSlide(); - } - - private async void ActorSetup() - { - // Wait for the first physics frame so the NavigationServer can sync. - await ToSignal(GetTree(), SceneTree.SignalName.PhysicsFrame); - - // Now that the navigation map is no longer empty, set the movement target. - MovementTarget = _movementTargetPosition; - } - } - -.. note:: +Note: On the first frame the NavigationServer map has not synchronized region data and any path query will return empty. Await one frame to pause scripts until the NavigationServer had time to sync. diff --git a/03_usage/11_navigation/03_navigation_using_navigationservers.md b/03_usage/11_navigation/03_navigation_using_navigationservers.md index 829dff8..fdba5fc 100644 --- a/03_usage/11_navigation/03_navigation_using_navigationservers.md +++ b/03_usage/11_navigation/03_navigation_using_navigationservers.md @@ -1,43 +1,40 @@ -.. _doc_navigation_using_navigationservers: -Using NavigationServer -====================== +# Using NavigationServer 2D and 3D version of the NavigationServer are available as -:ref:`NavigationServer2D` and -:ref:`NavigationServer3D` respectively. +`NavigationServer2D` and +`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. Hence it is entirely possible (if not a little cumbersome) to exclusively use the NavigationServer3D API for 2D navigation. -Communicating with the NavigationServer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Communicating with the NavigationServer -To work with the NavigationServer means to prepare parameters for a ``query`` that can be send to the NavigationServer for updates or requesting data. +To work with the NavigationServer means to prepare parameters for a `query` that can be send to the NavigationServer for updates or requesting data. To reference the internal NavigationServer objects like maps, regions and agents RIDs are used as identification numbers. Every navigation related node in the SceneTree has a function that returns the RID for this node. -Threading and Synchronization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Threading and Synchronization The NavigationServer does not update every change immediately but waits until -the end of the ``physics_frame`` to synchronize all the changes together. +the end of the `physics_frame` to synchronize all the changes together. Waiting for synchronization is required to apply changes to all maps, regions and agents. Synchronization 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. +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. +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 synchronization. +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 synchronization. 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 synchronization phase. +The NavigationServer is `thread-safe` as it places all API calls that want to make changes in a queue to be executed in the synchronization phase. Synchronization for the NavigationServer happens in the middle of the physics frame after scene input from scripts and nodes are all done. -.. note:: +Note: + The important takeaway is that most NavigationServer changes take effect after the next physics frame and not immediately. This includes all changes made by navigation related nodes in the SceneTree or through scripts. @@ -66,8 +63,7 @@ The following functions will be executed in the synchronization phase only: - agent_set_callback() - free() -2D and 3D NavigationServer differences -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### 2D and 3D NavigationServer differences NavigationServer2D and NavigationServer3D are equivalent in functionality for their dimension and both use the same NavigationServer behind the scene. @@ -87,33 +83,33 @@ polygon outline drawtools of NavigationRegion2D and NavigationPolygons. 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:: +Note: + Regions created in 2D and 3D will merge their navigationmeshes when placed on the same map and merge conditions apply. The NavigationServer does not discriminate between NavigationRegion2D and NavigationRegion3D nodes as both are regions on the server. By default those nodes register on different navigation maps so this merge can only happen when maps are changed manually e.g. with scripts. Actors with avoidance enabled will avoid both 2D and 3D avoidance agents when placed on the same map. -.. warning:: +Warning: + It is not possible to use NavigationServer2D while disabling 3D on a Pandemonium custom build. -Waiting for synchronization -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Waiting for synchronization At the start of the game, a new scene or procedural navigation changes any path query to a NavigationServer will return empty or wrong. The navigation map is still empty or not updated at this point. All nodes from the SceneTree need to first upload their navigation related data to the NavigationServer. Each added or changed map, region or agent need to be registered with the NavigationServer. -Afterward the NavigationServer requires a ``physics_frame`` for synchronization to update the maps, regions and agents. +Afterward the NavigationServer requires a `physics_frame` for synchronization to update the maps, regions and agents. One workaround is to make a deferred call to a custom setup function (so all nodes are ready). The setup function makes all the navigation changes, e.g. adding procedural stuff. Afterwards the function waits for the next physics_frame before continuing with path queries. -.. tabs:: - .. code-tab:: gdscript GDScript - +GDScript +``` extends Node3D func _ready(): @@ -162,14 +158,14 @@ Afterwards the function waits for the next physics_frame before continuing with print("Found a path!") print(path) +``` -Server Avoidance Callbacks -~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Server Avoidance Callbacks If RVO avoidance agents are registered for avoidance callbacks the NavigationServer dispatches -their ``safe_velocity`` signals just before the PhysicsServer synchronization. +their `safe_velocity` signals just before the PhysicsServer synchronization. -To learn more about NavigationAgents see :ref:`doc_navigation_using_navigationagents`. +To learn more about NavigationAgents see `doc_navigation_using_navigationagents`. The simplified order of execution for NavigationAgents that use avoidance: diff --git a/03_usage/11_navigation/04_navigation_using_navigationmaps.md b/03_usage/11_navigation/04_navigation_using_navigationmaps.md index 3857908..377a381 100644 --- a/03_usage/11_navigation/04_navigation_using_navigationmaps.md +++ b/03_usage/11_navigation/04_navigation_using_navigationmaps.md @@ -1,77 +1,69 @@ -.. _doc_navigation_using_navigationmaps: -Using NavigationMaps -==================== +# Using NavigationMaps -.. image:: img/nav_maps.png +![](img/nav_maps.png) -A NavigationMap is an abstract navigation world on the NavigationServer identified by a NavigationServer :ref:`RID`. +A NavigationMap is an abstract navigation world on the NavigationServer identified by a NavigationServer `RID`. A map can hold and connect a near infinite number of navigation regions with navigation meshes to build the traversable areas of a game world for pathfinding. A map can be joined by avoidance agents to process collision avoidance between the avoidance agents. -.. note:: +Note: 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 -~~~~~~~~~~~~~~~~~~~~~~~ +### Default navigation maps -By default Pandemonium creates a navigation map RID for each :ref:`World2D` and :ref:`World3D` of the root viewport. +By default Pandemonium creates a navigation map RID for each `World2D` and `World3D` of the root viewport. -The 2D default navigation ``map`` can be obtained with ``get_world_2d().get_navigation_map()`` from any :ref:`Node2D` inheriting Node. +The 2D default navigation `map` can be obtained with ``get_world_2d().get_navigation_map()`` from any `Node2D` inheriting Node. -The 3D default navigation ``map`` can be obtained with ``get_world_3d().get_navigation_map()`` from any :ref:`Node3D` inheriting Node. +The 3D default navigation `map` can be obtained with ``get_world_3d().get_navigation_map()`` from any `Node3D` inheriting Node. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends Node2D 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() - -Creating new navigation maps -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### 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 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`. +For example uses of different navigation maps see `doc_navigation_different_actor_types` and `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. Navigation regions and avoidance agents can only be part of a single navigations map but they can switch maps at any time. -.. note:: +Note: A navigation map switch will take effect only after the next NavigationServer synchronization. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends Node2D var new_navigation_map: RID = NavigationServer2D.map_create() NavigationServer2D.map_set_active(true) +``` -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends Node3D var new_navigation_map: RID = NavigationServer3D.map_create() NavigationServer3D.map_set_active(true) +``` -.. note:: +Note: There is no difference between navigation maps created with the NavigationServer2D API or the NavigationServer3D API. diff --git a/03_usage/11_navigation/05_navigation_using_navigationregions.md b/03_usage/11_navigation/05_navigation_using_navigationregions.md index 2e9bc75..a6821e4 100644 --- a/03_usage/11_navigation/05_navigation_using_navigationregions.md +++ b/03_usage/11_navigation/05_navigation_using_navigationregions.md @@ -1,25 +1,23 @@ -.. _doc_navigation_using_navigationregions: -Using NavigationRegions -======================= +# Using NavigationRegions -NavigationRegions are the visual Node representation of a ``region`` of the navigation ``map`` on the NavigationServer. +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` -and :ref:`NavigationRegion3D` respectively. +Both 2D and 3D version are available as `NavigationRegion2D` +and `NavigationRegion3D` respectively. Individual NavigationRegions upload their 2D NavigationPolygon or 3D NavigationMesh resource data to the NavigationServer. The NavigationServer map turns this information into a combined navigation map for pathfinding. -To create a navigation region using the SceneTree add a ``NavigationRegion2D`` or ``NavigationRegion3D`` node to the scene. -All regions require a navigationmesh resource to function. See :ref:`doc_navigation_using_navigationmeshes` to learn how to create and apply navigationmeshes. +To create a navigation region using the SceneTree add a `NavigationRegion2D` or `NavigationRegion3D` node to the scene. +All regions require a navigationmesh resource to function. See `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`. -To connect NavigationRegions over arbitrary distances see :ref:`doc_navigation_using_navigationlinks` to learn how to create and use ``NavigationLinks``. +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 `doc_navigation_connecting_navmesh`. +To connect NavigationRegions over arbitrary distances see `doc_navigation_using_navigationlinks` to learn how to create and use `NavigationLinks`. -.. warning:: +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 @@ -27,47 +25,49 @@ To connect NavigationRegions over arbitrary distances see :ref:`doc_navigation_u Regions can be enabled / disabled and if disabled will not contribute to future pathfinding queries. -.. note:: +Note: Existing paths will not be automatically updated when a region gets enabled / disabled. -Creating new navigation regions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Creating new navigation regions New NavigationRegion nodes will automatically register to the default world navigation map for their 2D/3D dimension. -The region RID can then be obtained from NavigationRegion Nodes with ``get_region_rid()``. +The region RID can then be obtained from NavigationRegion Nodes with `get_region_rid()`. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends NavigationRegion3D var navigationserver_region_rid: RID = get_region_rid() +``` New regions can also be created with the NavigationServer API and added to any existing map. If regions are created with the NavigationServer API directly they need to be assigned a navigation map manually. -.. tabs:: - .. code-tab:: gdscript 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() NavigationServer2D.region_set_map(new_2d_region_rid, default_2d_map_rid) +``` -.. tabs:: - .. code-tab:: gdscript 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() NavigationServer3D.region_set_map(new_3d_region_rid, default_3d_map_rid) +``` -.. note:: +Note: NavigationRegions can only be assigned to a single NavigationMap. If an existing region is assigned to a new map it will leave the old map. diff --git a/03_usage/11_navigation/06_navigation_using_navigationmeshes.md b/03_usage/11_navigation/06_navigation_using_navigationmeshes.md index cc221e2..e423580 100644 --- a/03_usage/11_navigation/06_navigation_using_navigationmeshes.md +++ b/03_usage/11_navigation/06_navigation_using_navigationmeshes.md @@ -1,13 +1,10 @@ -.. _doc_navigation_using_navigationmeshes: -Using NavigationMeshes -====================== +# Using NavigationMeshes -2D and 3D version of the navigation mesh are available as -:ref:`NavigationPolygon` and -:ref:`NavigationMesh` respectively. +2D and 3D version of the navigation mesh are available as `NavigationPolygon` and +`NavigationMesh` respectively. -.. note:: +Note: A navigation mesh describes the traversable safe area for an agent with its center position at zero radius. If you want pathfinding to account for an agent's (collision) size you need to shrink the navigation mesh accordingly. @@ -16,20 +13,17 @@ Navigation works independent from other engine parts like rendering and physics. If you experience clipping or collision problems while following navigation paths always remember that you need to tell the navigation system through an appropriated navigation mesh what your intentions are. By itself the navigation system will never know "this is a tree / rock / wall collision shape or visual mesh" because it only knows "here I was told I can path safely cause it is on navigation mesh". -.. _doc_navigation_navmesh_baking: - -Creating 2D NavigationMeshes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Creating 2D NavigationMeshes Navigation meshes in the 2D editor are created with the help of the NavigationPolygon draw tools that appear in the top bar of the editor when a NavigationRegion2D is selected. -.. image:: img/nav_polydrawtool.png +![](img/nav_polydrawtool.png) -The NavigationPolygon draw tools can be used to create and edit navigation meshes by defining ``outline`` polygons. +The NavigationPolygon draw tools can be used to create and edit navigation meshes by defining `outline` polygons. The outline polygons are later converted to real NavigationMesh resources for the NavigationServer regions. -.. image:: img/nav_polymatroschka.png +![](img/nav_polymatroschka.png) Multiple outlines can be added to the same NavPolygon resource as long as they **do not intersect or overlap**. Each additional outline will cut a hole in the polygon created by the larger outline. @@ -38,13 +32,13 @@ If the larger polygon is already a hole it will create a new navigation mesh pol Outlines are not a replacement if the intention is to merge aligned polygons e.g. from grid cells. Outlines, as the name would suggest, cannot intersect each other or have any overlapping vertex positions. -.. image:: img/nav_polyoutlinefail.png +![](img/nav_polyoutlinefail.png) Outline layouts like seen in this picture will fail the convex partitioning required by the navigation mesh generation. -In this layout cases the outline tool cannot be used. Use the :ref:`Geometry2D` class for +In this layout cases the outline tool cannot be used. Use the `Geometry2D` class for polygon merge or intersect operations to create a valid merged mesh for navigation. -.. note:: +Note: The NavigationServer does not connect navigation mesh islands from the same NavigationMesh resource. Do not create multiple disconnected islands in the same NavigationRegion2D and NavPoly resource if they should be later connected. @@ -53,13 +47,12 @@ For 2D no similar navigation mesh baking with geometry parsing exists like in 3D The Geometry2D class functions for offset, merge, intersect and clip can be used to shrink or enlarge existing NavigationPolygons to different actor sizes. -Creating 3D NavigationMeshes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Creating 3D NavigationMeshes -.. image:: img/baked_navmesh.png +![](img/baked_navmesh.png) Navigation meshes in the 3D editor are created with the help of the -:ref:`NavigationMeshGenerator` singleton +`NavigationMeshGenerator` singleton and the NavigationMesh bake settings that appear in the editor inspector. NavigationMesh baking is the process of creating a simplified mesh used for pathfinding out of (complex) 3D level geometry. @@ -72,20 +65,19 @@ to perfectly follow the original surfaces. Especially navigation polygons placed over ramps will not keep an equal distance to the ground surface. To align an actor perfectly with the ground use other means like physics. -.. warning:: +Warning: Meshes need to be triangulated to work as navigation meshes. Other mesh face formats like quad or ngon are not supported. -NavigationMesh rebaking at runtime -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### NavigationMesh rebaking at runtime -To rebake a ``NavigationMesh`` at runtime, use the NavigationRegion3D.bake_navigation_mesh() function. +To rebake a `NavigationMesh` at runtime, use the NavigationRegion3D.bake_navigation_mesh() function. Another option is to use the NavigationMeshGenerator.bake() Singleton function with the NavigationMesh resource directly. If the navigation mesh resource is already prepared, the region can be updated with the NavigationServer3D API directly as well. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends NavigationRegion3D func update_navigation_mesh(): @@ -104,14 +96,15 @@ If the navigation mesh resource is already prepared, the region can be updated w # or use NavigationServer API to update region with prepared navigation mesh var region_rid: RID = get_region_rid() NavigationServer3D.region_set_navigation_mesh(region_rid, navigation_mesh) +``` -.. note:: +Note: Baking a NavigationMesh at runtime is a costly operation. Complex navigation mesh take some time to bake and if done on the main thread can freeze a game. (Re)baking a large navigation mesh is preferably done in a separate thread. -.. warning:: +Warning: Property values on a NavigationMesh resource like ``cell_size`` need to match the actual mesh data stored inside in order to merge @@ -124,20 +117,19 @@ For 2D NavigationPolygon resources are used to draw outline points in the editor For 3D NavigationMesh resources are used. Instead of providing draw tools the 3D variant provides an extensive amount of parameters to bake a navigation mesh directly from 3D source geometry. -.. note:: +Note: Technically there is no hard distinction between 2D and 3D how to use the given toolsets to create flat navigation meshes. The 2D drawing tool can be used to create a flat 3D navmesh and the 3D baking tool can be used to parse flat 3D geometry into 2D appropriated navigationmeshes. -2D Navmesh from CollisionPolygons -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### 2D Navmesh from CollisionPolygons The following script parses all child nodes of a NavigationRegion2D for CollisionPolygons and bakes their shape into the NavigationPolygon. As the NavigationPolygon creates the navigationmesh from outline data the shapes cannot overlap. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends NavigationRegion2D var new_navigation_polygon: NavigationPolygon = get_navigation_polygon() @@ -164,15 +156,15 @@ navigationmesh from outline data the shapes cannot overlap. var new_collision_outline: PackedVector2Array = collisionpolygon_transform * collisionpolygon new_navigation_polygon.add_outline(new_collision_outline) +``` -Procedual 2D NavigationMesh -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Procedual 2D NavigationMesh The following script creates a new 2D navigation region and fills it with procedual generated navigation mesh data from a NavigationPolygon resource. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends Node2D var new_2d_region_rid: RID = NavigationServer2D.region_create() @@ -191,15 +183,15 @@ The following script creates a new 2D navigation region and fills it with proced new_navigation_polygon.make_polygons_from_outlines() NavigationServer2D.region_set_navigation_polygon(new_2d_region_rid, new_navigation_polygon) +``` -Procedual 3D NavigationMesh -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Procedual 3D NavigationMesh The following script creates a new 3D navigation region and fills it with procedual generated navigation mesh data from a NavigationMesh resource. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends Node3D var new_3d_region_rid: RID = NavigationServer3D.region_create() @@ -219,15 +211,15 @@ The following script creates a new 3D navigation region and fills it with proced PackedInt32Array([0, 1, 2]) ) NavigationServer3D.region_set_navigation_mesh(new_3d_region_rid, new_navigation_mesh) +``` -Navmesh for 3D GridMaps -~~~~~~~~~~~~~~~~~~~~~~~ +### Navmesh for 3D GridMaps The following script creates a new 3D navigation mesh for each GridMap items, clears the current grid cells and adds new procedual grid cells with the new navigation mesh. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends GridMap # enable navigation mesh for grid items @@ -266,3 +258,4 @@ The following script creates a new 3D navigation mesh for each GridMap items, cl _position.x = -i _position.z = -j gridmap.set_cell_item(_position, _item, _orientation) +``` diff --git a/03_usage/11_navigation/07_navigation_using_navigationpaths.md b/03_usage/11_navigation/07_navigation_using_navigationpaths.md index c141fe3..ea9b9b9 100644 --- a/03_usage/11_navigation/07_navigation_using_navigationpaths.md +++ b/03_usage/11_navigation/07_navigation_using_navigationpaths.md @@ -1,40 +1,36 @@ -.. _doc_navigation_using_navigationpaths: -Using NavigationPaths -===================== +# Using NavigationPaths -Obtaining a Navigationpath --------------------------- +## Obtaining a Navigationpath 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)``. +To obtain a 2D path, use `NavigationServer2D.map_get_path(map, from, to, optimize, navigation_layers)`. +To obtain a 3D path, use `NavigationServer3D.map_get_path(map, from, to, optimize, navigation_layers)`. -To obtain a 3D path, use ``NavigationServer3D.map_get_path(map, from, to, optimize, navigation_layers)``. - -For more customizable navigation path queries that require additional setup see :ref:`doc_navigation_using_navigationpathqueryobjects`. +For more customizable navigation path queries that require additional setup see `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 -any Node2D inheriting node or ``get_world_3d().get_navigation_map()`` from any Node3D inheriting node. +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 +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. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` 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() @@ -46,10 +42,11 @@ Outside of grids due to polygons often covering large open areas with a single, target_position, true ) +``` -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` 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() @@ -61,25 +58,26 @@ Outside of grids due to polygons often covering large open areas with a single, target_position, true ) +``` -A returned ``path`` by the NavigationServer will be a ``PackedVector2Array`` for 2D or a ``PackedVector3Array`` for 3D. -These are just a memory-optimized ``Array`` of vector positions. +A returned `path` by the NavigationServer will be a `PackedVector2Array` for 2D or a `PackedVector3Array` for 3D. +These are just a memory-optimized `Array` of vector positions. All position vectors inside the array are guaranteed to be inside a NavigationPolygon or NavigationMesh. -The path array, if not empty, has the navigationmesh position closest to the starting position at the first index ``path[0]`` position. -The closest available navigationmesh position to the target position is the last index ``path[path.size()-1]`` position. +The path array, if not empty, has the navigationmesh position closest to the starting position at the first index `path[0]` position. +The closest available navigationmesh position to the target position is the last index `path[path.size()-1]` position. All index between are the pathpoints that an actor should follow to reach the target without leaving the navigation mesh. -.. note:: +Note: 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 default navigation map by setting the target position with ``set_movement_target()``. +the default navigation map by setting the target position with `set_movement_target()`. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` var movement_speed: float = 4.0 var movement_delta: float var path_point_margin: float = 0.5 @@ -124,3 +122,4 @@ the default navigation map by setting the target position with ``set_movement_ta 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) +``` diff --git a/03_usage/11_navigation/08_navigation_using_navigationpathqueryobjects.md b/03_usage/11_navigation/08_navigation_using_navigationpathqueryobjects.md index eb3b0ad..b0b9cbf 100644 --- a/03_usage/11_navigation/08_navigation_using_navigationpathqueryobjects.md +++ b/03_usage/11_navigation/08_navigation_using_navigationpathqueryobjects.md @@ -1,26 +1,24 @@ -.. _doc_navigation_using_navigationpathqueryobjects: -Using NavigationPathQueryObjects -================================ +# 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 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 -for the query and a ``NavigationPathQueryResult`` that receives (regular) updates with the resulting path and meta data from the query. +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` and -:ref:`NavigationPathQueryParameters3D` respectively. +2D and 3D versions of `NavigationPathQueryParameters` are available as +`NavigationPathQueryParameters2D` and +`NavigationPathQueryParameters3D` respectively. -2D and 3D versions of ``NavigationPathQueryResult`` are available as -:ref:`NavigationPathQuerResult2D` and -:ref:`NavigationPathQueryResult3D` respectively. +2D and 3D versions of `NavigationPathQueryResult` are available as +`NavigationPathQuerResult2D` and +`NavigationPathQueryResult3D` respectively. -Both parameters and result are used as a pair with the ``NavigationServer.query_path()`` function. +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. @@ -29,9 +27,9 @@ persistent variable for the agent and reused for every followup path query with 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:: - .. code-tab:: gdscript GDScript +GDScript +``` # prepare query objects var query_parameters = NavigationPathQueryParameters2D.new() var query_result = NavigationPathQueryResult2D.new() @@ -44,10 +42,11 @@ 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() +``` -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` # prepare query objects var query_parameters = NavigationPathQueryParameters3D.new() var query_result = NavigationPathQueryResult3D.new() @@ -60,3 +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() +``` diff --git a/03_usage/11_navigation/09_navigation_using_navigationagents.md b/03_usage/11_navigation/09_navigation_using_navigationagents.md index 6044e0f..7e29f91 100644 --- a/03_usage/11_navigation/09_navigation_using_navigationagents.md +++ b/03_usage/11_navigation/09_navigation_using_navigationagents.md @@ -1,7 +1,5 @@ -.. _doc_navigation_using_navigationagents: -Using NavigationAgents -====================== +# Using NavigationAgents NavigationsAgents are helper nodes that combine functionality for pathfinding, path following and agent avoidance for a Node2D/3D inheriting parent node. @@ -9,67 +7,64 @@ They facilitate common calls to the NavigationServer API on behalf of the parent actor node in a more convenient manner for beginners. 2D and 3D version of NavigationAgents are available as -:ref:`NavigationAgent2D` and -:ref:`NavigationAgent3D` respectively. +`NavigationAgent2D` and +`NavigationAgent3D` respectively. New NavigationAgent nodes will automatically join the default navigation map on the World2D/World3D. NavigationsAgent nodes are optional and not a hard requirement to use the navigation system. Their entire functionality can be replaced with scripts and direct calls to the NavigationServer API. -NavigationAgent Pathfinding ---------------------------- +## NavigationAgent Pathfinding -NavigationAgents query a new navigation path on their current navigation map when their ``target_position`` is set with a global position. +NavigationAgents query a new navigation path on their current navigation map when their `target_position` is set with a global position. The result of the pathfinding can be influenced with the following properties. -- The ``navigation_layers`` bitmask can be used to limit the navigation meshes that the agent can use. -- The ``pathfinding_algorithm`` controls how the pathfinding travels through the navigation mesh polygons in the path search. -- The ``path_postprocessing`` sets if or how the raw path corridor found by the pathfinding is altered before it is returned. -- The ``path_metadata_flags`` enable the collection of additional path point meta data returned by the path. +- The `navigation_layers` bitmask can be used to limit the navigation meshes that the agent can use. +- The `pathfinding_algorithm` controls how the pathfinding travels through the navigation mesh polygons in the path search. +- The `path_postprocessing` sets if or how the raw path corridor found by the pathfinding is altered before it is returned. +- The `path_metadata_flags` enable the collection of additional path point meta data returned by the path. -.. warning:: +Warning: Disabling path meta flags will disable related signal emissions on the agent. -NavigationAgent Pathfollowing ------------------------------ +## NavigationAgent Pathfollowing -After a ``target_position`` has been set for the agent, the next position to follow in the path -can be retrieved with the ``get_next_path_position()`` function. +After a `target_position` has been set for the agent, the next position to follow in the path +can be retrieved with the `get_next_path_position()` function. Once the next path position is received move the parent actor node of the agent towards this path position with your own movement code. -.. note:: +Note: The navigation system never moves the parent node of a NavigationAgent. The movement is entirely in the hands of users and their custom scripts. NavigationAgents have their own internal logic to proceed with the current path and call for updates. -The ``get_next_path_position()`` function is responsible for updating many of the agent's internal states and properties. -The function should be repeatedly called `once` every ``physics_process`` until ``is_navigation_finished()`` tells that the path is finished. +The `get_next_path_position()` function is responsible for updating many of the agent's internal states and properties. +The function should be repeatedly called `once` every `physics_process` until `is_navigation_finished()` tells that the path is finished. The function should not be called after the target position or path end has been reached as it can make the agent jitter in place due to the repeated path updates. -Always check very early in script with ``is_navigation_finished()`` if the path is already finished. +Always check very early in script with `is_navigation_finished()` if the path is already finished. The following properties influence the path following behavior. -- The ``path_desired_distance`` defines the distance at which the agent advances its internal path index to the next path position. -- The ``target_desired_distance`` defines the distance at which the agent considers the target position to be reached and the path at its end. -- The ``path_max_distance`` defines when an agent requests a new path cause it was moved too far away from the current path point segment. +- The `path_desired_distance` defines the distance at which the agent advances its internal path index to the next path position. +- The `target_desired_distance` defines the distance at which the agent considers the target position to be reached and the path at its end. +- The `path_max_distance` defines when an agent requests a new path cause it was moved too far away from the current path point segment. -The important updates are all triggered with the ``get_next_path_position()`` function -when called in ``_physics_process()``. +The important updates are all triggered with the `get_next_path_position()` function +when called in `_physics_process()`. -NavigationAgents can be used with ``process`` but are still limited to a single update that happens in ``physics_process``. +NavigationAgents can be used with `process` but are still limited to a single update that happens in `physics_process`. Script examples for various nodes commonly used with NavigationAgents can be found further below. -Pathfollowing common problems -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Pathfollowing common problems There are some common user problems and important caveats to consider when writing agent movement scripts. @@ -85,59 +80,58 @@ There are some common user problems and important caveats to consider when writi - The agent is sometimes looking backwards for a frame Same as with stuck dancing agents between two positions, this is usually caused by very frequent path updates every single frame. Depending on your navigation mesh layout, and especially when an agent is directly placed over a navigation mesh edge or edge connection, expect path positions to be sometimes slightly "behind" your actors current orientation. This happens due to precision issues and can not always be avoided. This is usually only a visible problem if actors are instantly rotated to face the current path position. -NavigationAgent Avoidance -------------------------- +## NavigationAgent Avoidance This section explains how to use the navigation avoidance specific to NavigationAgents. -In order for NavigationAgents to use the avoidance feature the ``enable_avoidance`` property must be set to ``true``. +In order for NavigationAgents to use the avoidance feature the `enable_avoidance` property must be set to `true`. -.. image:: img/agent_avoidance_enabled.png +![](img/agent_avoidance_enabled.png) -The velocity_computed signal of the NavigationAgent node must be connected to receive the ``safe_velocity`` calculation result. +The velocity_computed signal of the NavigationAgent node must be connected to receive the `safe_velocity` calculation result. -.. image:: img/agent_safevelocity_signal.png +![](img/agent_safevelocity_signal.png) -Use ``set_velocity()`` on the NavigationAgent node in ``_physics_process()`` to update the agent with the current velocity of the agent's parent node. +Use `set_velocity()` on the NavigationAgent node in `_physics_process()` to update the agent with the current velocity of the agent's parent node. -While avoidance is enabled on the agent the ``safe_velocity`` vector will be received with the velocity_computed signal every physics frame. +While avoidance is enabled on the agent the `safe_velocity` vector will be received with the velocity_computed signal every physics frame. This velocity vector should be used to move the NavigationAgent's parent node in order to avoidance collision with other avoidance using agents or avoidance obstacles. -.. note:: +Note: Only other agents on the same map that are registered for avoidance themself will be considered in the avoidance calculation. The following NavigationAgent properties are relevant for avoidance: - - The property ``height`` is available in 3D only. The height together with the current global y-axis position of the agent determines the vertical placement of the agent in the avoidance simulation. Agents using the 2D avoidance will automatically ignore other agents or obstacles that are below or above them. - - The property ``radius`` controls the size of the avoidance circle, or in case of 3D sphere, around the agent. This area describes the agents body and not the avoidance maneuver distance. - - The property ``neighbor_distance`` controls the search radius of the agent when searching for other agents that should be avoided. A lower value reduces processing cost. - - The property ``max_neighbors`` controls how many other agents are considered in the avoidance calculation if they all have overlapping radius. + - The property `height` is available in 3D only. The height together with the current global y-axis position of the agent determines the vertical placement of the agent in the avoidance simulation. Agents using the 2D avoidance will automatically ignore other agents or obstacles that are below or above them. + - The property `radius` controls the size of the avoidance circle, or in case of 3D sphere, around the agent. This area describes the agents body and not the avoidance maneuver distance. + - The property `neighbor_distance` controls the search radius of the agent when searching for other agents that should be avoided. A lower value reduces processing cost. + - The property `max_neighbors` controls how many other agents are considered in the avoidance calculation if they all have overlapping radius. A lower value reduces processing cost but a too low value may result in agents ignoring the avoidance. - - The properties ``time_horizon_agents`` and ``time_horizon_obstacles`` control the avoidance prediction time for other agents or obstacles in seconds. When agents calculate their safe velocities they choose velocities that can be kept for this amount of seconds without colliding with another avoidance object. The prediction time should be kept as low as possible as agents will slow down their velocities to avoid collision in that timeframe. - - The property ``max_speed`` controls the maximum velocity allowed for the agents avoidance calculation. - If the agents parents moves faster than this value the avoidance ``safe_velocity`` might not be accurate enough to avoid collision. - - The property ``use_3d_avoidance`` switches the agent between the 2D avoidance (xz axis) and the 3D avoidance (xyz axis) on the next update. + - The properties `time_horizon_agents` and `time_horizon_obstacles` control the avoidance prediction time for other agents or obstacles in seconds. When agents calculate their safe velocities they choose velocities that can be kept for this amount of seconds without colliding with another avoidance object. The prediction time should be kept as low as possible as agents will slow down their velocities to avoid collision in that timeframe. + - The property `max_speed` controls the maximum velocity allowed for the agents avoidance calculation. + If the agents parents moves faster than this value the avoidance `safe_velocity` might not be accurate enough to avoid collision. + - The property `use_3d_avoidance` switches the agent between the 2D avoidance (xz axis) and the 3D avoidance (xyz axis) on the next update. Note that 2D avoidance and 3D avoidance run in separate avoidance simulations so agents split between them do not affect each other. - - The properties ``avoidance_layers`` and ``avoidance_mask`` are bitmasks similar to e.g. physics layers. Agents will only avoid other avoidance objects that are on an avoidance layer that matches at least one of their own avoidance mask bits. - - The ``avoidance_priority`` makes agents with a higher priority ignore agents with a lower priority. This can be used to give certain agents more importance in the avoidance simulation, e.g. important npcs characters, without constantly changing their entire avoidance layers or mask. + - The properties `avoidance_layers` and `avoidance_mask` are bitmasks similar to e.g. physics layers. Agents will only avoid other avoidance objects that are on an avoidance layer that matches at least one of their own avoidance mask bits. + - The `avoidance_priority` makes agents with a higher priority ignore agents with a lower priority. This can be used to give certain agents more importance in the avoidance simulation, e.g. important npcs characters, without constantly changing their entire avoidance layers or mask. Avoidance exists in its own space and has no information from navigation meshes or physics collision. Behind the scene avoidance agents are just circles with different radius on a flat 2D plane or spheres in an otherwise empty 3D space. -NavigationObstacles can be used to add some environment constrains to the avoidance simulation, see :ref:`doc_navigation_using_navigationobstacles`. +NavigationObstacles can be used to add some environment constrains to the avoidance simulation, see `doc_navigation_using_navigationobstacles`. -.. note:: +Note: Avoidance does not affect the pathfinding. It should be seen as an additional option for constantly moving objects that cannot be (re)baked to a navigation mesh efficiently in order to move around them. -Using the NavigationAgent ``enable_avoidance`` property is the preferred option +Using the NavigationAgent `enable_avoidance` property is the preferred option to toggle avoidance. The following code snippets can be used to toggle avoidance on agents, create or delete avoidance callbacks or switch avoidance modes. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends NavigationAgent2D var agent: RID = get_rid() @@ -150,10 +144,11 @@ toggle avoidance on agents, create or delete avoidance callbacks or switch avoid NavigationServer2D.agent_set_avoidance_enabled(agent, false) # Delete avoidance callback NavigationServer2D.agent_set_avoidance_callback(agent, Callable()) +``` -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends NavigationAgent3D var agent: RID = get_rid() @@ -170,20 +165,19 @@ toggle avoidance on agents, create or delete avoidance callbacks or switch avoid NavigationServer3D.agent_set_avoidance_callback(agent, Callable()) # Switch to 2D avoidance NavigationServer3D.agent_set_use_3d_avoidance(agent, false) +``` -NavigationAgent Script Templates --------------------------------- +## NavigationAgent Script Templates The following sections provides script templates for nodes commonly used with NavigationAgents. -Actor as Node3D -~~~~~~~~~~~~~~~ +### Actor as Node3D This script adds basic navigation movement to a Node3D with a NavigationAgent3D child node. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends Node3D @export var movement_speed: float = 4.0 @@ -211,15 +205,15 @@ This script adds basic navigation movement to a Node3D with a NavigationAgent3D func _on_velocity_computed(safe_velocity: Vector3) -> void: global_position = global_position.move_toward(global_position + safe_velocity, movement_delta) +``` -Actor as CharacterBody3D -~~~~~~~~~~~~~~~~~~~~~~~~ +### Actor as CharacterBody3D This script adds basic navigation movement to a CharacterBody3D with a NavigationAgent3D child node. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends CharacterBody3D @export var movement_speed: float = 4.0 @@ -246,15 +240,15 @@ This script adds basic navigation movement to a CharacterBody3D with a Navigatio func _on_velocity_computed(safe_velocity: Vector3): velocity = safe_velocity move_and_slide() +``` -Actor as RigidBody3D -~~~~~~~~~~~~~~~~~~~~ +### Actor as RigidBody3D This script adds basic navigation movement to a RigidBody3D with a NavigationAgent3D child node. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` extends RigidBody3D @export var movement_speed: float = 4.0 @@ -280,3 +274,4 @@ This script adds basic navigation movement to a RigidBody3D with a NavigationAge func _on_velocity_computed(safe_velocity: Vector3): linear_velocity = safe_velocity +``` diff --git a/03_usage/11_navigation/10_navigation_using_navigationobstacles.md b/03_usage/11_navigation/10_navigation_using_navigationobstacles.md index f6845f9..98a654d 100644 --- a/03_usage/11_navigation/10_navigation_using_navigationobstacles.md +++ b/03_usage/11_navigation/10_navigation_using_navigationobstacles.md @@ -1,30 +1,26 @@ -.. _doc_navigation_using_navigationobstacles: -Using NavigationObstacles -========================= +# Using NavigationObstacles NavigationObstacles can be used either as static or dynamic obstacles to affect avoidance controlled agents. - When used statically NavigationObstacles constrain avoidance controlled agents outside or inside a polygon defined area. - When used dynamically NavigationObstacles push away avoidance controlled agents in a radius around them. -2D and 3D versions of NavigationObstacles nodes are available as -:ref:`NavigationObstacle2D` and -:ref:`NavigationObstacle3D` respectively. +2D and 3D versions of NavigationObstacles nodes are available as `NavigationObstacle2D` and `NavigationObstacle3D` respectively. + +Note: -.. note:: NavigationObstacles do not change or influence the pathfinding in any way. NavigationObstacles only affect the avoidance velocities of agents controlled by avoidance. -Static obstacles -~~~~~~~~~~~~~~~~ +### Static obstacles -A NavigationObstacle is considered static when its ``vertices`` property is populated with an outline array of positions to form a polygon. +A NavigationObstacle is considered static when its `vertices` property is populated with an outline array of positions to form a polygon. -.. image:: img/nav_static_obstacle_build.gif +![](img/nav_static_obstacle_build.gif) - Static obstacles act as hard do-not-cross boundaries for avoidance using agents, e.g. similar to physics collision but for avoidance. -- Static obstacles define their boundaries with an array of outline ``vertices`` (positions), and in case of 3D with an additional ``height`` property. +- Static obstacles define their boundaries with an array of outline `vertices` (positions), and in case of 3D with an additional `height` property. - Static obstacles only work for agents that use the 2D avoidance mode. - Static obstacles define through winding order of the vertices if agents are pushed out or sucked in. - Static obstacles can not change their position. They can only be warped to a new position and rebuild from scratch. Static obstacles as a result are ill-suited for usages where the position is changed every frame as the constant rebuild has a high performance cost. @@ -32,13 +28,12 @@ A NavigationObstacle is considered static when its ``vertices`` property is popu When the 2D avoidance is used in 3D the y-axis of Vector3 vertices is ignored. Instead, the global y-axis position of the obstacle is used as the elevation level. Agents will ignore static obstacles in 3D that are below or above them. This is automatically determined by global y-axis position of both obstacle and agent as the elevation level as well as their respective height properties. -Dynamic obstacles -~~~~~~~~~~~~~~~~~ +### Dynamic obstacles -A NavigationObstacle is considered dynamic when its ``radius`` property is greater than zero. +A NavigationObstacle is considered dynamic when its `radius` property is greater than zero. - Dynamic obstacles act as a soft please-move-away-from-me object for avoidance using agents, e.g. similar to how they avoid other agents. -- Dynamic obstacles define their boundaries with a single ``radius`` for a 2D circle, or in case of 3D avoidance a sphere shape. +- Dynamic obstacles define their boundaries with a single `radius` for a 2D circle, or in case of 3D avoidance a sphere shape. - Dynamic obstacles can change their position every frame without additional performance cost. - Dynamic obstacles with a set velocity can be predicted in their movement by agents. - Dynamic obstacles are not a reliable way to constrain agents in crowded or narrow spaces. @@ -46,21 +41,20 @@ A NavigationObstacle is considered dynamic when its ``radius`` property is great While both static and dynamic properties can be active at the same time on the same obstacle this is not recommended for performance. Ideally when an obstacle is moving the static vertices are removed and instead the radius activated. When the obstacle reaches the new final position it should gradually enlarge its radius to push all other agents away. With enough created save space around the obstacle it should add the static vertices again and remove the radius. This helps to avoid getting agents stuck in the suddenly appearing static obstacle when the rebuild static boundary is finished. -Similar to agents the obstacles can make use of the ``avoidance_layers`` bitmask. +Similar to agents the obstacles can make use of the `avoidance_layers` bitmask. All agents with a matching bit on their own avoidance mask will avoid the obstacle. -Procedual obstacles -~~~~~~~~~~~~~~~~~~~ +### Procedual obstacles New obstacles can be created without a Node directly on the NavigationServer. -Obstacles created with scripts require at least a ``map`` and a ``position``. -For dynamic use a ``radius`` is required. -For static use an array of ``vertices`` is required. +Obstacles created with scripts require at least a `map` and a `position`. +For dynamic use a `radius` is required. +For static use an array of `vertices` is required. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` # For 2D # create a new "obstacle" and place it on the default navigation map. @@ -79,10 +73,11 @@ For static use an array of ``vertices`` is required. # Enable the obstacle. NavigationServer2D.obstacle_set_avoidance_enabled(new_obstacle_rid, true) +``` -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` # For 3D # Create a new "obstacle" and place it on the default navigation map. @@ -103,3 +98,4 @@ For static use an array of ``vertices`` is required. # Enable the obstacle. NavigationServer3D.obstacle_set_avoidance_enabled(new_obstacle_rid, true) +``` diff --git a/03_usage/11_navigation/11_navigation_using_navigationlinks.md b/03_usage/11_navigation/11_navigation_using_navigationlinks.md index 642ecb3..52b2d9c 100644 --- a/03_usage/11_navigation/11_navigation_using_navigationlinks.md +++ b/03_usage/11_navigation/11_navigation_using_navigationlinks.md @@ -1,50 +1,47 @@ -.. _doc_navigation_using_navigationlinks: -Using NavigationLinks -===================== +# Using NavigationLinks -.. image:: img/nav_navmesh_links.png +![](img/nav_navmesh_links.png) -NavigationLinks are used to connect navigation mesh polygons from :ref:`NavigationRegion2D` -and :ref:`NavigationRegion3D` over arbitrary distances for pathfinding. +NavigationLinks are used to connect navigation mesh polygons from `NavigationRegion2D` +and `NavigationRegion3D` over arbitrary distances for pathfinding. NavigationLinks are also used to consider movement shortcuts in pathfinding available through interacting with gameplay objects e.g. ladders, jump pads or teleports. 2D and 3D versions of NavigationJumplinks nodes are available as -:ref:`NavigationLink2D` and -:ref:`NavigationLink3D` respectively. +`NavigationLink2D` and `NavigationLink3D` respectively. Different NavigationRegions can connect their navigation meshes without the need for a NavigationLink -as long as they are within navigation map ``edge_connection_margin`` and have compatible ``navigation_layers``. +as long as they are within navigation map `edge_connection_margin` and have compatible `navigation_layers`. As soon as the distance becomes too large, building valid connections becomes a problem - a problem that NavigationLinks can solve. -See :ref:`doc_navigation_using_navigationregions` to learn more about the use of navigation regions. -See :ref:`doc_navigation_connecting_navmesh` to learn more about how to connect navigation meshes. +See `doc_navigation_using_navigationregions` to learn more about the use of navigation regions. +See `doc_navigation_connecting_navmesh` to learn more about how to connect navigation meshes. -.. image:: img/nav_link_properties.png +![](img/nav_link_properties.png) -NavigationLinks share many properties with NavigationRegions like ``navigation_layers``. +NavigationLinks share many properties with NavigationRegions like `navigation_layers`. NavigationLinks add a single connection between two positions over an arbitrary distance compared to NavigationRegions that add a more local traversable area with a navigation mesh resource. -NavigationLinks have a ``start_position`` and ``end_position`` and can go in both directions when ``bidirectional`` is enabled. -When placed a navigationlink connects the navigation mesh polygons closest to its ``start_position`` and ``end_position`` within search radius for pathfinding. +NavigationLinks have a `start_position` and `end_position` and can go in both directions when `bidirectional` is enabled. +When placed a navigationlink connects the navigation mesh polygons closest to its `start_position` and `end_position` within search radius for pathfinding. -The polygon search radius can be configured globally in the ProjectSettings under ``navigation/2d_or_3d/default_link_connection_radius`` -or set for each navigation ``map`` individually using the ``NavigationServer.map_set_link_connection_radius()`` function. +The polygon search radius can be configured globally in the ProjectSettings under `navigation/2d_or_3d/default_link_connection_radius` +or set for each navigation `map` individually using the `NavigationServer.map_set_link_connection_radius()` function. -Both ``start_position`` and ``end_position`` have debug markers in the Editor. +Both `start_position` and `end_position` have debug markers in the Editor. The visible radius of a position shows the polygon search radius. All navigation mesh polygons inside are compared and the closest is picked for the edge connection. If no valid polygon is found within the search radius the navigation link gets disabled. -.. image:: img/nav_link_debug_visuals.png +![](img/nav_link_debug_visuals.png) -The link debug visuals can be changed in the Editor :ref:`ProjectSettings` under ``debug/shapes/navigation``. +The link debug visuals can be changed in the Editor `ProjectSettings` under `debug/shapes/navigation`. The visibility of the debug can also be controlled in the Editor 3D Viewport gizmo menu. -.. note:: +Note: NavigationLinks do not move agents between the two link positions by themselves. diff --git a/03_usage/11_navigation/12_navigation_using_navigationlayers.md b/03_usage/11_navigation/12_navigation_using_navigationlayers.md index 8fb3548..ef69682 100644 --- a/03_usage/11_navigation/12_navigation_using_navigationlayers.md +++ b/03_usage/11_navigation/12_navigation_using_navigationlayers.md @@ -1,30 +1,28 @@ -.. _doc_navigation_advanced_using_navigationlayers: -Using NavigationLayers -====================== +# Using NavigationLayers NavigationLayers are an optional feature to further control which navigation meshes are considered in a path query and which regions can be connected. They work similar to how physics layers control collision between collision objects or how visual layers control what is rendered to the Viewport. -NavigationLayers can be named in the ``ProjectSettings`` the same as PhysicsLayers or VisualLayers. +NavigationLayers can be named in the `ProjectSettings` the same as PhysicsLayers or VisualLayers. -.. image:: img/navigationlayers_naming.png +![](img/navigationlayers_naming.png) -If two regions have not a single compatible layer they will not be merged by the NavigationServer. See :ref:`doc_navigation_connecting_navmesh` for more information on merging navmesh. +If two regions have not a single compatible layer they will not be merged by the NavigationServer. See `doc_navigation_connecting_navmesh` for more information on merging navmesh. -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. +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 `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 +GDScript +``` func change_layers(): var region: NavigationRegion3D = get_node("NavigationRegion3D") # enables 4-th layer for this region @@ -55,6 +53,7 @@ In scripts the following helper functions can be used to work with the navigatio 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 diff --git a/03_usage/11_navigation/13_navigation_debug_tools.md b/03_usage/11_navigation/13_navigation_debug_tools.md index f263afc..cb18fa0 100644 --- a/03_usage/11_navigation/13_navigation_debug_tools.md +++ b/03_usage/11_navigation/13_navigation_debug_tools.md @@ -1,78 +1,72 @@ -.. _doc_navigation_debug_tools: -Navigation Debug Tools -====================== +# Navigation Debug Tools -.. note:: +Note: The debug tools, properties and functions are only available in Pandemonium debug builds. Do not use any of them in code that will be part of a release build. -Enabling debug navigation -------------------------- +## Enabling debug navigation The navigation debug visualization is enabled by default inside the Editor. To visualize navigation meshes and connections also at runtime -enable the option ``Visible Navigation`` in the editor debug menu. +enable the option `Visible Navigation` in the editor debug menu. -.. image:: img/navigation_debug_toggle.png +![](img/navigation_debug_toggle.png) In Pandemonium debug builds the navigation debug can also be toggled on the NavigationServers from scripts. -.. tabs:: - .. code-tab:: gdscript GDScript - +GDScript + +``` NavigationServer2D.set_debug_enabled(false) NavigationServer3D.set_debug_enabled(true) +``` -Debug navigation settings -------------------------- +## Debug navigation settings -The appearance of navigation debug can be change in the ProjectSettings under ``debug/shapes/navigation``. +The appearance of navigation debug can be change in the ProjectSettings under `debug/shapes/navigation`. Certain debug features can also be enabled or disabled at will but may require a scene restart to apply. -.. image:: img/nav_debug_settings.png +![](img/nav_debug_settings.png) -Debug navigation mesh polygons ------------------------------- +## Debug navigation mesh polygons -If ``enable_edge_lines`` is enabled the edges of navigation mesh polygons will be highlighted. -If ``enable_edge_lines_xray`` is also enabled the edges of navigationmeshes will be visible through geometry. +If `enable_edge_lines` is enabled the edges of navigation mesh polygons will be highlighted. +If `enable_edge_lines_xray` is also enabled the edges of navigationmeshes will be visible through geometry. -if ``enable_geometry_face_random_color`` is enabled each navigation mesh face receives -a random color that is mixed with the main color from ``geometry_face_color``. +if `enable_geometry_face_random_color` is enabled each navigation mesh face receives +a random color that is mixed with the main color from `geometry_face_color`. -.. image:: img/nav_debug_xray_edge_lines.png +![](img/nav_debug_xray_edge_lines.png) -Debug edge connections ----------------------- +## Debug edge connections -Different navigation meshes connected within ``edge_connection_margin`` distance are overlaid. -The color of the overlay is controlled with the navigation debug ``edge_connection_color``. -The connections can be made visible through geometry with the navigation debug ``enable_edge_connections_xray`` property. +Different navigation meshes connected within `edge_connection_margin` distance are overlaid. +The color of the overlay is controlled with the navigation debug `edge_connection_color`. +The connections can be made visible through geometry with the navigation debug `enable_edge_connections_xray` property. -.. image:: img/nav_edge_connection2d.gif +![](img/nav_edge_connection2d.gif) -.. image:: img/nav_edge_connection3d.gif +![](img/nav_edge_connection3d.gif) -.. note:: +Note: Edge connections are only visible when the NavigationServer is active. -Debug Performance ------------------ +## Debug Performance To measure NavigationServer performance a dedicated monitor exists that can be found within the Editor Debugger under `Debugger->Monitors->NavigationProcess`. -.. image:: img/navigation_debug_performance1.webp +![](img/navigation_debug_performance1.webp) NavigationProcess shows how long the NavigationServer spends updating its internals this update frame in milliseconds. NavigationProcess works similar to Process for visual frame rendering and PhysicsProcess for collision and fixed updates. -NavigationProcess accounts for all updates to ``navigation maps``, ``navigation regions`` and ``navigation agents`` as well as all the ``avoidance calculations`` for the update frame. +NavigationProcess accounts for all updates to `navigation maps`, `navigation regions` and `navigation agents` as well as all the `avoidance calculations` for the update frame. -.. note:: +Note: NavigationProcess does NOT include pathfinding performance cause pathfinding operates on the navigation map data independently from the server process update. @@ -81,7 +75,7 @@ Note that since the NavigationServer process update happens in the middle of the Navigation also provides more detailed statistics about the current navigation related objects and navigation map composition on the NavigationServer. -.. image:: img/navigation_debug_performance2.webp +![](img/navigation_debug_performance2.webp) Navigation statistics shown here can not be judged as good or bad for performance as it depends entirely on the project what can be considered as reasonable or horribly excessive. diff --git a/03_usage/11_navigation/14_navigation_connecting_navmesh.md b/03_usage/11_navigation/14_navigation_connecting_navmesh.md index a465020..21b9a0c 100644 --- a/03_usage/11_navigation/14_navigation_connecting_navmesh.md +++ b/03_usage/11_navigation/14_navigation_connecting_navmesh.md @@ -1,14 +1,12 @@ -.. _doc_navigation_connecting_navmesh: -Connecting NavigationMeshes -=========================== +# Connecting NavigationMeshes 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`. +To connect over arbitrary distances see `doc_navigation_using_navigationlinks`. -.. image:: img/navigation_vertex_merge.png +![](img/navigation_vertex_merge.png) The same is true for multiple NavigationPolygon resources. As long as their outline points overlap exactly the NavigationServer will merge them. @@ -19,46 +17,48 @@ 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 +![](img/navigation_vertex_merge2.png) -.. warning:: +Warning: 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 +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 +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 +![](img/navigation_edge_connection.png) 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. +See `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``. +The default 2D `edge_connection_margin` can be changed in the ProjectSettings under `navigation/2d/default_edge_connection_margin`. -The default 3D ``edge_connection_margin`` can be changed in the ProjectSettings under ``navigation/3d/default_edge_connection_margin``. +The default 3D `edge_connection_margin` can be changed in the ProjectSettings under `navigation/3d/default_edge_connection_margin`. The edge connection margin value of any navigation map can also be changed at runtime with the NavigationServer API. -.. tabs:: - .. code-tab:: gdscript 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() NavigationServer2D.map_set_edge_connection_margin(default_2d_map_rid, 50.0) +``` -.. tabs:: - .. code-tab:: gdscript 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() NavigationServer3D.map_set_edge_connection_margin(default_3d_map_rid, 0.5) +``` -.. note:: +Note: Changing the edge connection margin will trigger a full update of all navigation mesh connections on the NavigationServer. diff --git a/03_usage/11_navigation/15_navigation_different_actor_types.md b/03_usage/11_navigation/15_navigation_different_actor_types.md index 4b117a9..69d7041 100644 --- a/03_usage/11_navigation/15_navigation_different_actor_types.md +++ b/03_usage/11_navigation/15_navigation_different_actor_types.md @@ -1,21 +1,19 @@ -.. _doc_navigation_different_actor_types: -Support different actor types -============================= +# Support different actor types -.. image:: img/nav_actor_sizes.png +![](img/nav_actor_sizes.png) 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. -.. note:: +Note: Agents are exclusively defined by a radius and height value for baking navigation meshes, pathfinding and avoidance. More complex shapes are not supported. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` # Create a navigation mesh resource for each actor size. var navigation_mesh_standard_size: NavigationMesh = NavigationMesh.new() var navigation_mesh_small_size: NavigationMesh = NavigationMesh.new() @@ -79,3 +77,4 @@ The same approach can be used to distinguish between e.g. landwalking, swimming var path_standard_agent = NavigationServer3D.map_get_path(navigation_map_standard, start_pos, end_pos, use_corridorfunnel) var path_small_agent = NavigationServer3D.map_get_path(navigation_map_small, start_pos, end_pos, use_corridorfunnel) var path_huge_agent = NavigationServer3D.map_get_path(navigation_map_huge, start_pos, end_pos, use_corridorfunnel) +``` diff --git a/03_usage/11_navigation/16_navigation_different_actor_locomotion.md b/03_usage/11_navigation/16_navigation_different_actor_locomotion.md index 7b0cc20..f50e126 100644 --- a/03_usage/11_navigation/16_navigation_different_actor_locomotion.md +++ b/03_usage/11_navigation/16_navigation_different_actor_locomotion.md @@ -1,12 +1,10 @@ -.. _doc_navigation_different_actor_locomotion: -Support different actor locomotion -================================== +# Support different actor locomotion -.. image:: img/nav_actor_locomotion.png +![](img/nav_actor_locomotion.png) To support different actor locomotion like crouching and crawling, a similar -map setup as supporting :ref:`doc_navigation_different_actor_types` is required. +map setup as supporting `doc_navigation_different_actor_types` is required. Bake different navigation meshes with an appropriate height for crouched or crawling actors so they can find paths through those narrow sections in your game world. @@ -17,9 +15,9 @@ crouching or crawling, query the appropriate map for a path. If the avoidance behavior should also change with the locomotion e.g. only avoid while standing or only avoid other agents in the same locomotion state, switch the actors's avoidance agent to another avoidance map with each locomotion change. -.. tabs:: - .. code-tab:: gdscript GDScript +GDScript +``` func update_path(): if actor_standing: @@ -37,7 +35,8 @@ other agents in the same locomotion state, switch the actors's avoidance agent t NavigationServer3D.agent_set_map(avoidance_agent_rid, crouched_navigation_map_rid) elif actor_crawling: NavigationServer3D.agent_set_map(avoidance_agent_rid, crawling_navigation_map_rid) +``` -.. note:: +Note: While a path query can be execute immediately for multiple maps, the avoidance agent map switch will only take effect after the next server synchronization. diff --git a/03_usage/11_navigation/17_navigation_different_actor_area_access.md b/03_usage/11_navigation/17_navigation_different_actor_area_access.md index 792813b..8be44b3 100644 --- a/03_usage/11_navigation/17_navigation_different_actor_area_access.md +++ b/03_usage/11_navigation/17_navigation_different_actor_area_access.md @@ -1,9 +1,7 @@ -.. _doc_navigation_different_actor_area_access: -Support different actor area access -=================================== +# Support different actor area access -.. image:: img/nav_actor_doors.png +![](img/nav_actor_doors.png) A typical example for different area access in gameplay are doors that connect rooms with different navigation meshes and are not accessible by all actors all the time. @@ -17,13 +15,13 @@ The bitmask can act as a set of door keys or abilities and only actors with at l one matching and enabled bit layer in their pathfinding query will find a path through this region. See :ref:`doc_navigation_advanced_using_navigationlayers` for more information on how to work with navigation layers and the bitmask. -.. image:: img/nav_actor_doorbitmask.png +![](img/nav_actor_doorbitmask.png) The entire "door" region can also be enabled / disable if required but if disabled will block access for all path queries. Prefer working with navigation layers in path queries whenever possible as enabling or disabling navigation layers on a region triggers a performance costly recalculation of the navigation map connections. -.. warning:: +Warning: Changing navigation layers will only affect new path queries but not automatically update existing paths. diff --git a/03_usage/11_navigation/18_navigation_optimizing_performance.md b/03_usage/11_navigation/18_navigation_optimizing_performance.md index 1130421..a68cf9b 100644 --- a/03_usage/11_navigation/18_navigation_optimizing_performance.md +++ b/03_usage/11_navigation/18_navigation_optimizing_performance.md @@ -1,9 +1,7 @@ -.. _doc_navigation_optimizing_performance: -Optimizing Navigation Performance -================================= +# Optimizing Navigation Performance -.. image:: img/nav_optimization.webp +![](img/nav_optimization.webp) Common Navigation related performance problems can be categorized into the following topics: @@ -15,10 +13,9 @@ Common Navigation related performance problems can be categorized into the follo In the following sections information can be found on how to identify and fix or at least mitigate their impact on framerates. -Performance problems with parsing SceneTree nodes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Performance problems with parsing SceneTree nodes -.. tip:: +Tip: Prefer using simple shapes with as few edges as possible e.g. nothing rounded like a circle, sphere or torus. @@ -34,18 +31,18 @@ On top, to gain access to visual mesh data the parser needs to request the mesh This requires locking the RenderingServer thread and can severely impact framerate at runtime while the rendering runs multi-threaded. If the rendering runs single-threaded, the framerate impact might be even worse and the mesh parsing might freeze the entire game for a few seconds on complex meshes. -Performance problems with navigation mesh baking -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Performance problems with navigation mesh baking -.. tip:: +Tip: At runtime, always prefer to use a background thread for baking navigation meshes. - Increase NavigationMesh ``cell_size`` and ``cell_height`` to create less voxels. + Increase NavigationMesh `cell_size` and `cell_height` to create less voxels. - Change the ``SamplePartitionType`` from watershed to monotone or layers to gain baking performance. + Change the `SamplePartitionType` from watershed to monotone or layers to gain baking performance. + +Warning: -.. warning:: NEVER scale source geometry with nodes to avoid precision errors. Most scale applies only visually and shapes that are very large at their base scale require still a lot of extra processing even while downscaled. Baking navigation meshes at runtime should always be done in a background thread if possible. Even small sized navigation meshes can take far longer to bake than what is possible to squeeze into a single frame, at least if the framerate should stay at a bearable level. @@ -60,10 +57,9 @@ E.g. games with mostly flat surfaces with blocky geometry can get away with the Never scale source geometry with nodes. Not only can it result in a lot of precision errors with wrongly matched vertices and edges but also some scaling only exists as visuals and not in the actual parsed data. E.g. if a mesh is downscaled visually in the Editor, e.g. the scale set to 0.001 on a MeshInstance, the mesh still requires a gigantic and very complex voxel grid to be processed for the baking. -Performance problems with NavigationAgent path queries -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Performance problems with NavigationAgent path queries -.. tip:: +Tip: Avoid unnecessary path resets and queries every frame in NavigationAgent scripts. @@ -82,10 +78,9 @@ This avoids doing the equivalent of two full path queries every frame for the sa Divide the total number of NavigationAgents into update groups or use random timers so that they do not all request new paths in the same frame. -Performance problems with the actual path search -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Performance problems with the actual path search -.. tip:: +Tip: Optimize overdetailed navigation meshes by reducing the amount of polygons and edges. @@ -98,10 +93,9 @@ This performance drop is "normal" and the result of a too large, too unoptimized In normal path searches where the target position can be reached quickly the pathfinding will do an early exit as soon as the position is reached which can hide this lack of optimization for a while. If the target position can not be reached the pathfinding has to do a far longer search through the available polygons to confirm that the position is absolutely not reachable. -Performance problems with navigation map synchronization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Performance problems with navigation map synchronization -.. tip:: +Tip: Merge navigation meshes polygons by vertex instead of by edge connection wherever possible. diff --git a/03_usage/11_navigation/19_real_time_navigation_3d.md b/03_usage/11_navigation/19_real_time_navigation_3d.md index b09e0fd..a4c223e 100644 --- a/03_usage/11_navigation/19_real_time_navigation_3d.md +++ b/03_usage/11_navigation/19_real_time_navigation_3d.md @@ -1,10 +1,8 @@ -Real Time Navigation (3D) -========================= +# Real Time Navigation (3D) -Introduction ------------- +## Introduction Pathfinding in a 3D environment is crucial for many games, it's commonly how non directly controlled characters or entities find their way around @@ -15,8 +13,7 @@ an environment. Pandemonium provides several nodes for this purpose: - `NavigationAgent( NavigationAgent )` - `NavigationObstacle( NavigationObstacle )` -The map and navigation regions ------------------------------- +## The map and navigation regions The "map" is the entire world for navigation, it's similar to "space" for the physics engine. It's comprised of navigation regions, these regions @@ -37,7 +34,6 @@ button at the top of the inspector to generate it. Note: - It can also be generated at runtime using the `bake_navigation_region()` method of the navigation region node. @@ -56,8 +52,7 @@ to each other. Additionally a baked navmesh can be moved at runtime and agents will still be able to navigate onto it from another region. For example, navigating onto a moving platform that has stopped will work. -NavigationAgent3D ------------------ +## NavigationAgent3D Navigation agent nodes are what actually does the pathfinding in a scene, one can be attached to the root node of an entity that needs to navigate. @@ -66,8 +61,7 @@ To have it pathfind use its `set_target_location` method. Once the target has been set a path will be generated to the node using navigation regions, with several points on the way to the final destination. -RVO processing --------------- +## RVO processing RVO stands for reciprocal velocity obstacle. RVO processing is a way to pathfind while taking into account other agents and physics bodies that @@ -84,8 +78,7 @@ agents, this is the `max neighbors( NavigationAgent_property_max_neighbors )` property of an agent and can be adjusted. This is **not** a limit for how many agents can use a navigation region at the same time. -NavigationObstacle3D --------------------- +## NavigationObstacle3D This node is used to mark physics bodies that move around a navigation area that agents need to avoid (this will only work if you use RVO processing). @@ -93,8 +86,7 @@ For example, this node would be useful for pieces of debris in a destructible environment. Add it as the child of a physics body and navigation agent nodes will avoid it while pathfinding. -Generating a path (old method) ------------------------------- +## Generating a path (old method) This is the old method for generating a navigation path, it will be removed in Pandemonium 4. First, add a navigation node to the scene, then