mirror of
https://github.com/godotengine/godot-docs.git
synced 2026-01-08 10:10:54 +03:00
Merge pull request #9833 from ShawnHardern/update-csharp-docs
Add missing C# code examples to documentation
This commit is contained in:
@@ -5,11 +5,19 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
func _ready():
|
||||
print("Hello world!")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
GD.Print("Hello world!");
|
||||
}
|
||||
|
||||
Welcome to the official documentation of **Godot Engine**, the free and open source
|
||||
community-driven 2D and 3D game engine! Behind this mouthful, you will find a
|
||||
powerful yet user-friendly tool that you can use to develop any kind of game,
|
||||
|
||||
@@ -75,11 +75,17 @@ Translate** in the inspector.
|
||||
In code, the :ref:`Object.tr() <class_Object_method_tr>` function can be used.
|
||||
This will just look up the text in the translations and convert it if found:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
level.text = tr("LEVEL_5_NAME")
|
||||
status.text = tr("GAME_STATUS_%d" % status_index)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
level.Text = Tr("LEVEL_5_NAME");
|
||||
status.Text = Tr($"GAME_STATUS_{statusIndex}");
|
||||
|
||||
.. note::
|
||||
|
||||
If no text is displayed after changing the language, try to use a different
|
||||
@@ -105,7 +111,8 @@ allows translations to sound more natural. Named placeholders with the
|
||||
``String.format()`` function should be used whenever possible, as they also
|
||||
allow translators to choose the *order* in which placeholders appear:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
# The placeholder's locations can be changed, but not their order.
|
||||
# This will probably not suffice for some target languages.
|
||||
@@ -125,7 +132,8 @@ optionally specify a *translation context* to resolve this ambiguity and allow
|
||||
target languages to use different strings, even though the source string is
|
||||
identical:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
# "Close", as in an action (to close something).
|
||||
button.set_text(tr("Close", "Actions"))
|
||||
@@ -133,6 +141,14 @@ identical:
|
||||
# "Close", as in a distance (opposite of "far").
|
||||
distance_label.set_text(tr("Close", "Distance"))
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// "Close", as in an action (to close something).
|
||||
GetNode<Button>("Button").Text = Tr("Close", "Actions");
|
||||
|
||||
// "Close", as in a distance (opposite of "far").
|
||||
GetNode<Label>("Distance").Text = Tr("Close", "Distance");
|
||||
|
||||
Pluralization
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
@@ -148,18 +164,30 @@ Pluralization is meant to be used with positive (or zero) integer numbers only.
|
||||
Negative and floating-point values usually represent physical entities for which
|
||||
singular and plural don't clearly apply.
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
var num_apples = 5
|
||||
label.text = tr_n("There is %d apple", "There are %d apples", num_apples) % num_apples
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
int numApples = 5;
|
||||
GetNode<Label>("Label").Text = string.Format(TrN("There is {0} apple", "There are {0} apples", numApples), numApples);
|
||||
|
||||
This can be combined with a context if needed:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
var num_jobs = 1
|
||||
label.text = tr_n("%d job", "%d jobs", num_jobs, "Task Manager") % num_jobs
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
int numJobs = 1;
|
||||
GetNode<Label>("Label").Text = string.Format(TrN("{0} job", "{0} jobs", numJobs, "Task Manager"), numJobs);
|
||||
|
||||
.. note::
|
||||
|
||||
Providing pluralized translations is only supported with
|
||||
|
||||
@@ -45,9 +45,10 @@ Plain text and binary files
|
||||
Godot's :ref:`class_FileAccess` class provides methods to access files on the
|
||||
filesystem for reading and writing:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
func save_file(content):
|
||||
func save_file(content):
|
||||
var file = FileAccess.open("/path/to/file.txt", FileAccess.WRITE)
|
||||
file.store_string(content)
|
||||
|
||||
@@ -56,6 +57,21 @@ filesystem for reading and writing:
|
||||
var content = file.get_as_text()
|
||||
return content
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private void SaveFile(string content)
|
||||
{
|
||||
using var file = FileAccess.Open("/Path/To/File.txt", FileAccess.ModeFlags.Write);
|
||||
file.StoreString(content);
|
||||
}
|
||||
|
||||
private string LoadFile()
|
||||
{
|
||||
using var file = FileAccess.Open("/Path/To/File.txt", FileAccess.ModeFlags.Read);
|
||||
string content = file.GetAsText();
|
||||
return content;
|
||||
}
|
||||
|
||||
To handle custom binary formats (such as loading file formats not supported by
|
||||
Godot), :ref:`class_FileAccess` provides several methods to read/write integers,
|
||||
floats, strings and more. These FileAccess methods have names that start with
|
||||
@@ -116,7 +132,8 @@ increase performance by reducing I/O utilization.
|
||||
Example of loading an image and displaying it in a :ref:`class_TextureRect` node
|
||||
(which requires conversion to :ref:`class_ImageTexture`):
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
# Load an image of any format supported by Godot from the filesystem.
|
||||
var image = Image.load_from_file(path)
|
||||
@@ -131,6 +148,21 @@ Example of loading an image and displaying it in a :ref:`class_TextureRect` node
|
||||
# Save the converted ImageTexture to a PNG image.
|
||||
$TextureRect.texture.get_image().save_png("/path/to/file.png")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Load an image of any format supported by Godot from the filesystem.
|
||||
var image = Image.LoadFromFile(path);
|
||||
// Optionally, generate mipmaps if displaying the texture on a 3D surface
|
||||
// so that the texture doesn't look grainy when viewed at a distance.
|
||||
// image.GenerateMipmaps();
|
||||
GetNode<TextureRect>("TextureRect").Texture = ImageTexture.CreateFromImage(image);
|
||||
|
||||
// Save the loaded Image to a PNG image.
|
||||
image.SavePng("/Path/To/File.png");
|
||||
|
||||
// Save the converted ImageTexture to a PNG image.
|
||||
GetNode<TextureRect>("TextureRect").Texture.GetImage().SavePng("/Path/To/File.png");
|
||||
|
||||
.. _doc_runtime_file_loading_and_saving_audio_video_files:
|
||||
|
||||
Audio/video files
|
||||
@@ -143,13 +175,19 @@ load correctly as audio files in Godot.
|
||||
|
||||
Example of loading an Ogg Vorbis audio file in an :ref:`class_AudioStreamPlayer` node:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
$AudioStreamPlayer.stream = AudioStreamOggVorbis.load_from_file(path)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
GetNode<AudioStreamPlayer>("AudioStreamPlayer").Stream = AudioStreamOggVorbis.LoadFromFile(path);
|
||||
|
||||
Example of loading an Ogg Theora video file in a :ref:`class_VideoStreamPlayer` node:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
var video_stream_theora = VideoStreamTheora.new()
|
||||
# File extension is ignored, so it is possible to load Ogg Theora videos
|
||||
@@ -161,6 +199,18 @@ Example of loading an Ogg Theora video file in a :ref:`class_VideoStreamPlayer`
|
||||
# before this property is set, so call `play()` after setting `stream`.
|
||||
$VideoStreamPlayer.play()
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
var videoStreamTheora = new VideoStreamTheora();
|
||||
// File extension is ignored, so it is possible to load Ogg Theora videos
|
||||
// that have an `.ogg` extension this way.
|
||||
videoStreamTheora.File = "/Path/To/File.ogv";
|
||||
GetNode<VideoStreamPlayer>("VideoStreamPlayer").Stream = videoStreamTheora;
|
||||
|
||||
// VideoStreamPlayer's Autoplay property won't work if the stream is empty
|
||||
// before this property is set, so call `Play()` after setting `Stream`.
|
||||
GetNode<VideoStreamPlayer>("VideoStreamPlayer").Play();
|
||||
|
||||
.. note::
|
||||
|
||||
Godot doesn't support runtime loading of MP3 or WAV files yet. Until this is
|
||||
@@ -185,7 +235,8 @@ as it's faster to write and smaller, but the text format is easier to debug.
|
||||
|
||||
Example of loading a glTF scene and appending its root node to the scene:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
# Load an existing glTF scene.
|
||||
# GLTFState is used by GLTFDocument to store the loaded scene's state.
|
||||
@@ -209,6 +260,34 @@ Example of loading a glTF scene and appending its root node to the scene:
|
||||
# `GLTFDocument.generate_buffer()` is also available for saving to memory.
|
||||
gltf_document_save.write_to_filesystem(gltf_state_save, path)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Load an existing glTF scene.
|
||||
// GLTFState is used by GLTFDocument to store the loaded scene's state.
|
||||
// GLTFDocument is the class that handles actually loading glTF data into a Godot node tree,
|
||||
// which means it supports glTF features such as lights and cameras.
|
||||
var gltfDocumentLoad = new GltfDocument();
|
||||
var gltfStateLoad = new GltfState();
|
||||
var error = gltfDocumentLoad.AppendFromFile("/Path/To/File.gltf", gltfStateLoad);
|
||||
if (error == Error.Ok)
|
||||
{
|
||||
var gltfSceneRootNode = gltfDocumentLoad.GenerateScene(gltfStateLoad);
|
||||
AddChild(gltfSceneRootNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
GD.PrintErr($"Couldn't load glTF scene (error code: {error}).");
|
||||
}
|
||||
|
||||
// Save a new glTF scene.
|
||||
var gltfDocumentSave = new GltfDocument();
|
||||
var gltfStateSave = new GltfState();
|
||||
gltfDocumentSave.AppendFromScene(gltfSceneRootNode, gltfStateSave);
|
||||
// The file extension in the output `path` (`.gltf` or `.glb`) determines
|
||||
// whether the output uses text or binary format.
|
||||
// `GltfDocument.GenerateBuffer()` is also available for saving to memory.
|
||||
gltfDocumentSave.WriteToFilesystem(gltfStateSave, path);
|
||||
|
||||
.. note::
|
||||
|
||||
When loading a glTF scene, a *base path* must be set so that external
|
||||
@@ -240,7 +319,8 @@ Godot's support for :ref:`doc_using_fonts_system_fonts`.
|
||||
Example of loading a font file automatically according to its file extension,
|
||||
then adding it as a theme override to a :ref:`class_Label` node:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
var path = "/path/to/font.ttf"
|
||||
var path_lower = path.to_lower()
|
||||
@@ -263,6 +343,37 @@ then adding it as a theme override to a :ref:`class_Label` node:
|
||||
# If font was loaded successfully, add it as a theme override.
|
||||
$Label.add_theme_font_override("font", font_file)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
string path = "/Path/To/Font.ttf";
|
||||
var fontFile = new FontFile();
|
||||
|
||||
if (
|
||||
path.EndsWith(".ttf", StringComparison.OrdinalIgnoreCase)
|
||||
|| path.EndsWith(".otf", StringComparison.OrdinalIgnoreCase)
|
||||
|| path.EndsWith(".woff", StringComparison.OrdinalIgnoreCase)
|
||||
|| path.EndsWith(".woff2", StringComparison.OrdinalIgnoreCase)
|
||||
|| path.EndsWith(".pfb", StringComparison.OrdinalIgnoreCase)
|
||||
|| path.EndsWith(".pfm", StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
{
|
||||
fontFile.LoadDynamicFont(path);
|
||||
}
|
||||
else if (path.EndsWith(".fnt", StringComparison.OrdinalIgnoreCase) || path.EndsWith(".font", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
fontFile.LoadBitmapFont(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
GD.PrintErr("Invalid font file format.");
|
||||
}
|
||||
|
||||
if (!fontFile.Data.IsEmpty())
|
||||
{
|
||||
// If font was loaded successfully, add it as a theme override.
|
||||
GetNode<Label>("Label").AddThemeFontOverride("font", fontFile);
|
||||
}
|
||||
|
||||
ZIP archives
|
||||
------------
|
||||
|
||||
@@ -285,7 +396,8 @@ through the Godot editor to generate PCK/ZIP files.
|
||||
Example that lists files in a ZIP archive in an :ref:`class_ItemList` node,
|
||||
then writes contents read from it to a new ZIP archive (essentially duplicating the archive):
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
# Load an existing ZIP archive.
|
||||
var zip_reader = ZIPReader.new()
|
||||
@@ -312,3 +424,37 @@ then writes contents read from it to a new ZIP archive (essentially duplicating
|
||||
zip_packer.close_file()
|
||||
|
||||
zip_packer.close()
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Load an existing ZIP archive.
|
||||
var zipReader = new ZipReader();
|
||||
zipReader.Open(path);
|
||||
string[] files = zipReader.GetFiles();
|
||||
// The list of files isn't sorted by default. Sort it for more consistent processing.
|
||||
Array.Sort(files);
|
||||
foreach (string file in files)
|
||||
{
|
||||
GetNode<ItemList>("ItemList").AddItem(file);
|
||||
// Make folders disabled in the list.
|
||||
GetNode<ItemList>("ItemList").SetItemDisabled(-1, file.EndsWith('/'));
|
||||
}
|
||||
|
||||
// Save a new ZIP archive.
|
||||
var zipPacker = new ZipPacker();
|
||||
var error = zipPacker.Open(path);
|
||||
if (error != Error.Ok)
|
||||
{
|
||||
GD.PrintErr($"Couldn't open path for saving ZIP archive (error code: {error}).");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reuse the above ZIPReader instance to read files from an existing ZIP archive.
|
||||
foreach (string file in zipReader.GetFiles())
|
||||
{
|
||||
zipPacker.StartFile(file);
|
||||
zipPacker.WriteFile(zipReader.ReadFile(file));
|
||||
zipPacker.CloseFile();
|
||||
}
|
||||
|
||||
zipPacker.Close();
|
||||
|
||||
@@ -53,6 +53,20 @@ The edge connection margin value of any navigation map can also be changed at ru
|
||||
var default_map_rid: RID = get_world_2d().get_navigation_map()
|
||||
NavigationServer2D.map_set_edge_connection_margin(default_map_rid, 50.0)
|
||||
|
||||
.. code-tab:: csharp 2D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode2D : Node2D
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
// 2D margins are designed to work with 2D "pixel" values.
|
||||
Rid defaultMapRid = GetWorld2D().NavigationMap;
|
||||
NavigationServer2D.MapSetEdgeConnectionMargin(defaultMapRid, 50.0f);
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends Node3D
|
||||
@@ -62,6 +76,20 @@ The edge connection margin value of any navigation map can also be changed at ru
|
||||
var default_map_rid: RID = get_world_3d().get_navigation_map()
|
||||
NavigationServer3D.map_set_edge_connection_margin(default_map_rid, 0.5)
|
||||
|
||||
.. code-tab:: csharp 3D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode3D : Node3D
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
// 3D margins are designed to work with 3D world unit values.
|
||||
Rid defaultMapRid = GetWorld3D().NavigationMap;
|
||||
NavigationServer3D.MapSetEdgeConnectionMargin(defaultMapRid, 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
Changing the edge connection margin will trigger a full update of all navigation mesh connections on the NavigationServer.
|
||||
|
||||
@@ -24,7 +24,12 @@ In Godot debug builds the navigation debug can also be toggled through the Navig
|
||||
NavigationServer2D.set_debug_enabled(false)
|
||||
NavigationServer3D.set_debug_enabled(true)
|
||||
|
||||
Debug visualizations are currently based on Nodes in the SceneTree. If the :ref:`NavigationServer2D<class_NavigationServer2D>` or :ref:`NavigationServer3D<class_NavigationServer3D>`
|
||||
.. code-tab:: csharp
|
||||
|
||||
NavigationServer2D.SetDebugEnabled(false);
|
||||
NavigationServer3D.SetDebugEnabled(true);
|
||||
|
||||
Debug visualizations are currently based on Nodes in the SceneTree. If the :ref:`NavigationServer2D<class_NavigationServer2D>` or :ref:`NavigationServer3D<class_NavigationServer3D>`
|
||||
APIs are used exclusively then changes will not be reflected by the debug navigation tools.
|
||||
|
||||
Navigation debug settings
|
||||
|
||||
@@ -5,13 +5,13 @@ Support different actor locomotion
|
||||
|
||||
.. image:: img/nav_actor_locomotion.png
|
||||
|
||||
To support different actor locomotion like crouching and crawling, a similar
|
||||
To support different actor locomotion like crouching and crawling, a similar
|
||||
map setup as supporting :ref:`doc_navigation_different_actor_types` is required.
|
||||
|
||||
Bake different navigation meshes with an appropriate height for crouched
|
||||
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.
|
||||
|
||||
When an actor changes locomotion state, e.g. stands up, starts
|
||||
When an actor changes locomotion state, e.g. stands up, starts
|
||||
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
|
||||
@@ -19,18 +19,18 @@ other agents in the same locomotion state, switch the actor's avoidance agent to
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
|
||||
func update_path():
|
||||
|
||||
|
||||
if actor_standing:
|
||||
path = NavigationServer3D.map_get_path(standing_navigation_map_rid, start_position, target_position, true)
|
||||
elif actor_crouching:
|
||||
path = NavigationServer3D.map_get_path(crouched_navigation_map_rid, start_position, target_position, true)
|
||||
elif actor_crawling:
|
||||
path = NavigationServer3D.map_get_path(crawling_navigation_map_rid, start_position, target_position, true)
|
||||
|
||||
|
||||
func change_agent_avoidance_state():
|
||||
|
||||
|
||||
if actor_standing:
|
||||
NavigationServer3D.agent_set_map(avoidance_agent_rid, standing_navigation_map_rid)
|
||||
elif actor_crouching:
|
||||
@@ -38,6 +38,40 @@ other agents in the same locomotion state, switch the actor's avoidance agent to
|
||||
elif actor_crawling:
|
||||
NavigationServer3D.agent_set_map(avoidance_agent_rid, crawling_navigation_map_rid)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private void UpdatePath()
|
||||
{
|
||||
if (_actorStanding)
|
||||
{
|
||||
_path = NavigationServer3D.MapGetPath(_standingNavigationMapRid, _startPosition, _targetPosition, true);
|
||||
}
|
||||
else if (_actorCrouching)
|
||||
{
|
||||
_path = NavigationServer3D.MapGetPath(_crouchedNavigationMapRid, _startPosition, _targetPosition, true);
|
||||
}
|
||||
else if (_actorCrawling)
|
||||
{
|
||||
_path = NavigationServer3D.MapGetPath(_crawlingNavigationMapRid, _startPosition, _targetPosition, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeAgentAvoidanceState()
|
||||
{
|
||||
if (_actorStanding)
|
||||
{
|
||||
NavigationServer3D.AgentSetMap(_avoidanceAgentRid, _standingNavigationMapRid);
|
||||
}
|
||||
else if (_actorCrouching)
|
||||
{
|
||||
NavigationServer3D.AgentSetMap(_avoidanceAgentRid, _crouchedNavigationMapRid);
|
||||
}
|
||||
else if (_actorCrawling)
|
||||
{
|
||||
NavigationServer3D.AgentSetMap(_avoidanceAgentRid, _crawlingNavigationMapRid);
|
||||
}
|
||||
}
|
||||
|
||||
.. 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.
|
||||
|
||||
@@ -79,3 +79,69 @@ 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)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Create a navigation mesh resource for each actor size.
|
||||
NavigationMesh navigationMeshStandardSize = new NavigationMesh();
|
||||
NavigationMesh navigationMeshSmallSize = new NavigationMesh();
|
||||
NavigationMesh navigationMeshHugeSize = new NavigationMesh();
|
||||
|
||||
// Set appropriated agent parameters.
|
||||
navigationMeshStandardSize.AgentRadius = 0.5f;
|
||||
navigationMeshStandardSize.AgentHeight = 1.8f;
|
||||
navigationMeshSmallSize.AgentRadius = 0.25f;
|
||||
navigationMeshSmallSize.AgentHeight = 0.7f;
|
||||
navigationMeshHugeSize.AgentRadius = 1.5f;
|
||||
navigationMeshHugeSize.AgentHeight = 2.5f;
|
||||
|
||||
// Get the root node to parse geometry for the baking.
|
||||
Node3D rootNode = GetNode<Node3D>("NavigationMeshBakingRootNode");
|
||||
|
||||
// Create the source geometry resource that will hold the parsed geometry data.
|
||||
NavigationMeshSourceGeometryData3D sourceGeometryData = new NavigationMeshSourceGeometryData3D();
|
||||
|
||||
// Parse the source geometry from the scene tree on the main thread.
|
||||
// The navigation mesh is only required for the parse settings so any of the three will do.
|
||||
NavigationServer3D.ParseSourceGeometryData(navigationMeshStandardSize, sourceGeometryData, rootNode);
|
||||
|
||||
// Bake the navigation geometry for each agent size from the same source geometry.
|
||||
// If required for performance this baking step could also be done on background threads.
|
||||
NavigationServer3D.BakeFromSourceGeometryData(navigationMeshStandardSize, sourceGeometryData);
|
||||
NavigationServer3D.BakeFromSourceGeometryData(navigationMeshSmallSize, sourceGeometryData);
|
||||
NavigationServer3D.BakeFromSourceGeometryData(navigationMeshHugeSize, sourceGeometryData);
|
||||
|
||||
// Create different navigation maps on the NavigationServer.
|
||||
Rid navigationMapStandard = NavigationServer3D.MapCreate();
|
||||
Rid navigationMapSmall = NavigationServer3D.MapCreate();
|
||||
Rid navigationMapHuge = NavigationServer3D.MapCreate();
|
||||
|
||||
// Set the new navigation maps as active.
|
||||
NavigationServer3D.MapSetActive(navigationMapStandard, true);
|
||||
NavigationServer3D.MapSetActive(navigationMapSmall, true);
|
||||
NavigationServer3D.MapSetActive(navigationMapHuge, true);
|
||||
|
||||
// Create a region for each map.
|
||||
Rid navigationRegionStandard = NavigationServer3D.RegionCreate();
|
||||
Rid navigationRegionSmall = NavigationServer3D.RegionCreate();
|
||||
Rid navigationRegionHuge = NavigationServer3D.RegionCreate();
|
||||
|
||||
// Add the regions to the maps.
|
||||
NavigationServer3D.RegionSetMap(navigationRegionStandard, navigationMapStandard);
|
||||
NavigationServer3D.RegionSetMap(navigationRegionSmall, navigationMapSmall);
|
||||
NavigationServer3D.RegionSetMap(navigationRegionHuge, navigationMapHuge);
|
||||
|
||||
// Set navigation mesh for each region.
|
||||
NavigationServer3D.RegionSetNavigationMesh(navigationRegionStandard, navigationMeshStandardSize);
|
||||
NavigationServer3D.RegionSetNavigationMesh(navigationRegionSmall, navigationMeshSmallSize);
|
||||
NavigationServer3D.RegionSetNavigationMesh(navigationRegionHuge, navigationMeshHugeSize);
|
||||
|
||||
// Create start and end position for the navigation path query.
|
||||
Vector3 startPos = new Vector3(0.0f, 0.0f, 0.0f);
|
||||
Vector3 endPos = new Vector3(2.0f, 0.0f, 0.0f);
|
||||
bool useCorridorFunnel = true;
|
||||
|
||||
// Query paths for each agent size.
|
||||
var pathStandardAgent = NavigationServer3D.MapGetPath(navigationMapStandard, startPos, endPos, useCorridorFunnel);
|
||||
var pathSmallAgent = NavigationServer3D.MapGetPath(navigationMapSmall, startPos, endPos, useCorridorFunnel);
|
||||
var pathHugeAgent = NavigationServer3D.MapGetPath(navigationMapHuge, startPos, endPos, useCorridorFunnel);
|
||||
|
||||
@@ -158,6 +158,29 @@ toggle avoidance on agents, create or delete avoidance callbacks or switch avoid
|
||||
# Delete avoidance callback
|
||||
NavigationServer2D.agent_set_avoidance_callback(agent, Callable())
|
||||
|
||||
.. code-tab:: csharp 2D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNavigationAgent2D : NavigationAgent2D
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
Rid agent = GetRid();
|
||||
// Enable avoidance
|
||||
NavigationServer2D.AgentSetAvoidanceEnabled(agent, true);
|
||||
// Create avoidance callback
|
||||
NavigationServer2D.AgentSetAvoidanceCallback(agent, Callable.From(AvoidanceDone));
|
||||
|
||||
// Disable avoidance
|
||||
NavigationServer2D.AgentSetAvoidanceEnabled(agent, false);
|
||||
//Delete avoidance callback
|
||||
NavigationServer2D.AgentSetAvoidanceCallback(agent, default);
|
||||
}
|
||||
|
||||
private void AvoidanceDone() { }
|
||||
}
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends NavigationAgent3D
|
||||
@@ -178,6 +201,32 @@ toggle avoidance on agents, create or delete avoidance callbacks or switch avoid
|
||||
# Switch to 2D avoidance
|
||||
NavigationServer3D.agent_set_use_3d_avoidance(agent, false)
|
||||
|
||||
.. code-tab:: csharp 3D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNavigationAgent3D : NavigationAgent3D
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
Rid agent = GetRid();
|
||||
// Enable avoidance
|
||||
NavigationServer3D.AgentSetAvoidanceEnabled(agent, true);
|
||||
// Create avoidance callback
|
||||
NavigationServer3D.AgentSetAvoidanceCallback(agent, Callable.From(AvoidanceDone));
|
||||
// Switch to 3D avoidance
|
||||
NavigationServer3D.AgentSetUse3DAvoidance(agent, true);
|
||||
|
||||
// Disable avoidance
|
||||
NavigationServer3D.AgentSetAvoidanceEnabled(agent, false);
|
||||
//Delete avoidance callback
|
||||
NavigationServer3D.AgentSetAvoidanceCallback(agent, default);
|
||||
// Switch to 2D avoidance
|
||||
NavigationServer3D.AgentSetUse3DAvoidance(agent, false);
|
||||
}
|
||||
|
||||
private void AvoidanceDone() { }
|
||||
}
|
||||
|
||||
NavigationAgent Script Templates
|
||||
--------------------------------
|
||||
@@ -283,6 +332,169 @@ The following sections provides script templates for nodes commonly used with Na
|
||||
func _on_velocity_computed(safe_velocity: Vector2):
|
||||
linear_velocity = safe_velocity
|
||||
|
||||
.. tab:: 2D C#
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: csharp Node2D
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode2D : Node2D
|
||||
{
|
||||
[Export]
|
||||
public float MovementSpeed { get; set; } = 4.0f;
|
||||
NavigationAgent2D _navigationAgent;
|
||||
private float _movementDelta;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
|
||||
_navigationAgent.VelocityComputed += OnVelocityComputed;
|
||||
}
|
||||
|
||||
private void SetMovementTarget(Vector2 movementTarget)
|
||||
{
|
||||
_navigationAgent.TargetPosition = movementTarget;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
// Do not query when the map has never synchronized and is empty.
|
||||
if (NavigationServer2D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_navigationAgent.IsNavigationFinished())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_movementDelta = MovementSpeed * (float)delta;
|
||||
Vector2 nextPathPosition = _navigationAgent.GetNextPathPosition();
|
||||
Vector2 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * _movementDelta;
|
||||
if (_navigationAgent.AvoidanceEnabled)
|
||||
{
|
||||
_navigationAgent.Velocity = newVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnVelocityComputed(newVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVelocityComputed(Vector2 safeVelocity)
|
||||
{
|
||||
GlobalPosition = GlobalPosition.MoveToward(GlobalPosition + safeVelocity, _movementDelta);
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: csharp CharacterBody2D
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyCharacterBody2D : CharacterBody2D
|
||||
{
|
||||
[Export]
|
||||
public float MovementSpeed { get; set; } = 4.0f;
|
||||
NavigationAgent2D _navigationAgent;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
|
||||
_navigationAgent.VelocityComputed += OnVelocityComputed;
|
||||
}
|
||||
|
||||
private void SetMovementTarget(Vector2 movementTarget)
|
||||
{
|
||||
_navigationAgent.TargetPosition = movementTarget;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
// Do not query when the map has never synchronized and is empty.
|
||||
if (NavigationServer2D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_navigationAgent.IsNavigationFinished())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 nextPathPosition = _navigationAgent.GetNextPathPosition();
|
||||
Vector2 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * MovementSpeed;
|
||||
if (_navigationAgent.AvoidanceEnabled)
|
||||
{
|
||||
_navigationAgent.Velocity = newVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnVelocityComputed(newVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVelocityComputed(Vector2 safeVelocity)
|
||||
{
|
||||
Velocity = safeVelocity;
|
||||
MoveAndSlide();
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: csharp RigidBody2D
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyRigidBody2D : RigidBody2D
|
||||
{
|
||||
[Export]
|
||||
public float MovementSpeed { get; set; } = 4.0f;
|
||||
NavigationAgent2D _navigationAgent;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
|
||||
_navigationAgent.VelocityComputed += OnVelocityComputed;
|
||||
}
|
||||
|
||||
private void SetMovementTarget(Vector2 movementTarget)
|
||||
{
|
||||
_navigationAgent.TargetPosition = movementTarget;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
// Do not query when the map has never synchronized and is empty.
|
||||
if (NavigationServer2D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_navigationAgent.IsNavigationFinished())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 nextPathPosition = _navigationAgent.GetNextPathPosition();
|
||||
Vector2 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * MovementSpeed;
|
||||
if (_navigationAgent.AvoidanceEnabled)
|
||||
{
|
||||
_navigationAgent.Velocity = newVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnVelocityComputed(newVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVelocityComputed(Vector2 safeVelocity)
|
||||
{
|
||||
LinearVelocity = safeVelocity;
|
||||
}
|
||||
}
|
||||
|
||||
.. tab:: 3D GDScript
|
||||
|
||||
.. tabs::
|
||||
@@ -379,3 +591,166 @@ The following sections provides script templates for nodes commonly used with Na
|
||||
|
||||
func _on_velocity_computed(safe_velocity: Vector3):
|
||||
linear_velocity = safe_velocity
|
||||
|
||||
.. tab:: 3D C#
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: csharp Node3D
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode3D : Node3D
|
||||
{
|
||||
[Export]
|
||||
public float MovementSpeed { get; set; } = 4.0f;
|
||||
NavigationAgent3D _navigationAgent;
|
||||
private float _movementDelta;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationAgent = GetNode<NavigationAgent3D>("NavigationAgent3D");
|
||||
_navigationAgent.VelocityComputed += OnVelocityComputed;
|
||||
}
|
||||
|
||||
private void SetMovementTarget(Vector3 movementTarget)
|
||||
{
|
||||
_navigationAgent.TargetPosition = movementTarget;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
// Do not query when the map has never synchronized and is empty.
|
||||
if (NavigationServer3D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_navigationAgent.IsNavigationFinished())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_movementDelta = MovementSpeed * (float)delta;
|
||||
Vector3 nextPathPosition = _navigationAgent.GetNextPathPosition();
|
||||
Vector3 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * _movementDelta;
|
||||
if (_navigationAgent.AvoidanceEnabled)
|
||||
{
|
||||
_navigationAgent.Velocity = newVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnVelocityComputed(newVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVelocityComputed(Vector3 safeVelocity)
|
||||
{
|
||||
GlobalPosition = GlobalPosition.MoveToward(GlobalPosition + safeVelocity, _movementDelta);
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: csharp CharacterBody3D
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyCharacterBody3D : CharacterBody3D
|
||||
{
|
||||
[Export]
|
||||
public float MovementSpeed { get; set; } = 4.0f;
|
||||
NavigationAgent3D _navigationAgent;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationAgent = GetNode<NavigationAgent3D>("NavigationAgent3D");
|
||||
_navigationAgent.VelocityComputed += OnVelocityComputed;
|
||||
}
|
||||
|
||||
private void SetMovementTarget(Vector3 movementTarget)
|
||||
{
|
||||
_navigationAgent.TargetPosition = movementTarget;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
// Do not query when the map has never synchronized and is empty.
|
||||
if (NavigationServer3D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_navigationAgent.IsNavigationFinished())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 nextPathPosition = _navigationAgent.GetNextPathPosition();
|
||||
Vector3 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * MovementSpeed;
|
||||
if (_navigationAgent.AvoidanceEnabled)
|
||||
{
|
||||
_navigationAgent.Velocity = newVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnVelocityComputed(newVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVelocityComputed(Vector3 safeVelocity)
|
||||
{
|
||||
Velocity = safeVelocity;
|
||||
MoveAndSlide();
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: csharp RigidBody3D
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyRigidBody3D : RigidBody3D
|
||||
{
|
||||
[Export]
|
||||
public float MovementSpeed { get; set; } = 4.0f;
|
||||
NavigationAgent3D _navigationAgent;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationAgent = GetNode<NavigationAgent3D>("NavigationAgent3D");
|
||||
_navigationAgent.VelocityComputed += OnVelocityComputed;
|
||||
}
|
||||
|
||||
private void SetMovementTarget(Vector3 movementTarget)
|
||||
{
|
||||
_navigationAgent.TargetPosition = movementTarget;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
// Do not query when the map has never synchronized and is empty.
|
||||
if (NavigationServer3D.MapGetIterationId(_navigationAgent.GetNavigationMap()) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_navigationAgent.IsNavigationFinished())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 nextPathPosition = _navigationAgent.GetNextPathPosition();
|
||||
Vector3 newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * MovementSpeed;
|
||||
if (_navigationAgent.AvoidanceEnabled)
|
||||
{
|
||||
_navigationAgent.Velocity = newVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnVelocityComputed(newVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVelocityComputed(Vector3 safeVelocity)
|
||||
{
|
||||
LinearVelocity = safeVelocity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,56 @@ In scripts the following helper functions can be used to work with the ``navigat
|
||||
static func disable_bitmask_inx(_bitmask: int, _index: int) -> int:
|
||||
return _bitmask & ~(1 << _index)
|
||||
|
||||
.. code-tab:: csharp 2D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode2D : Node2D
|
||||
{
|
||||
private Rid _map;
|
||||
private Vector2 _startPosition;
|
||||
private Vector2 _targetPosition;
|
||||
|
||||
private void ChangeLayers()
|
||||
{
|
||||
NavigationRegion2D region = GetNode<NavigationRegion2D>("NavigationRegion2D");
|
||||
// Enables the 4th layer for this region.
|
||||
region.NavigationLayers = EnableBitmaskInx(region.NavigationLayers, 4);
|
||||
// Disables the 1st layer for this region.
|
||||
region.NavigationLayers = DisableBitmaskInx(region.NavigationLayers, 1);
|
||||
|
||||
NavigationAgent2D agent = GetNode<NavigationAgent2D>("NavigationAgent2D");
|
||||
// Make future path queries of this agent ignore regions with the 4th layer.
|
||||
agent.NavigationLayers = DisableBitmaskInx(agent.NavigationLayers, 4);
|
||||
|
||||
uint pathQueryNavigationLayers = 0;
|
||||
pathQueryNavigationLayers = EnableBitmaskInx(pathQueryNavigationLayers, 2);
|
||||
// Get a path that only considers 2nd layer regions.
|
||||
Vector2[] path = NavigationServer2D.MapGetPath(
|
||||
_map,
|
||||
_startPosition,
|
||||
_targetPosition,
|
||||
true,
|
||||
pathQueryNavigationLayers
|
||||
);
|
||||
}
|
||||
|
||||
private static bool IsBitmaskInxEnabled(uint bitmask, int index)
|
||||
{
|
||||
return (bitmask & (1 << index)) != 0;
|
||||
}
|
||||
|
||||
private static uint EnableBitmaskInx(uint bitmask, int index)
|
||||
{
|
||||
return bitmask | (1u << index);
|
||||
}
|
||||
|
||||
private static uint DisableBitmaskInx(uint bitmask, int index)
|
||||
{
|
||||
return bitmask & ~(1u << index);
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
func change_layers():
|
||||
@@ -87,6 +137,56 @@ In scripts the following helper functions can be used to work with the ``navigat
|
||||
static func disable_bitmask_inx(_bitmask: int, _index: int) -> int:
|
||||
return _bitmask & ~(1 << _index)
|
||||
|
||||
.. code-tab:: csharp 3D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode3D : Node3D
|
||||
{
|
||||
private Rid _map;
|
||||
private Vector3 _startPosition;
|
||||
private Vector3 _targetPosition;
|
||||
|
||||
private void ChangeLayers()
|
||||
{
|
||||
NavigationRegion3D region = GetNode<NavigationRegion3D>("NavigationRegion3D");
|
||||
// Enables the 4th layer for this region.
|
||||
region.NavigationLayers = EnableBitmaskInx(region.NavigationLayers, 4);
|
||||
// Disables the 1st layer for this region.
|
||||
region.NavigationLayers = DisableBitmaskInx(region.NavigationLayers, 1);
|
||||
|
||||
NavigationAgent3D agent = GetNode<NavigationAgent3D>("NavigationAgent2D");
|
||||
// Make future path queries of this agent ignore regions with the 4th layer.
|
||||
agent.NavigationLayers = DisableBitmaskInx(agent.NavigationLayers, 4);
|
||||
|
||||
uint pathQueryNavigationLayers = 0;
|
||||
pathQueryNavigationLayers = EnableBitmaskInx(pathQueryNavigationLayers, 2);
|
||||
// Get a path that only considers 2nd layer regions.
|
||||
Vector3[] path = NavigationServer3D.MapGetPath(
|
||||
_map,
|
||||
_startPosition,
|
||||
_targetPosition,
|
||||
true,
|
||||
pathQueryNavigationLayers
|
||||
);
|
||||
}
|
||||
|
||||
private static bool IsBitmaskInxEnabled(uint bitmask, int index)
|
||||
{
|
||||
return (bitmask & (1 << index)) != 0;
|
||||
}
|
||||
|
||||
private static uint EnableBitmaskInx(uint bitmask, int index)
|
||||
{
|
||||
return bitmask | (1u << index);
|
||||
}
|
||||
|
||||
private static uint DisableBitmaskInx(uint bitmask, int index)
|
||||
{
|
||||
return bitmask & ~(1u << index);
|
||||
}
|
||||
}
|
||||
|
||||
Changing navigation layers for path queries is a performance friendly alternative to
|
||||
enabling / disabling entire navigation regions. Compared to region changes a
|
||||
navigation path query with different navigation layers does not
|
||||
|
||||
@@ -88,6 +88,42 @@ The following script uses the NavigationServer to create a new navigation link.
|
||||
NavigationServer2D.link_set_start_position(link_rid, link_start_position)
|
||||
NavigationServer2D.link_set_end_position(link_rid, link_end_position)
|
||||
|
||||
.. code-tab:: csharp 2D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode2D : Node2D
|
||||
{
|
||||
private Rid _linkRid;
|
||||
private Vector2 _linkStartPosition;
|
||||
private Vector2 _linkEndPosition;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_linkRid = NavigationServer2D.LinkCreate();
|
||||
|
||||
ulong linkOwnerId = GetInstanceId();
|
||||
float linkEnterCost = 1.0f;
|
||||
float linkTravelCost = 1.0f;
|
||||
uint linkNavigationLayers = 1;
|
||||
bool linkBidirectional = true;
|
||||
|
||||
NavigationServer2D.LinkSetOwnerId(_linkRid, linkOwnerId);
|
||||
NavigationServer2D.LinkSetEnterCost(_linkRid, linkEnterCost);
|
||||
NavigationServer2D.LinkSetTravelCost(_linkRid, linkTravelCost);
|
||||
NavigationServer2D.LinkSetNavigationLayers(_linkRid, linkNavigationLayers);
|
||||
NavigationServer2D.LinkSetBidirectional(_linkRid, linkBidirectional);
|
||||
|
||||
// Enable the link and set it to the default navigation map.
|
||||
NavigationServer2D.LinkSetEnabled(_linkRid, true);
|
||||
NavigationServer2D.LinkSetMap(_linkRid, GetWorld2D().NavigationMap);
|
||||
|
||||
// Move the 2 link positions to their intended global positions.
|
||||
NavigationServer2D.LinkSetStartPosition(_linkRid, _linkStartPosition);
|
||||
NavigationServer2D.LinkSetEndPosition(_linkRid, _linkEndPosition);
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends Node3D
|
||||
@@ -118,3 +154,39 @@ The following script uses the NavigationServer to create a new navigation link.
|
||||
# Move the 2 link positions to their intended global positions.
|
||||
NavigationServer3D.link_set_start_position(link_rid, link_start_position)
|
||||
NavigationServer3D.link_set_end_position(link_rid, link_end_position)
|
||||
|
||||
.. code-tab:: csharp 3D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode3D : Node3D
|
||||
{
|
||||
private Rid _linkRid;
|
||||
private Vector3 _linkStartPosition;
|
||||
private Vector3 _linkEndPosition;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_linkRid = NavigationServer3D.LinkCreate();
|
||||
|
||||
ulong linkOwnerId = GetInstanceId();
|
||||
float linkEnterCost = 1.0f;
|
||||
float linkTravelCost = 1.0f;
|
||||
uint linkNavigationLayers = 1;
|
||||
bool linkBidirectional = true;
|
||||
|
||||
NavigationServer3D.LinkSetOwnerId(_linkRid, linkOwnerId);
|
||||
NavigationServer3D.LinkSetEnterCost(_linkRid, linkEnterCost);
|
||||
NavigationServer3D.LinkSetTravelCost(_linkRid, linkTravelCost);
|
||||
NavigationServer3D.LinkSetNavigationLayers(_linkRid, linkNavigationLayers);
|
||||
NavigationServer3D.LinkSetBidirectional(_linkRid, linkBidirectional);
|
||||
|
||||
// Enable the link and set it to the default navigation map.
|
||||
NavigationServer3D.LinkSetEnabled(_linkRid, true);
|
||||
NavigationServer3D.LinkSetMap(_linkRid, GetWorld3D().NavigationMap);
|
||||
|
||||
// Move the 2 link positions to their intended global positions.
|
||||
NavigationServer3D.LinkSetStartPosition(_linkRid, _linkStartPosition);
|
||||
NavigationServer3D.LinkSetEndPosition(_linkRid, _linkEndPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ By itself the navigation system will never know "this is a tree / rock / wall co
|
||||
|
||||
.. _doc_navigation_navmesh_baking:
|
||||
|
||||
Navigation mesh baking can be done either by using a :ref:`NavigationRegion2D<class_NavigationRegion2D>` or :ref:`NavigationRegion3D<class_NavigationRegion3D>`, or by using the
|
||||
Navigation mesh baking can be done either by using a :ref:`NavigationRegion2D<class_NavigationRegion2D>` or :ref:`NavigationRegion3D<class_NavigationRegion3D>`, or by using the
|
||||
:ref:`NavigationServer2D<class_NavigationServer2D>` and :ref:`NavigationServer3D<class_NavigationServer3D>` API directly.
|
||||
|
||||
.. _doc_navigation_using_navigationmeshes_baking_navigation_mesh_with_navigationregion:
|
||||
@@ -84,6 +84,11 @@ The nodes are available in 2D and 3D as :ref:`NavigationRegion2D<class_Navigatio
|
||||
var on_thread: bool = true
|
||||
bake_navigation_polygon(on_thread)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
bool onThread = true;
|
||||
BakeNavigationPolygon(onThread);
|
||||
|
||||
To quickly test the 2D baking with default settings:
|
||||
|
||||
- Add a :ref:`NavigationRegion2D<class_NavigationRegion2D>`.
|
||||
@@ -137,6 +142,11 @@ The nodes are available in 2D and 3D as :ref:`NavigationRegion2D<class_Navigatio
|
||||
var on_thread: bool = true
|
||||
bake_navigation_mesh(on_thread)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
bool onThread = true;
|
||||
BakeNavigationMesh(onThread);
|
||||
|
||||
To quickly test the 3D baking with default settings:
|
||||
|
||||
- Add a :ref:`NavigationRegion3D<class_NavigationRegion3D>`.
|
||||
@@ -370,6 +380,76 @@ The following script uses the NavigationServer to parse source geometry from the
|
||||
# Update the region with the updated navigation mesh.
|
||||
NavigationServer2D.region_set_navigation_polygon(region_rid, navigation_mesh)
|
||||
|
||||
.. code-tab:: csharp 2D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode2D : Node2D
|
||||
{
|
||||
private NavigationPolygon _navigationMesh;
|
||||
private NavigationMeshSourceGeometryData2D _sourceGeometry;
|
||||
private Callable _callbackParsing;
|
||||
private Callable _callbackBaking;
|
||||
private Rid _regionRid;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationMesh = new NavigationPolygon();
|
||||
_navigationMesh.AgentRadius = 10.0f;
|
||||
_sourceGeometry = new NavigationMeshSourceGeometryData2D();
|
||||
_callbackParsing = Callable.From(OnParsingDone);
|
||||
_callbackBaking = Callable.From(OnBakingDone);
|
||||
_regionRid = NavigationServer2D.RegionCreate();
|
||||
|
||||
// Enable the region and set it to the default navigation map.
|
||||
NavigationServer2D.RegionSetEnabled(_regionRid, true);
|
||||
NavigationServer2D.RegionSetMap(_regionRid, GetWorld2D().NavigationMap);
|
||||
|
||||
// Some mega-nodes like TileMap are often not ready on the first frame.
|
||||
// Also the parsing needs to happen on the main-thread.
|
||||
// So do a deferred call to avoid common parsing issues.
|
||||
CallDeferred(MethodName.ParseSourceGeometry);
|
||||
}
|
||||
|
||||
private void ParseSourceGeometry()
|
||||
{
|
||||
_sourceGeometry.Clear();
|
||||
Node2D rootNode = this;
|
||||
|
||||
// Parse the obstruction outlines from all child nodes of the root node by default.
|
||||
NavigationServer2D.ParseSourceGeometryData(
|
||||
_navigationMesh,
|
||||
_sourceGeometry,
|
||||
rootNode,
|
||||
_callbackParsing
|
||||
);
|
||||
}
|
||||
|
||||
private void OnParsingDone()
|
||||
{
|
||||
// If we did not parse a TileMap with navigation mesh cells we may now only
|
||||
// have obstruction outlines so add at least one traversable outline
|
||||
// so the obstructions outlines have something to "cut" into.
|
||||
_sourceGeometry.AddTraversableOutline(new Vector2[]
|
||||
{
|
||||
new Vector2(0.0f, 0.0f),
|
||||
new Vector2(500.0f, 0.0f),
|
||||
new Vector2(500.0f, 500.0f),
|
||||
new Vector2(0.0f, 500.0f),
|
||||
});
|
||||
|
||||
// Bake the navigation mesh on a thread with the source geometry data.
|
||||
NavigationServer2D.BakeFromSourceGeometryDataAsync(_navigationMesh, _sourceGeometry, _callbackBaking);
|
||||
}
|
||||
|
||||
private void OnBakingDone()
|
||||
{
|
||||
// Update the region with the updated navigation mesh.
|
||||
NavigationServer2D.RegionSetNavigationPolygon(_regionRid, _navigationMesh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends Node3D
|
||||
@@ -421,6 +501,64 @@ The following script uses the NavigationServer to parse source geometry from the
|
||||
# Update the region with the updated navigation mesh.
|
||||
NavigationServer3D.region_set_navigation_mesh(region_rid, navigation_mesh)
|
||||
|
||||
.. code-tab:: csharp 3D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode3D : Node3D
|
||||
{
|
||||
private NavigationMesh _navigationMesh;
|
||||
private NavigationMeshSourceGeometryData3D _sourceGeometry;
|
||||
private Callable _callbackParsing;
|
||||
private Callable _callbackBaking;
|
||||
private Rid _regionRid;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationMesh = new NavigationMesh();
|
||||
_navigationMesh.AgentRadius = 0.5f;
|
||||
_sourceGeometry = new NavigationMeshSourceGeometryData3D();
|
||||
_callbackParsing = Callable.From(OnParsingDone);
|
||||
_callbackBaking = Callable.From(OnBakingDone);
|
||||
_regionRid = NavigationServer3D.RegionCreate();
|
||||
|
||||
// Enable the region and set it to the default navigation map.
|
||||
NavigationServer3D.RegionSetEnabled(_regionRid, true);
|
||||
NavigationServer3D.RegionSetMap(_regionRid, GetWorld3D().NavigationMap);
|
||||
|
||||
// Some mega-nodes like GridMap are often not ready on the first frame.
|
||||
// Also the parsing needs to happen on the main-thread.
|
||||
// So do a deferred call to avoid common parsing issues.
|
||||
CallDeferred(MethodName.ParseSourceGeometry);
|
||||
}
|
||||
|
||||
private void ParseSourceGeometry ()
|
||||
{
|
||||
_sourceGeometry.Clear();
|
||||
Node3D rootNode = this;
|
||||
|
||||
// Parse the geometry from all mesh child nodes of the root node by default.
|
||||
NavigationServer3D.ParseSourceGeometryData(
|
||||
_navigationMesh,
|
||||
_sourceGeometry,
|
||||
rootNode,
|
||||
_callbackParsing
|
||||
);
|
||||
}
|
||||
|
||||
private void OnParsingDone()
|
||||
{
|
||||
// Bake the navigation mesh on a thread with the source geometry data.
|
||||
NavigationServer3D.BakeFromSourceGeometryDataAsync(_navigationMesh, _sourceGeometry, _callbackBaking);
|
||||
}
|
||||
|
||||
private void OnBakingDone()
|
||||
{
|
||||
// Update the region with the updated navigation mesh.
|
||||
NavigationServer3D.RegionSetNavigationMesh(_regionRid, _navigationMesh);
|
||||
}
|
||||
}
|
||||
|
||||
The following script uses the NavigationServer to update a navigation region with procedurally generated navigation mesh data.
|
||||
|
||||
.. tabs::
|
||||
@@ -444,7 +582,7 @@ The following script uses the NavigationServer to update a navigation region wit
|
||||
Vector2(0.0, 0.0),
|
||||
Vector2(100.0, 0.0),
|
||||
Vector2(100.0, 100.0),
|
||||
Vector2(0.0, 100.0)
|
||||
Vector2(0.0, 100.0),
|
||||
])
|
||||
|
||||
# Add indices for the polygon.
|
||||
@@ -454,6 +592,41 @@ The following script uses the NavigationServer to update a navigation region wit
|
||||
|
||||
NavigationServer2D.region_set_navigation_polygon(region_rid, navigation_mesh)
|
||||
|
||||
.. code-tab:: csharp 2D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode2D : Node2D
|
||||
{
|
||||
private NavigationPolygon _navigationMesh;
|
||||
private Rid _regionRid;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationMesh = new NavigationPolygon();
|
||||
_regionRid = NavigationServer2D.RegionCreate();
|
||||
|
||||
// Enable the region and set it to the default navigation map.
|
||||
NavigationServer2D.RegionSetEnabled(_regionRid, true);
|
||||
NavigationServer2D.RegionSetMap(_regionRid, GetWorld2D().NavigationMap);
|
||||
|
||||
// Add vertices for a convex polygon.
|
||||
_navigationMesh.Vertices = new Vector2[]
|
||||
{
|
||||
new Vector2(0, 0),
|
||||
new Vector2(100.0f, 0),
|
||||
new Vector2(100.0f, 100.0f),
|
||||
new Vector2(0, 100.0f),
|
||||
};
|
||||
|
||||
// Add indices for the polygon.
|
||||
_navigationMesh.AddPolygon(new int[] { 0, 1, 2, 3 });
|
||||
|
||||
NavigationServer2D.RegionSetNavigationPolygon(_regionRid, _navigationMesh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends Node3D
|
||||
@@ -483,3 +656,38 @@ The following script uses the NavigationServer to update a navigation region wit
|
||||
)
|
||||
|
||||
NavigationServer3D.region_set_navigation_mesh(region_rid, navigation_mesh)
|
||||
|
||||
.. code-tab:: csharp 3D C#
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode3D : Node3D
|
||||
{
|
||||
private NavigationMesh _navigationMesh;
|
||||
private Rid _regionRid;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationMesh = new NavigationMesh();
|
||||
_regionRid = NavigationServer3D.RegionCreate();
|
||||
|
||||
// Enable the region and set it to the default navigation map.
|
||||
NavigationServer3D.RegionSetEnabled(_regionRid, true);
|
||||
NavigationServer3D.RegionSetMap(_regionRid, GetWorld3D().NavigationMap);
|
||||
|
||||
// Add vertices for a convex polygon.
|
||||
_navigationMesh.Vertices = new Vector3[]
|
||||
{
|
||||
new Vector3(-1.0f, 0.0f, 1.0f),
|
||||
new Vector3(1.0f, 0.0f, 1.0f),
|
||||
new Vector3(1.0f, 0.0f, -1.0f),
|
||||
new Vector3(-1.0f, 0.0f, -1.0f),
|
||||
};
|
||||
|
||||
// Add indices for the polygon.
|
||||
_navigationMesh.AddPolygon(new int[] { 0, 1, 2, 3 });
|
||||
|
||||
NavigationServer3D.RegionSetNavigationMesh(_regionRid, _navigationMesh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,26 @@ Obstacles are not involved in the source geometry parsing so adding them just be
|
||||
|
||||
NavigationServer2D.bake_from_source_geometry_data(navigation_mesh, source_geometry)
|
||||
|
||||
.. code-tab:: csharp 2D C#
|
||||
|
||||
Vector2[] obstacleOutline = new Vector2[]
|
||||
{
|
||||
new Vector2(-50, -50),
|
||||
new Vector2(50, -50),
|
||||
new Vector2(50, 50),
|
||||
new Vector2(-50, 50),
|
||||
};
|
||||
|
||||
var navigationMesh = new NavigationPolygon();
|
||||
var sourceGeometry = new NavigationMeshSourceGeometryData2D();
|
||||
|
||||
NavigationServer2D.ParseSourceGeometryData(navigationMesh, sourceGeometry, GetNode<Node2D>("MyTestRootNode"));
|
||||
|
||||
bool obstacleCarve = true;
|
||||
|
||||
sourceGeometry.AddProjectedObstruction(obstacleOutline, obstacleCarve);
|
||||
NavigationServer2D.BakeFromSourceGeometryData(navigationMesh, sourceGeometry);
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
var obstacle_outline = PackedVector3Array([
|
||||
@@ -101,6 +121,28 @@ Obstacles are not involved in the source geometry parsing so adding them just be
|
||||
|
||||
NavigationServer3D.bake_from_source_geometry_data(navigation_mesh, source_geometry)
|
||||
|
||||
.. code-tab:: csharp 3D C#
|
||||
|
||||
Vector3[] obstacleOutline = new Vector3[]
|
||||
{
|
||||
new Vector3(-5, 0, -5),
|
||||
new Vector3(5, 0, -5),
|
||||
new Vector3(5, 0, 5),
|
||||
new Vector3(-5, 0, 5),
|
||||
};
|
||||
|
||||
var navigationMesh = new NavigationMesh();
|
||||
var sourceGeometry = new NavigationMeshSourceGeometryData3D();
|
||||
|
||||
NavigationServer3D.ParseSourceGeometryData(navigationMesh, sourceGeometry, GetNode<Node3D>("MyTestRootNode"));
|
||||
|
||||
float obstacleElevation = GetNode<Node3D>("MyTestObstacleNode").GlobalPosition.Y;
|
||||
float obstacleHeight = 50.0f;
|
||||
bool obstacleCarve = true;
|
||||
|
||||
sourceGeometry.AddProjectedObstruction(obstacleOutline, obstacleElevation, obstacleHeight, obstacleCarve);
|
||||
NavigationServer3D.BakeFromSourceGeometryData(navigationMesh, sourceGeometry);
|
||||
|
||||
Obstacles and agent avoidance
|
||||
-----------------------------
|
||||
|
||||
@@ -179,6 +221,31 @@ For static use an array of ``vertices`` is required.
|
||||
# Enable the obstacle.
|
||||
NavigationServer2D.obstacle_set_avoidance_enabled(new_obstacle_rid, true)
|
||||
|
||||
.. code-tab:: csharp 2D C#
|
||||
|
||||
// Create a new "obstacle" and place it on the default navigation map.
|
||||
Rid newObstacleRid = NavigationServer2D.ObstacleCreate();
|
||||
Rid defaultMapRid = GetWorld2D().NavigationMap;
|
||||
|
||||
NavigationServer2D.ObstacleSetMap(newObstacleRid, defaultMapRid);
|
||||
NavigationServer2D.ObstacleSetPosition(newObstacleRid, GlobalPosition);
|
||||
|
||||
// Use obstacle dynamic by increasing radius above zero.
|
||||
NavigationServer2D.ObstacleSetRadius(newObstacleRid, 5.0f);
|
||||
|
||||
// Use obstacle static by adding a square that pushes agents out.
|
||||
Vector2[] outline = new Vector2[]
|
||||
{
|
||||
new Vector2(-100, -100),
|
||||
new Vector2(100, -100),
|
||||
new Vector2(100, 100),
|
||||
new Vector2(-100, 100),
|
||||
};
|
||||
NavigationServer2D.ObstacleSetVertices(newObstacleRid, outline);
|
||||
|
||||
// Enable the obstacle.
|
||||
NavigationServer2D.ObstacleSetAvoidanceEnabled(newObstacleRid, true);
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
# Create a new "obstacle" and place it on the default navigation map.
|
||||
@@ -199,3 +266,30 @@ For static use an array of ``vertices`` is required.
|
||||
|
||||
# Enable the obstacle.
|
||||
NavigationServer3D.obstacle_set_avoidance_enabled(new_obstacle_rid, true)
|
||||
|
||||
.. code-tab:: csharp 3D C#
|
||||
|
||||
// Create a new "obstacle" and place it on the default navigation map.
|
||||
Rid newObstacleRid = NavigationServer3D.ObstacleCreate();
|
||||
Rid defaultMapRid = GetWorld3D().NavigationMap;
|
||||
|
||||
NavigationServer3D.ObstacleSetMap(newObstacleRid, defaultMapRid);
|
||||
NavigationServer3D.ObstacleSetPosition(newObstacleRid, GlobalPosition);
|
||||
|
||||
// Use obstacle dynamic by increasing radius above zero.
|
||||
NavigationServer3D.ObstacleSetRadius(newObstacleRid, 5.0f);
|
||||
|
||||
// Use obstacle static by adding a square that pushes agents out.
|
||||
Vector3[] outline = new Vector3[]
|
||||
{
|
||||
new Vector3(-5, 0, -5),
|
||||
new Vector3(5, 0, -5),
|
||||
new Vector3(5, 0, 5),
|
||||
new Vector3(-5, 0, 5),
|
||||
};
|
||||
NavigationServer3D.ObstacleSetVertices(newObstacleRid, outline);
|
||||
// Set the obstacle height on the y-axis.
|
||||
NavigationServer3D.ObstacleSetHeight(newObstacleRid, 1.0f);
|
||||
|
||||
// Enable the obstacle.
|
||||
NavigationServer3D.ObstacleSetAvoidanceEnabled(newObstacleRid, true);
|
||||
|
||||
@@ -52,6 +52,33 @@ Outside of grids due to polygons often covering large open areas with a single,
|
||||
)
|
||||
return path
|
||||
|
||||
.. code-tab:: csharp 2D C#
|
||||
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public partial class MyNode2D : Node2D
|
||||
{
|
||||
// Basic query for a navigation path using the default navigation map.
|
||||
|
||||
private Vector2[] GetNavigationPath(Vector2 startPosition, Vector2 targetPosition)
|
||||
{
|
||||
if (!IsInsideTree())
|
||||
{
|
||||
return Array.Empty<Vector2>();
|
||||
}
|
||||
|
||||
Rid defaultMapRid = GetWorld2D().NavigationMap;
|
||||
Vector2[] path = NavigationServer2D.MapGetPath(
|
||||
defaultMapRid,
|
||||
startPosition,
|
||||
targetPosition,
|
||||
true
|
||||
);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: gdscript 3D GDScript
|
||||
|
||||
extends Node3D
|
||||
@@ -71,6 +98,33 @@ Outside of grids due to polygons often covering large open areas with a single,
|
||||
)
|
||||
return path
|
||||
|
||||
.. code-tab:: csharp 3D C#
|
||||
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public partial class MyNode3D : Node3D
|
||||
{
|
||||
// Basic query for a navigation path using the default navigation map.
|
||||
|
||||
private Vector3[] GetNavigationPath(Vector3 startPosition, Vector3 targetPosition)
|
||||
{
|
||||
if (!IsInsideTree())
|
||||
{
|
||||
return Array.Empty<Vector3>();
|
||||
}
|
||||
|
||||
Rid defaultMapRid = GetWorld3D().NavigationMap;
|
||||
Vector3[] path = NavigationServer3D.MapGetPath(
|
||||
defaultMapRid,
|
||||
startPosition,
|
||||
targetPosition,
|
||||
true
|
||||
);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -134,3 +188,67 @@ the default navigation map by setting the target position with ``set_movement_ta
|
||||
var new_velocity: Vector3 = global_transform.origin.direction_to(current_path_point) * movement_delta
|
||||
|
||||
global_transform.origin = global_transform.origin.move_toward(global_transform.origin + new_velocity, movement_delta)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode3D : Node3D
|
||||
{
|
||||
private Rid _default3DMapRid;
|
||||
|
||||
private float _movementSpeed = 4.0f;
|
||||
private float _movementDelta;
|
||||
private float _pathPointMargin = 0.5f;
|
||||
|
||||
private int _currentPathIndex = 0;
|
||||
private Vector3 _currentPathPoint;
|
||||
private Vector3[] _currentPath;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_default3DMapRid = GetWorld3D().NavigationMap;
|
||||
}
|
||||
|
||||
private void SetMovementTarget(Vector3 targetPosition)
|
||||
{
|
||||
Vector3 startPosition = GlobalTransform.Origin;
|
||||
|
||||
_currentPath = NavigationServer3D.MapGetPath(_default3DMapRid, startPosition, targetPosition, true);
|
||||
|
||||
if (!_currentPath.IsEmpty())
|
||||
{
|
||||
_currentPathIndex = 0;
|
||||
_currentPathPoint = _currentPath[0];
|
||||
}
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
if (_currentPath.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_movementDelta = _movementSpeed * (float)delta;
|
||||
|
||||
if (GlobalTransform.Origin.DistanceTo(_currentPathPoint) <= _pathPointMargin)
|
||||
{
|
||||
_currentPathIndex += 1;
|
||||
if (_currentPathIndex >= _currentPath.Length)
|
||||
{
|
||||
_currentPath = Array.Empty<Vector3>();
|
||||
_currentPathIndex = 0;
|
||||
_currentPathPoint = GlobalTransform.Origin;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_currentPathPoint = _currentPath[_currentPathIndex];
|
||||
|
||||
Vector3 newVelocity = GlobalTransform.Origin.DirectionTo(_currentPathPoint) * _movementDelta;
|
||||
var globalTransform = GlobalTransform;
|
||||
globalTransform.Origin = globalTransform.Origin.MoveToward(globalTransform.Origin + newVelocity, _movementDelta);
|
||||
GlobalTransform = globalTransform;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,22 +25,38 @@ Scene tree
|
||||
|
||||
Interacting with the active scene tree is **NOT** thread-safe. Make sure to use mutexes when sending data between threads. If you want to call functions from a thread, the *call_deferred* function may be used:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
# Unsafe:
|
||||
node.add_child(child_node)
|
||||
# Safe:
|
||||
node.add_child.call_deferred(child_node)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Unsafe:
|
||||
node.AddChild(childNode);
|
||||
// Safe:
|
||||
node.CallDeferred(Node.MethodName.AddChild, childNode);
|
||||
|
||||
However, creating scene chunks (nodes in tree arrangement) outside the active tree is fine. This way, parts of a scene can be built or instantiated in a thread, then added in the main thread:
|
||||
|
||||
::
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript
|
||||
|
||||
var enemy_scene = load("res://enemy_scene.scn")
|
||||
var enemy = enemy_scene.instantiate()
|
||||
enemy.add_child(weapon) # Set a weapon.
|
||||
world.add_child.call_deferred(enemy)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
PackedScene enemyScene = GD.Load<PackedScene>("res://EnemyScene.scn");
|
||||
Node enemy = enemyScene.Instantiate<Node>();
|
||||
enemy.AddChild(weapon);
|
||||
world.CallDeferred(Node.MethodName.AddChild, enemy);
|
||||
|
||||
Still, this is only really useful if you have **one** thread loading data.
|
||||
Attempting to load or create scene chunks from multiple threads may work, but you risk
|
||||
resources (which are only loaded once in Godot) tweaked by the multiple
|
||||
@@ -61,7 +77,7 @@ Note that the Multi-Threaded thread model has several known bugs, so it may not
|
||||
in all scenarios.
|
||||
|
||||
You should avoid calling functions involving direct interaction with the GPU on other threads, such as creating new textures
|
||||
or modifying and retrieving image data, these operations can lead to performance stalls because they require synchronization
|
||||
or modifying and retrieving image data, these operations can lead to performance stalls because they require synchronization
|
||||
with the :ref:`RenderingServer<class_RenderingServer>`, as data needs to be transmitted to or updated on the GPU.
|
||||
|
||||
GDScript arrays, dictionaries
|
||||
|
||||
@@ -252,6 +252,41 @@ and moves a :ref:`CanvasItem <class_CanvasItem>` when the body moves.
|
||||
# if you have many bodies and a single callback.
|
||||
Physics2DServer.body_set_force_integration_callback(body, self, "_body_moved", 0)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
using Godot;
|
||||
|
||||
public partial class MyNode2D : Node2D
|
||||
{
|
||||
private Rid _canvasItem;
|
||||
|
||||
private void BodyMoved(PhysicsDirectBodyState2D state, int index)
|
||||
{
|
||||
RenderingServer.CanvasItemSetTransform(_canvasItem, state.Transform);
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
// Create the body.
|
||||
var body = PhysicsServer2D.BodyCreate();
|
||||
PhysicsServer2D.BodySetMode(body, PhysicsServer2D.BodyMode.Rigid);
|
||||
// Add a shape.
|
||||
var shape = PhysicsServer2D.RectangleShapeCreate();
|
||||
// Set rectangle extents.
|
||||
PhysicsServer2D.ShapeSetData(shape, new Vector2(10, 10));
|
||||
// Make sure to keep the shape reference!
|
||||
PhysicsServer2D.BodyAddShape(body, shape);
|
||||
// Set space, so it collides in the same space as current scene.
|
||||
PhysicsServer2D.BodySetSpace(body, GetWorld2D().Space);
|
||||
// Move initial position.
|
||||
PhysicsServer2D.BodySetState(body, PhysicsServer2D.BodyState.Transform, new Transform2D(0, new Vector2(10, 20)));
|
||||
// Add the transform callback, when body moves
|
||||
// The last parameter is optional, can be used as index
|
||||
// if you have many bodies and a single callback.
|
||||
PhysicsServer2D.BodySetForceIntegrationCallback(body, new Callable(this, MethodName.BodyMoved), 0);
|
||||
}
|
||||
}
|
||||
|
||||
The 3D version should be very similar, as 2D and 3D physics servers are identical (using
|
||||
:ref:`RigidBody3D <class_RigidBody3D>` and :ref:`PhysicsServer3D <class_PhysicsServer3D>` respectively).
|
||||
|
||||
|
||||
@@ -68,6 +68,13 @@ The ragdoll is now ready to use. To start the simulation and play the ragdoll an
|
||||
func _ready():
|
||||
physical_bones_start_simulation()
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
PhysicalBonesStartSimulation();
|
||||
}
|
||||
|
||||
To stop the simulation, call the ``physical_bones_stop_simulation()`` method.
|
||||
|
||||
.. image:: img/ragdoll_sim_stop.gif
|
||||
|
||||
@@ -171,6 +171,24 @@ with Area3D, the boolean parameter ``collide_with_areas`` must be set to ``true`
|
||||
|
||||
var result = space_state.intersect_ray(query)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private const int RayLength = 1000;
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
var spaceState = GetWorld3D().DirectSpaceState;
|
||||
var cam = GetNode<Camera3D>("Camera3D");
|
||||
var mousePos = GetViewport().GetMousePosition();
|
||||
|
||||
var origin = cam.ProjectRayOrigin(mousePos);
|
||||
var end = origin + cam.ProjectRayNormal(mousePos) * RayLength;
|
||||
var query = PhysicsRayQueryParameters3D.Create(origin, end);
|
||||
query.CollideWithAreas = true;
|
||||
|
||||
var result = spaceState.IntersectRay(query);
|
||||
}
|
||||
|
||||
Collision exceptions
|
||||
--------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user