Path3D, PathFollow3D и сплайны
Curve3D, Path3D, PathFollow3D — движение по сплайну, AI-патрули, кат-сцены камеры.
В Godot есть встроенные Path3D и PathFollow3D для движения по сплайну. Кривая хранится в
ресурсе Curve3D. Не нужно ставить плагины — это часть core engine.
Главные узлы
- Path3D — узел-контейнер с ресурсом
Curve3D. Описывает кривую в пространстве. - PathFollow3D — дочерний узел Path3D. Содержит свойство
progressилиprogress_ratio, по которому Godot вычисляет позицию вдоль кривой. Всё, что лежит внутри PathFollow3D, едет вдоль сплайна. - Curve3D — ресурс с массивом точек, у каждой — позиция + in/out tangents (для Bezier).
SVG-path с командами M, L, C. Или GSAP MotionPath plugin для анимации по кривой.
Базовая работа
Создание Path3D в редакторе
- Добавьте на сцену Path3D.
- В Inspector создайте новый
Curve3D(если ещё нет). - В Scene view выберите Path3D — появится Path Tool на toolbar.
- Кликами добавляйте точки. Перетаскивайте handles для редактирования касательных (Bezier).
Структура сцены
Level (Node3D)
└── Path3D ← curve: Curve3D с N точками
└── PathFollow3D
└── Cart (CharacterBody3D или Node3D) ← поедет вдоль сплайна
Движение из кода
extends PathFollow3D
@export var speed: float = 4.0
@export var loop_mode: int = 0 # 0 = loop, 1 = stop at end
func _process(delta: float) -> void:
progress += speed * delta
# progress — дистанция в метрах вдоль кривой
# progress_ratio — нормализовано 0..1 (для удобного цикла)
Свойства PathFollow3D:
progress: float— текущая позиция вдоль кривой в метрах.progress_ratio: float— нормализованный параметр[0, 1].loop: bool— зацикливать ли (по умолчаниюtrue).rotation_mode: RotationMode—ROTATION_NONE/_Y/_XY/_XYZ/_ORIENTED. ORIENTED — поворачивает по tangent кривой (для машинки, движущейся по дороге).use_model_front: bool— корректирует ориентацию (если модель смотрит в -Z).
progress — это расстояние в метрах вдоль сплайна. Скорость в м/с — progress += speed * delta
даст постоянную скорость. progress_ratio (0..1) удобнее для UI или анимации длительности
(“проехать всю кривую за 5 секунд”), но скорость станет зависеть от длины кривой.
Программное создание кривой
var curve := Curve3D.new()
curve.add_point(Vector3(0, 0, 0))
curve.add_point(Vector3(5, 0, 0))
curve.add_point(Vector3(5, 2, -5),
Vector3(0, 0, 2), # in-tangent — вход в точку из предыдущей
Vector3(0, 0, -2)) # out-tangent — выход к следующей
curve.add_point(Vector3(0, 0, -10))
var path := Path3D.new()
path.curve = curve
add_child(path)
add_point(position, in_handle, out_handle, index) — без in/out даёт прямые сегменты,
с — Bezier-кривые.
Сэмплирование без узла PathFollow3D
Curve3D имеет методы для прямого сэмплирования:
var point := curve.sample(0.5) # точка на середине (нормализовано)
var point_baked := curve.sample_baked(distance_m) # точка на distance_m метров вдоль
var transform := curve.sample_baked_with_rotation(distance_m, true, true)
sample_baked использует baked length cache — внутренний массив precomputed точек, который
обновляется при изменении кривой. Сэмплирование по distance работает в O(log N).
Камера на рельсах (cinemachine-style)
В Godot нет встроенного эквивалента Cinemachine SplineDolly, но самостоятельно — 5 строк:
extends Camera3D
@export var path: Path3D
@export var duration: float = 5.0
@export var look_at: Node3D
var _t: float = 0.0
var _playing: bool = false
func play() -> void:
_t = 0.0
_playing = true
func _process(delta: float) -> void:
if not _playing or path == null:
return
_t += delta / duration
if _t >= 1.0:
_t = 1.0
_playing = false
var distance := path.curve.get_baked_length() * _t
var pos := path.curve.sample_baked(distance)
global_position = path.to_global(pos)
if look_at != null:
look_at(look_at.global_position, Vector3.UP)
Или используйте плагин Phantom Camera (упомянут в главе про камеру) — у него есть Path follow mode.
Расстановка объектов вдоль сплайна
Godot core не имеет встроенного “Instantiate along path” — это нужно делать самостоятельно. Простой
скрипт-инструмент через @tool:
@tool
extends Node3D
@export var path: Path3D
@export var prefab: PackedScene
@export var spacing: float = 2.0
@export var update: bool = false:
set(value):
if value: _rebuild()
update = false
func _rebuild() -> void:
for child in get_children():
child.queue_free()
if path == null or prefab == null:
return
var length := path.curve.get_baked_length()
var count := int(length / spacing)
for i in count:
var instance := prefab.instantiate()
var pos := path.curve.sample_baked(i * spacing)
instance.position = pos
add_child(instance)
instance.owner = get_tree().edited_scene_root
Поставьте этот узел детям Path3D, перетащите prefab, нажмите update в Inspector — расставит
N инстансов с шагом spacing.
Когда использовать сплайны
- AI-патруль по предзаданному маршруту — PathFollow3D + ROTATION_Y.
- Машинки на трассе — PathFollow3D + ROTATION_ORIENTED.
- Кат-сцены камеры — Camera3D на PathFollow3D + look_at цель.
- Конвейерные ленты, грузовые тележки — что угодно, движущееся по фиксированной траектории.
- Trail-эффекты — позиция позади игрока, сэмплируется из недавнего трейла.
Когда НЕ использовать
- Прямые отрезки — массив
Vector3дешевле. - NavMesh-навигация по неровной местности — у Path3D нет obstacle avoidance. Используйте
NavigationAgent3D(см. главу про Navigation). - Динамически создаваемые сотни кривых в кадре — для тяжёлых случаев лучше WorkerThreadPool (см. следующую главу) или GDExtension.