Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Використання NavigationServer
2D і 3D версії NavigationServer доступні як NavigationServer2D і NavigationServer3D відповідно.
Зв'язок із навігаційним сервером
Працювати з NavigationServer означає підготувати параметри для запиту, який можна надіслати на NavigationServer для оновлення або запиту даних.
Для посилань на внутрішні об’єкти NavigationServer, такі як карти, регіони та агенти, RID використовуються як ідентифікаційні номери. Кожен пов’язаний з навігацією вузол у дереві сцени має функцію, яка повертає RID для цього вузла.
Потоковість і синхронізація
NavigationServer не оновлює кожну зміну негайно, а чекає до кінця фізичного кадру, щоб синхронізувати всі зміни разом.
Очікування синхронізації потрібне для застосування змін до всіх карт, регіонів і агентів. Синхронізація виконується, оскільки деякі оновлення, як-от перерахунок усієї навігаційної карти, є дуже дорогими та вимагають оновлення даних з усіх інших об’єктів. Крім того, NavigationServer використовує threadpool за замовчуванням для деяких функцій, як-от розрахунок уникнення між агентами.
Очікування не потрібне для більшості функцій get(), які лише запитують дані від NavigationServer без внесення змін. Зауважте, що не всі дані враховуватимуть зміни, внесені в одному фреймі. наприклад якщо агент уникнення змінив навігаційну карту в цьому кадрі, функція agent_get_map() все одно повертатиме стару карту до синхронізації. Винятком є вузли, які зберігають свої значення всередині перед надсиланням оновлення на NavigationServer. Коли геттер на вузлі використовується для значення, яке було оновлено в тому самому кадрі, він поверне вже оновлене значення, що зберігається на вузлі.
NavigationServer потоково-безпечний, оскільки розміщує всі виклики API, які хочуть внести зміни, у чергу для виконання на етапі синхронізації. Синхронізація для NavigationServer відбувається в середині кадру фізики після завершення введення сцени зі сценаріїв і вузлів.
Примітка
Важливим висновком є те, що більшість змін NavigationServer набувають чинності після наступного кадру фізики, а не відразу. Це включає всі зміни, внесені пов’язаними з навігацією вузлами в дереві сцени або за допомогою сценаріїв.
Примітка
Усі функції встановлення та видалення потребують синхронізації.
Відмінності 2D і 3D NavigationServer
NavigationServer2D та NavigationServer3D еквівалентні за функціональністю для своїх розмірів.
Технічно можливо використовувати інструменти для створення навігаційних сіток в одному вимірі для іншого виміру, напр. запікання 2D навігаційної сітки за допомогою 3D NavigationMesh при використанні плоскої 3D вихідної геометрії або створення 3D плоских навігаційних сіток за допомогою інструментів малювання контуру багатокутника NavigationRegion2D і NavigationPolygons.
Очікування синхронізації
На початку гри нова сцена або процедурна навігація змінює будь-який запит шляху до NavigationServer, повертаючи пустий або неправильний.
На даний момент навігаційна карта все ще порожня або не оновлена. Усі вузли з дерева сцени повинні спочатку завантажити свої навігаційні дані на NavigationServer. Кожну додану або змінену карту, регіон або агента необхідно зареєструвати на NavigationServer. Після цього навігаційному серверу потрібен фізичний фрейм для синхронізації для оновлення карт, регіонів і агентів.
Один обхідний шлях полягає в тому, щоб зробити відкладений виклик спеціальної функції налаштування (щоб усі вузли були готові). Функція налаштування вносить усі зміни навігації, напр. додавання процедурних речей. Після цього функція чекає наступного фізичного кадру, перш ніж продовжити запити шляху.
extends Node3D
func _ready():
# Use call deferred to make sure the entire scene tree nodes are setup
# else await on 'physics_frame' in a _ready() might get stuck.
custom_setup.call_deferred()
func custom_setup():
# Create a new navigation map.
var map: RID = NavigationServer3D.map_create()
NavigationServer3D.map_set_up(map, Vector3.UP)
NavigationServer3D.map_set_active(map, true)
# Create a new navigation region and add it to the map.
var region: RID = NavigationServer3D.region_create()
NavigationServer3D.region_set_transform(region, Transform3D())
NavigationServer3D.region_set_map(region, map)
# Create a procedural navigation mesh for the region.
var new_navigation_mesh: NavigationMesh = NavigationMesh.new()
var vertices: PackedVector3Array = PackedVector3Array([
Vector3(0, 0, 0),
Vector3(9.0, 0, 0),
Vector3(0, 0, 9.0)
])
new_navigation_mesh.set_vertices(vertices)
var polygon: PackedInt32Array = PackedInt32Array([0, 1, 2])
new_navigation_mesh.add_polygon(polygon)
NavigationServer3D.region_set_navigation_mesh(region, new_navigation_mesh)
# Wait for NavigationServer sync to adapt to made changes.
await get_tree().physics_frame
# Query the path from the navigation server.
var start_position: Vector3 = Vector3(0.1, 0.0, 0.1)
var target_position: Vector3 = Vector3(1.0, 0.0, 1.0)
var optimize_path: bool = true
var path: PackedVector3Array = NavigationServer3D.map_get_path(
map,
start_position,
target_position,
optimize_path
)
print("Found a path!")
print(path)
using Godot;
public partial class MyNode3D : Node3D
{
public override void _Ready()
{
// Use call deferred to make sure the entire scene tree nodes are setup
// else await on 'physics_frame' in a _Ready() might get stuck.
CallDeferred(MethodName.CustomSetup);
}
private async void CustomSetup()
{
// Create a new navigation map.
Rid map = NavigationServer3D.MapCreate();
NavigationServer3D.MapSetUp(map, Vector3.Up);
NavigationServer3D.MapSetActive(map, true);
// Create a new navigation region and add it to the map.
Rid region = NavigationServer3D.RegionCreate();
NavigationServer3D.RegionSetTransform(region, Transform3D.Identity);
NavigationServer3D.RegionSetMap(region, map);
// Create a procedural navigation mesh for the region.
var newNavigationMesh = new NavigationMesh()
{
Vertices =
[
new Vector3(0.0f, 0.0f, 0.0f),
new Vector3(9.0f, 0.0f, 0.0f),
new Vector3(0.0f, 0.0f, 9.0f),
],
};
int[] polygon = [0, 1, 2];
newNavigationMesh.AddPolygon(polygon);
NavigationServer3D.RegionSetNavigationMesh(region, newNavigationMesh);
// Wait for NavigationServer sync to adapt to made changes.
await ToSignal(GetTree(), SceneTree.SignalName.PhysicsFrame);
// Query the path from the navigation server.
var startPosition = new Vector3(0.1f, 0.0f, 0.1f);
var targetPosition = new Vector3(1.0f, 0.0f, 1.0f);
Vector3[] path = NavigationServer3D.MapGetPath(map, startPosition, targetPosition, optimize: true);
GD.Print("Found a path!");
GD.Print((Variant)path);
}
}
Зворотні виклики для уникнення сервера
Якщо агенти уникнення RVO зареєстровані для зворотних викликів уникнення, NavigationServer надсилає їхні сигнали velocity_computed безпосередньо перед синхронізацією PhysicsServer.
Щоб дізнатися більше про NavigationAgents, перегляньте Використання NavigationAgents.
Спрощений порядок виконання для NavigationAgents, які використовують уникнення:
Починається фізичний кадр.
_physics_process(delta).Властивість
velocityвстановлено на вузлі NavigationAgent.Агент надсилає швидкість і положення до NavigationServer.
NavigationServer очікує на синхронізацію.
NavigationServer синхронізує та обчислює швидкість уникнення для всіх зареєстрованих агентів уникнення.
NavigationServer надсилає вектор безпечної швидкості з сигналами для кожного зареєстрованого агента уникнення.
Агенти отримують сигнал і переміщують свого батька, напр. з
move_and_slideабоlinear_velocity.PhysicsServer синхронізується.
закінчення фізики.
Тому переміщення актора фізичного тіла у функції зворотного виклику з безпечною швидкістю є абсолютно безпечним для потоків і фізики, оскільки все відбувається в одному фізичному кадрі до того, як PhysicsServer внесе зміни та зробить власні обчислення.