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...
Використання NavigationAgents
NavigationsAgents — це допоміжні вузли, які поєднують функціональні можливості для пошуку шляху, відстеження шляху та уникнення агента для Node2D/3D-спадкового батьківського вузла. Вони полегшують звичайні виклики API NavigationServer від імені батьківського вузла актора більш зручним способом для початківців.
2D і 3D версії NavigationAgents доступні як NavigationAgent2D і NavigationAgent3D відповідно.
Нові вузли NavigationAgent автоматично приєднаються до навігаційної карти за замовчуванням на World2D/World3D.
Вузли NavigationsAgent є необов’язковими та не є жорсткою вимогою для використання системи навігації. Усю їхню функціональність можна замінити сценаріями та прямими викликами API NavigationServer.
Порада
Для більш просунутих застосувань розгляньте Використання NavigationPath QueryObjects замість вузлів NavigationAgent.
Пошук шляхів навігаційного агента
NavigationAgents запитують новий навігаційний шлях на їхній поточній навігаційній карті, коли для їх target_position встановлено глобальне положення.
На результат пошуку шляху можна впливати за допомогою наступних властивостей.
Бітову маску
navigation_layersможна використовувати для обмеження навігаційних сіток, які може використовувати агент.pathfinding_algorithmконтролює те, як пошук шляху проходить багатокутниками навігаційної сітки під час пошуку шляху.path_postprocessingвстановлює, чи змінюється необроблений коридор шляху, знайдений пошуком шляху, перед його поверненням.path_metadata_flagsдозволяють збирати додаткові метадані точки шляху, які повертає шлях.Властивості
simplify_pathіsimplify_epsilonможна використовувати для видалення менш критичних точок із шляху.
Попередження
Вимкнення мета-прапорців шляху призведе до вимкнення пов’язаних сигналів агента.
Навігаційний агент Pathfollowing
Після встановлення target_position для агента, наступну позицію на шляху можна отримати за допомогою функції get_next_path_position().
Після отримання наступної позиції на шляху перемістіть батьківський вузол актора агента до цієї позиції на шляху за допомогою власного коду переміщення.
Примітка
Навігаційна система ніколи не переміщує батьківський вузол NavigationAgent. Рух повністю в руках користувачів та їхніх власних сценаріїв.
NavigationAgents мають власну внутрішню логіку для продовження поточного шляху та виклику оновлень.
Функція get_next_path_position() відповідає за оновлення багатьох внутрішніх станів і властивостей агента. Функцію слід повторно викликати один раз кожен physics_process, поки is_navigation_finished() не повідомить, що шлях завершено. Функцію не слід викликати після досягнення цільової позиції або кінця шляху, оскільки це може викликати тремтіння агента на місці через повторне оновлення шляху. Завжди перевіряйте на початку сценарію за допомогою is_navigation_finished(), якщо шлях уже завершено.
Наступні властивості відстані впливають на поведінку проходження шляху.
На
path_desired_distanceвід позиції наступного шляху агент переміщує свій внутрішній індекс шляху до наступної позиції шляху.На
target_desired_distanceвід позиції цільового шляху агент вважає цільову позицію досягнутою, а шлях — у кінці.На
path_max_distanceвід ідеального шляху до наступної позиції шляху агент запитує новий шлях, оскільки він був відсунутий занадто далеко.
Усі важливі оновлення запускаються за допомогою функції get_next_path_position() під час виклику _physics_process().
NavigationAgents можна використовувати з process, але вони все ще обмежені одним оновленням, яке відбувається в physics_process.
Приклади сценаріїв для різних вузлів, які зазвичай використовуються з NavigationAgents, можна знайти нижче.
Шляхи вирішення поширених проблем
Під час написання сценаріїв переміщення агента слід враховувати деякі поширені проблеми користувачів і важливі застереження.
- Шлях повертається порожнім
Якщо агент запитує шлях до синхронізації навігаційної карти, напр. у функції
_ready()шлях може повертатися порожнім. У цьому випадку функціяget_next_path_position()поверне ту саму позицію, що й батьківський вузол агента, і агент вважатиме кінець шляху досягнутим. Це виправляється шляхом здійснення відкладеного виклику або використання зворотного виклику, наприклад. чекаю зміни сигналу навігаційної карти.
- Агент застряг, танцюючи між двома позиціями
Це зазвичай спричинено дуже частими оновленнями шляху кожного окремого кадру, навмисно чи випадково (наприклад, максимальна відстань шляху встановлена занадто мало). Пошук шляху повинен знайти найближче положення, яке є дійсним у сітці навігації. Якщо новий шлях запитується в кожному окремому кадрі, перші позиції шляху можуть постійно перемикатися попереду та позаду поточної позиції агента, змушуючи його танцювати між двома позиціями.
- Агент іноді повертається назад
Якщо агент рухається дуже швидко, він може перевершити перевірку path_desired_distance без жодного просування індексу шляху. Це може призвести до повернення агента до точки шляху, яка зараз знаходиться позаду нього, доки він не пройде перевірку відстані для збільшення індексу шляху. Збільшення бажаних відстаней відповідно до швидкості вашого агента та частоти оновлення зазвичай виправляє це, а також більш збалансований макет багатокутника навігаційної сітки з невеликою кількістю країв полігону, стиснутих разом у невеликих просторах.
- Агент іноді дивиться назад у пошуках кадру
Так само, як із застряглими танцюючими агентами між двома позиціями, це зазвичай спричинено дуже частими оновленнями шляху кожного окремого кадру. Залежно від макета навігаційної сітки, особливо коли агент розміщено безпосередньо над краєм навігаційної сітки або з’єднанням країв, очікуйте, що позиції шляху іноді будуть трохи «позаду» поточної орієнтації ваших акторів. Це відбувається через проблеми з точністю, і цього не завжди можна уникнути. Зазвичай це лише видима проблема, якщо актори миттєво повертаються обличчям до поточної позиції шляху.
Уникнення NavigationAgent
У цьому розділі пояснюється, як використовувати уникнення навігації, специфічне для NavigationAgents.
Щоб NavigationAgents могли використовувати функцію уникнення, властивість avoidance_enabled повинна бути встановлена на true.
Сигнал velocity_computed вузла NavigationAgent має бути підключений, щоб отримати результат розрахунку безпечної швидкості.
Встановіть velocity вузла NavigationAgent у _physics_process(), щоб оновити агент до поточної швидкості батьківського вузла агента.
Якщо в агенті ввімкнено уникнення, вектор safe_velocity отримуватиметься разом із сигналом velocity_computed кожного кадру фізики. Цей вектор швидкості слід використовувати для переміщення батьківського вузла NavigationAgent, щоб уникнути зіткнення з іншими агентами або перешкодами для уникнення.
Примітка
Лише інші агенти на тій же карті, які самі зареєстровані для уникнення, враховуватимуться під час розрахунку уникнення.
Примітка
The NavigationAgent must be supplied with a target_position attribute,
even if you are only using the agent for avoidance. Otherwise, the safe_velocity
received from the velocity_computed signal will always be the zero vector.
Наступні властивості NavigationAgent є актуальними для уникнення:
Властивість
висотадоступна лише в 3D. Висота разом із поточним глобальним положенням агента по осі y визначає вертикальне розташування агента в симуляції уникнення. Агенти, які використовують 2D-уникнення, автоматично ігноруватимуть інших агентів або перешкоди, які знаходяться під або над ними.Властивість
radiusконтролює розмір кола уникнення або, у випадку тривимірної сфери, навколо агента. Ця область описує тіло агента, а не дистанцію маневру для уникнення.Властивість
neighbor_distanceконтролює радіус пошуку агента під час пошуку інших агентів, яких слід уникати. Менше значення зменшує вартість обробки.Властивість
max_neighborsконтролює кількість інших агентів, які враховуються в обчисленні уникнення, якщо всі вони мають радіус перекриття. Нижче значення зменшує вартість обробки, але занадто низьке значення може призвести до того, що агенти ігноруватимуть уникнення.Властивості
time_horizon_agentsіtime_horizon_obstaclesконтролюють час передбачення уникнення для інших агентів або перешкод у секундах. Коли агенти обчислюють свої безпечні швидкості, вони вибирають швидкості, які можуть зберігатися протягом цієї кількості секунд без зіткнення з іншим об’єктом уникнення. Час передбачення має бути якомога меншим, оскільки агенти сповільнюватимуть свої швидкості, щоб уникнути зіткнення за цей час.Властивість
max_speedконтролює максимальну швидкість, дозволену для розрахунку уникнення агентів. Якщо батьки-агенти рухаються швидше, ніж це значення, уникненняsafe_velocityможе бути недостатньо точним, щоб уникнути зіткнення.Властивість
use_3d_avoidanceперемикає агента між 2D уникненням (вісь xz) та 3D уникненням (вісь xyz) під час наступного оновлення. Зверніть увагу, що 2D уникнення та 3D уникнення виконуються в окремих симуляціях уникнення, тому агенти, розділені між ними, не впливають один на одного.Властивості
avoidance_layersіavoidance_maskє бітовими масками, подібними до, наприклад, шари фізики. Агенти уникатимуть лише інших об’єктів уникнення, які знаходяться на шарі уникнення, який відповідає принаймні одному з їхніх власних бітів маски уникнення.
avoidance_priorityзмушує агентів з вищим пріоритетом ігнорувати агентів з нижчим пріоритетом. Це можна використовувати, щоб надати певним агентам більшої важливості в симуляції уникнення, наприклад. важливі неігрові персонажі, без постійної зміни їхніх шарів уникнення або маски.
Уникнення існує у власному просторі й не має інформації від навігаційних сіток або фізичних зіткнень. Агенти уникнення за кадром — це просто кола з різним радіусом на плоскій 2D-площині або сфери в порожньому 3D-просторі. NavigationObstacles можна використовувати для додавання деяких обмежень середовища до симуляції уникнення, див. Використання навігаційних перешкод.
Примітка
Уникнення не впливає на пошук шляху. Його слід розглядати як додаткову опцію для постійно рухомих об’єктів, які не можна (повторно) запекти до навігаційної сітки, щоб ефективно рухатися навколо них.
Примітка
Уникнення RVO робить неявні припущення щодо поведінки природного агента. наприклад що агенти рухаються розумними обхідними сторонами, які можуть бути призначені, коли вони стикаються один з одним. Це означає, що дуже клінічні сценарії тестування на уникнення зазвичай не вдаються. наприклад агенти, які рухаються прямо один проти одного з ідеально протилежними швидкостями, зазнають невдачі, тому що агенти не можуть отримати призначення своїх сторін повз.
Використання властивості avoidance_enabled NavigationAgent є кращим варіантом для перемикання уникнення. Наведені нижче фрагменти коду можна використовувати для перемикання уникнення для агентів, створення або видалення зворотних викликів уникнення або перемикання режимів уникнення.
extends NavigationAgent2D
func _ready() -> void:
var agent: RID = get_rid()
# Enable avoidance
NavigationServer2D.agent_set_avoidance_enabled(agent, true)
# Create avoidance callback
NavigationServer2D.agent_set_avoidance_callback(agent, Callable(self, "_avoidance_done"))
# Disable avoidance
NavigationServer2D.agent_set_avoidance_enabled(agent, false)
# Delete avoidance callback
NavigationServer2D.agent_set_avoidance_callback(agent, Callable())
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() { }
}
extends NavigationAgent3D
func _ready() -> void:
var agent: RID = get_rid()
# Enable avoidance
NavigationServer3D.agent_set_avoidance_enabled(agent, true)
# Create avoidance callback
NavigationServer3D.agent_set_avoidance_callback(agent, Callable(self, "_avoidance_done"))
# Switch to 3D avoidance
NavigationServer3D.agent_set_use_3d_avoidance(agent, true)
# Disable avoidance
NavigationServer3D.agent_set_avoidance_enabled(agent, false)
# Delete avoidance callback
NavigationServer3D.agent_set_avoidance_callback(agent, Callable())
# Switch to 2D avoidance
NavigationServer3D.agent_set_use_3d_avoidance(agent, false)
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
У наступних розділах наведено шаблони сценаріїв для вузлів, які зазвичай використовуються з NavigationAgents.
extends Node2D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent2D = get_node("NavigationAgent2D")
var movement_delta: float
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector2):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
# Do not query when the map has never synchronized and is empty.
if NavigationServer2D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
if navigation_agent.is_navigation_finished():
return
movement_delta = movement_speed * delta
var next_path_position: Vector2 = navigation_agent.get_next_path_position()
var new_velocity: Vector2 = global_position.direction_to(next_path_position) * movement_delta
if navigation_agent.avoidance_enabled:
navigation_agent.set_velocity(new_velocity)
else:
_on_velocity_computed(new_velocity)
func _on_velocity_computed(safe_velocity: Vector2) -> void:
global_position = global_position.move_toward(global_position + safe_velocity, movement_delta)
extends CharacterBody2D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent2D = get_node("NavigationAgent2D")
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector2):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
# Do not query when the map has never synchronized and is empty.
if NavigationServer2D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
if navigation_agent.is_navigation_finished():
return
var next_path_position: Vector2 = navigation_agent.get_next_path_position()
var new_velocity: Vector2 = global_position.direction_to(next_path_position) * movement_speed
if navigation_agent.avoidance_enabled:
navigation_agent.set_velocity(new_velocity)
else:
_on_velocity_computed(new_velocity)
func _on_velocity_computed(safe_velocity: Vector2):
velocity = safe_velocity
move_and_slide()
extends RigidBody2D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent2D = get_node("NavigationAgent2D")
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector2):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
# Do not query when the map has never synchronized and is empty.
if NavigationServer2D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
if navigation_agent.is_navigation_finished():
return
var next_path_position: Vector2 = navigation_agent.get_next_path_position()
var new_velocity: Vector2 = global_position.direction_to(next_path_position) * movement_speed
if navigation_agent.avoidance_enabled:
navigation_agent.set_velocity(new_velocity)
else:
_on_velocity_computed(new_velocity)
func _on_velocity_computed(safe_velocity: Vector2):
linear_velocity = safe_velocity
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);
}
}
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();
}
}
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;
}
}
extends Node3D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
var physics_delta: float
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
# Save the delta for use in _on_velocity_computed.
physics_delta = delta
# Do not query when the map has never synchronized and is empty.
if NavigationServer3D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
if navigation_agent.is_navigation_finished():
return
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_speed
if navigation_agent.avoidance_enabled:
navigation_agent.set_velocity(new_velocity)
else:
_on_velocity_computed(new_velocity)
func _on_velocity_computed(safe_velocity: Vector3) -> void:
global_position = global_position.move_toward(global_position + safe_velocity, physics_delta * movement_speed)
extends CharacterBody3D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
# Do not query when the map has never synchronized and is empty.
if NavigationServer3D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
if navigation_agent.is_navigation_finished():
return
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_speed
if navigation_agent.avoidance_enabled:
navigation_agent.set_velocity(new_velocity)
else:
_on_velocity_computed(new_velocity)
func _on_velocity_computed(safe_velocity: Vector3):
velocity = safe_velocity
move_and_slide()
extends RigidBody3D
@export var movement_speed: float = 4.0
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
# Do not query when the map has never synchronized and is empty.
if NavigationServer3D.map_get_iteration_id(navigation_agent.get_navigation_map()) == 0:
return
if navigation_agent.is_navigation_finished():
return
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_speed
if navigation_agent.avoidance_enabled:
navigation_agent.set_velocity(new_velocity)
else:
_on_velocity_computed(new_velocity)
func _on_velocity_computed(safe_velocity: Vector3):
linear_velocity = safe_velocity
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);
}
}
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();
}
}
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;
}
}