~2 мин чтения

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 для анимации по кривой.

Unity

Базовая работа

Создание Path3D в редакторе

  1. Добавьте на сцену Path3D.
  2. В Inspector создайте новый Curve3D (если ещё нет).
  3. В Scene view выберите Path3D — появится Path Tool на toolbar.
  4. Кликами добавляйте точки. Перетаскивайте 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: RotationModeROTATION_NONE / _Y / _XY / _XYZ / _ORIENTED. ORIENTED — поворачивает по tangent кривой (для машинки, движущейся по дороге).
  • use_model_front: bool — корректирует ориентацию (если модель смотрит в -Z).
progress vs progress_ratio

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.