Расширенная физическая интерполяция
Хотя предыдущие инструкции дадут удовлетворительные результаты во многих играх, в некоторых случаях вам захочется пойти немного дальше, чтобы получить наилучшие результаты и максимально плавный игровой процесс.
Исключения из автоматической интерполяции физики
Даже при активной интерполяции физики могут возникнуть некоторые локальные ситуации, в которых вам будет полезно отключить автоматическую интерполяцию для Node (или ветви SceneTree), чтобы иметь более точный контроль над выполнением интерполяции вручную.
Это возможно благодаря свойству Node.physics_interpolation_mode, которое присутствует во всех узлах. Например, если вы отключите интерполяцию для узла, дочерние узлы также будут рекурсивно затронуты (поскольку они по умолчанию наследуют родительские настройки). Это означает, что вы можете легко отключить интерполяцию для всей подсцены.
Наиболее распространенная ситуация, когда вам может понадобиться выполнить собственную интерполяцию, — это Камеры.
Камеры
Во многих случаях узел Camera3D может использовать автоматическую интерполяцию, как и любой другой узел. Однако для достижения наилучших результатов, особенно при низкой частоте физических тиков, рекомендуется использовать ручную интерполяцию камеры.
Это связано с тем, что зрители очень чувствительны к движению камеры. Например, Camera3D, которая слегка перестраивается каждую десятую секунды (с частотой тиков 10 транзакций в секунду), часто будет заметна. Вы можете добиться гораздо более плавного результата, перемещая камеру в каждом кадре в _process и вручную следуя за интерполированной целью.
Ручная интерполяция камеры
Убедитесь, что камера использует глобальное координатное пространство
Самый первый шаг при ручной интерполяции камеры — убедиться, что преобразование Camera3D задано в глобальном пространстве, а не наследует преобразование движущегося родительского узла. Это связано с тем, что между движением родительского узла Camera3D и движением самого узла камеры может возникнуть обратная связь, что может привести к сбоям в интерполяции.
Есть два способа сделать это:
Переместите Camera3D так, чтобы она стала независима на своей собственной ветке, а не дочерним элементом движущегося объекта.
Вызовите Node3D.top_level и установите для него значение
true, что заставит камеру игнорировать преобразование своего родителя.
Типичный пример
Типичным примером пользовательского подхода является использование функции look_at в Camera3D в каждом кадре в _process() для просмотра целевого узла (например, игрока).
Но есть проблема. Если мы используем традиционный метод get_global_transform() для узла "target" Camera3D, это преобразование сфокусирует Camera3D только на цели на текущем такте физики. Это не то, что нам нужно, поскольку камера будет перемещаться с каждым тактом физики по мере движения цели. Даже если камера обновляется в каждом кадре, это не обеспечит плавного движения, если цель меняется только на каждом такте физики.
get_global_transform_interpolated()
На самом деле мы хотим сфокусировать камеру не на положении цели на физическом такте, а на интерполированном положении, т. е. положении, в котором цель будет отображаться.
Это можно сделать с помощью функции Node3D.get_global_transform_interpolated. Это работает точно так же, как и получение Node3D.global_transform, но возвращает интерполированное преобразование (во время вызова _process()).
Важно
get_global_transform_interpolated() следует использовать только один или два раза для особых случаев, таких как камеры. Не следует использовать его повсеместно в коде (как из соображений производительности, так и для обеспечения корректного игрового процесса).
Примечание
За исключением таких исключений, как камера, в большинстве случаев ваша игровая логика должна быть реализована в _physics_process(). В игровой логике следует вызывать get_global_transform() или get_transform(), которые вернут текущее физическое преобразование (в глобальном или локальном пространстве соответственно), что обычно требуется для игрового кода.
Пример ручного скрипта камеры
Вот пример простой фиксированной камеры, которая следует за интерполированной целью:
extends Camera3D
# Node that the camera will follow
var _target
# We will smoothly lerp to follow the target
# rather than follow exactly
var _target_pos : Vector3 = Vector3()
func _ready() -> void:
# Find the target node
_target = get_node("../Player")
# Turn off automatic physics interpolation for the Camera3D,
# we will be doing this manually
set_physics_interpolation_mode(Node.PHYSICS_INTERPOLATION_MODE_OFF)
func _process(delta: float) -> void:
# Find the current interpolated transform of the target
var tr : Transform = _target.get_global_transform_interpolated()
# Provide some delayed smoothed lerping towards the target position
_target_pos = lerp(_target_pos, tr.origin, min(delta, 1.0))
# Fixed camera position, but it will follow the target
look_at(_target_pos, Vector3(0, 1, 0))
Движение мыши
Управление камерой с помощью мыши — очень распространённый способ. Но есть проблема. В отличие от ввода с клавиатуры, который может считываться периодически в каждом такте физики, события движения мыши могут поступать непрерывно. Ожидается, что камера отреагирует и будет следовать за этими движениями мыши в следующем кадре, а не будет ждать следующего такта физики.
В этой ситуации может быть лучше отключить физическую интерполяцию для узла камеры (используя Node.physics_interpolation_mode) и напрямую применить ввод мыши к вращению камеры, а не применять его в _physics_process.
Иногда, особенно при работе с камерами, вам может потребоваться комбинация интерполяции и отсутствия интерполяции:
Камера от первого лица может располагаться в месте расположения игрока (возможно, с использованием Node3D.get_global_transform_interpolated), но управлять вращением камеры можно с помощью мыши без интерполяции.
Камера от третьего лица может аналогичным образом определить направление взгляда (целевое местоположение) камеры, используя Node3D.get_global_transform_interpolated, но расположить камеру можно, используя вид мыши без интерполяции.
Существует множество вариаций и разновидностей типов камер, но должно быть ясно, что во многих случаях отключение автоматической интерполяции физики и выполнение этой задачи самостоятельно может дать лучший результат.
Отключение интерполяции на других узлах
Хотя камеры — наиболее распространённый пример, существует ряд случаев, когда может потребоваться, чтобы другие узлы самостоятельно управляли интерполяцией или не использовали её вовсе. Рассмотрим, например, игрока в игре с видом сверху, вращение которого управляется движением мыши. Отключение вращения с учётом физических факторов позволяет игроку в реальном времени синхронизировать вращение с движением мыши.
MultiMeshes (МультиСетки)
Хотя большинство визуальных узлов следуют парадигме одного визуального экземпляра узла, мультисетки могут управлять несколькими экземплярами одного узла. Поэтому у них есть дополнительные функции для управления интерполяцией на уровне каждого экземпляра. Если вы используете интерполированные мультисетки, вам стоит изучить эти функции.
Полную информацию можно найти в документации MultiMesh.