Использование AnimationTree
Введение
Благодаря AnimationPlayer, Godot обладает одной из самых гибких систем анимации, которую вы можете найти в любом игровом движке. Он практически уникален своей способностью анимировать практически любое свойство в любом узле или ресурсе, а также своими выделенными дорожками преобразования, кривыми Безье, вызова функций, аудио и суб-анимации.
Однако поддержка смешивания этих анимаций через AnimationPlayer ограничена, поскольку можно установить только фиксированное время перехода с плавным переходом.
AnimationTree is a node designed to deal with advanced transitions.
AnimationTree и AnimationPlayer
Прежде чем начать, учтите, что узел AnimationTree не содержит собственных анимаций. Вместо этого он использует анимации, содержащиеся в узле AnimationPlayer. Вы создаёте, редактируете или импортируете анимации в узле AnimationPlayer, а затем используете AnimationTree для управления воспроизведением.
AnimationPlayer и AnimationTree Можно использовать как в 2D-, так и в 3D-сценах. При импорте 3D-сцен и их анимаций можно использовать name suffixes для упрощения процесса и импорта с правильными свойствами. В конце импортированная сцена Godot будет содержать анимацию в узле AnimationPlayer. Поскольку вы редко используете импортированные сцены напрямую в Godot (они либо создаются как экземпляры, либо наследуются), вы можете поместить узел AnimationTree в новую сцену, содержащую импортированную сцену. После этого укажите узлу AnimationTree на AnimationPlayer, созданный в импортированной сцене.
This is how it's done in the Third Person Shooter demo, for reference:
Для игрока была создана новая сцена с CharacterBody3D в качестве корня. Внутри этой сцены был создан исходный файл .dae (Collada) и создан узел AnimationTree.
Создание дерева
Чтобы использовать AnimationTree, необходимо задать корневой узел. Корневой узел анимации — это класс, который содержит и оценивает подузлы и выводит анимацию. Существует 3 типа под-узлов:
Узлы анимации, которые ссылаются на анимацию из связанного
AnimationPlayer.Корневые узлы анимации, которые используются для смешивания подузлов и могут быть вложенными.
Узлы смешивания анимации, которые используются в
AnimationNodeBlendTree, двумерном графе узлов. Узлы смешивания принимают несколько входных портов и имеют один выходной порт.
Доступно несколько типов корневых узлов:
AnimationNodeAnimation: Выбирает анимацию из списка и воспроизводит её. Это простейший корневой узел, который обычно не используется в качестве корня.AnimationNodeBlendTree: Содержит несколько дочерних узлов в графе. Доступно множество смешанных узлов, таких как mix, blend2, blend3, one shot и т. д.AnimationNodeBlendSpace1D: Обеспечивает линейное смешивание между двумя узлами анимации. Управляйте положением смешивания в одномерном пространстве (1D) смешивания для смешивания анимаций.AnimationNodeBlendSpace2D: Обеспечивает линейное смешивание между тремя узлами анимации. Управляйте положением смешивания в двумерном пространстве смешивания для смешивания анимаций.AnimationNodeStateMachine: содержит несколько дочерних узлов в графе. Каждый узел используется как состояние, а для переключения между состояниями используются несколько функций.
Дерево смешения
При создании AnimationNodeBlendTree на нижней панели, под вкладкой AnimationTree, появляется пустой 2d график. По умолчанию он содержит только узел Output.
Для воспроизведения анимации необходимо подключить узел к выходу. Узлы можно добавить через меню Add Node.. или щёлкнув правой кнопкой мыши по пустому месту:
Самый простой способ подключения — напрямую подключить узел Animation к выходу, который просто воспроизведет анимацию.
Ниже приведено описание других доступных узлов:
Blend2 / Blеnd3
Эти узлы будут смешиваться между двумя или тремя входами по указанному пользователем значению смешения:
Смешивание может использовать filters для индивидуального управления тем, какие дорожки смешиваются, а какие нет. Это может быть полезно для наложения анимаций друг на друга.
Для более сложного смешивания рекомендуется использовать пространства смешивания.
OneShоt
Этот узел выполнит анимацию один раз и вернётся после её завершения. Вы можете настроить время наложения для плавного появления и исчезновения, а также фильтры.
# Play child animation connected to "shot" port.
animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE)
# Alternative syntax (same result).
animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE
# Abort child animation connected to "shot" port.
animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT)
# Alternative syntax (same result).
animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT
# Get current state (read-only).
animation_tree.get("parameters/OneShot/active"))
# Alternative syntax (same result).
animation_tree["parameters/OneShot/active"]
// Play child animation connected to "shot" port.
animationTree.Set("parameters/OneShot/request", (int)AnimationNodeOneShot.OneShotRequest.Fire);
// Abort child animation connected to "shot" port.
animationTree.Set("parameters/OneShot/request", (int)AnimationNodeOneShot.OneShotRequest.Abort);
// Get current state (read-only).
animationTree.Get("parameters/OneShot/active");
TimeSeek
Этот узел позволяет выполнить поиск по времени в анимации, связанному с его входом in. Используйте этот узел для воспроизведения Animation, начиная с определённой позиции воспроизведения. Обратите внимание, что значение запроса поиска измеряется в секундах, поэтому, если вы хотите воспроизвести анимацию с начала, установите значение 0.0, а если вы хотите воспроизвести анимацию с 3-й секунды, установите значение 3.0.
# Play child animation from the start.
animation_tree.set("parameters/TimeSeek/seek_request", 0.0)
# Alternative syntax (same result).
animation_tree["parameters/TimeSeek/seek_request"] = 0.0
# Play child animation from 12 second timestamp.
animation_tree.set("parameters/TimeSeek/seek_request", 12.0)
# Alternative syntax (same result).
animation_tree["parameters/TimeSeek/seek_request"] = 12.0
// Play child animation from the start.
animationTree.Set("parameters/TimeSeek/seek_request", 0.0);
// Play child animation from 12 second timestamp.
animationTree.Set("parameters/TimeSeek/seek_request", 12.0);
TimeScаle
Этот узел позволяет масштабировать скорость анимации, подключенной к его входу in. Скорость анимации будет умножена на число, указанное в параметре scale. Установка значения scale в 0 приостановит анимацию. Установка значения scale в отрицательное число приведет к воспроизведению анимации в обратном направлении.
Transition (Переход)
Этот узел представляет собой упрощённую версию StateMachine. Вы подключаете анимации к входам, а текущий индекс состояния определяет, какую анимацию воспроизводить. Вы можете указать время перехода для плавного перехода. В инспекторе вы можете изменить количество входных портов, переупорядочить входы или удалить их.
# Play child animation connected to "state_2" port.
animation_tree.set("parameters/Transition/transition_request", "state_2")
# Alternative syntax (same result).
animation_tree["parameters/Transition/transition_request"] = "state_2"
# Get current state name (read-only).
animation_tree.get("parameters/Transition/current_state")
# Alternative syntax (same result).
animation_tree["parameters/Transition/current_state"]
# Get current state index (read-only).
animation_tree.get("parameters/Transition/current_index"))
# Alternative syntax (same result).
animation_tree["parameters/Transition/current_index"]
// Play child animation connected to "state_2" port.
animationTree.Set("parameters/Transition/transition_request", "state_2");
// Get current state name (read-only).
animationTree.Get("parameters/Transition/current_state");
// Get current state index (read-only).
animationTree.Get("parameters/Transition/current_index");
StateMachinе (машина состояний)
При создании AnimationNodeStateMachine на нижней панели, под вкладкой AnimationTree, появляется пустой 2d график. Он по умолчанию содержит состояния Start и End.
Чтобы добавить состояния, щёлкните правой кнопкой мыши или используйте кнопку Сreate new nodes, значок которой представляет собой плюс в квадратике. Вы можете добавить анимации, пространства смешивания, деревья смешивания и даже другой StateMachine. Чтобы отредактировать один из этих более сложных подузлов, щёлкните по значку карандаша справа от состояния. Чтобы вернуться к исходному StateMachine, щёлкните по кнопке Root в левом верхнем углу панели.
Прежде чем StateMachine сможет что-либо сделать, состояния должны быть соединены переходами. Чтобы добавить переход, нажмите кнопку connect nodes, которая представляет собой линию со стрелкой вправо, и перетащите её между двумя состояниями. Вы можете создать два перехода между состояниями, по одному в каждом направлении.
Существует 3 типа переходов:
Immediate: Немедленно перейдет в следующее состояние.
Sync: Немедленно перейдет в следующее состояние, но будет установлено в позицию воспроизведения старого состояния.
At End: Дождется окончания воспроизведения текущего состояния, а затем переключится в начало анимации следующего состояния.
У переходов также есть несколько свойств. Щёлкните по переходу, и он отобразится в инспекторе:
Xfade Time - время перекрестного затухания между этим состоянием и следующим.
Xfade Curve — это плавный переход по кривой, а не линейное смешивание.
Reset определяет, воспроизводится ли состояние, в которое вы переключаетесь, с самого начала (true) или нет (false).
Приоритет используется совместно с функцией
travel()из кода (подробнее об этом позже). При перемещении по дереву предпочтительны переходы с более низким приоритетом.Switch Mode — тип перехода (см. выше). Его можно изменить после создания здесь.
Advance Mode определяет режим перехода. Если
Disabled, переход не будет использоваться. ЕслиEnabled, переход будет использоваться только во времяtravel(). ЕслиAuto, переход будет использоваться, если условие перехода и выражение истинны или если условия перехода/выражения отсутствуют.
Advance Condition и Advance Expression
Последние два свойства в переходе StateMachine — это Advance Condition и Advance Expression.. Если для параметра «Advance Mode» установлено значение Авто, они определяют, будет ли переход продвигаться или нет.
Условие «Advance Condition» проверяет true/false. Вы можете указать имя пользовательской переменной в текстовом поле, и когда StateMachine достигнет этого перехода, он проверит, является ли ваша переменная true. Если да, переход продолжается. Обратите внимание, что условие «Advance Condition» проверяет только, что переменная имеет значение true, и не может проверять её на ложность (falseness).
Это сильно ограничивает возможности Advance Condition. Если бы вы хотели осуществить переход туда и обратно на основе одного свойства, вам пришлось бы создать две переменные с противоположными значениями и проверять, является ли хотя бы одна из них истинной. Именно поэтому в Godot 4 было добавлено Advance Expression.
Advance Expression работает аналогично Advance Condition, но вместо проверки истинности одной переменной оно вычисляет любое выражение. Выражение (expression) — это любое выражение, которое можно поместить в оператор if. Вот примеры выражений, которые можно использовать в Advance Expression:
is_walkingis_walking == true(behaves the same as the one above)is_walking && !is_idlevelocity > 0player.is_on_floor()
Предупреждение
The expression is case-sensitive. If you reference engine properties,
such as velocity on a CharacterBody3D node, you should use
snake_case naming conventions. If you reference script properties,
you should match the style used in the script, which is typically
snake_case in GDScript and PascalCase in C#.
Вот пример неправильно настроенного перехода StateMachine с использованием Advance Condition:
Это не работает, так как в предварительном условии есть переменная !, которую невозможно проверить.
Вот тот же пример, настроенный правильно, с использованием двух противоположных переменных:
Вот тот же пример, но с использованием расширенного выражения вместо расширенного условия, что устраняет необходимость в двух переменных:
Для использования расширенных выражений необходимо задать базовый узел расширенных выражений в инспекторе узла AnimationTree. По умолчанию он указывает на сам узел AnimationTree, но должен указывать на любой узел, содержащий скрипт с переменными анимации.
См. также
The Advance Expression is evaluated using Godot's Expression class. See Оценка выражений for more information on writing expressions.
Путешествие StateMachine
Одна из приятных особенностей реализации StateMachine в Godot — возможность перемещения. Вы можете указать графу перейти из текущего состояния в другое, посетив все промежуточные. Это делается с помощью алгоритма A*. Если нет пути переходов, начинающихся в текущем состоянии и заканчивающихся в конечном, граф телепортируется в конечное состояние.
Чтобы использовать возможность перемещения, необходимо сначала извлечь объект AnimationNodeStateMachinePlayback из узла AnimationTree (он экспортируется как свойство), а затем вызвать одну из его многочисленных функций:
var state_machine = animation_tree["parameters/playback"]
state_machine.travel("SomeState")
AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)animationTree.Get("parameters/playback");
stateMachine.Travel("SomeState");
Перед началом путешествия необходимо запустить StateMachine. Убедитесь, что вы либо вызвали start(), либо подключили узел к Start.
BlendSpace2D и BlendSpace1D
BlendSpace2D — это узел для расширенного смешивания в двух измерениях. Точки, представляющие анимацию, добавляются в 2D пространство, а затем положение между ними контролируется для определения смешивания:
Вы можете разместить эти точки в любом месте графика, щёлкнув правой кнопкой мыши или используя кнопку Добавить точку, значок которой представляет собой ручку и точку. Независимо от того, где вы разместите точки, треугольник между ними будет автоматически сгенерирован с помощью метода Делоне. Вы также можете управлять диапазонами по осям X и Y и подписывать их.
Наконец, вы также можете изменить режим смешивания. По умолчанию смешивание происходит путём интерполяции точек внутри ближайшего треугольника. При работе с 2D-анимацией (покадровой) может потребоваться режим Discrete. Кроме того, если вы хотите сохранить текущую позицию воспроизведения при переключении между дискретными анимациями, существует режим Carry. Этот режим можно изменить в меню Blend:
BlendSpace1D работает так же, как BlendSpace2D, но в одном измерении (прямой). Треугольники не используются.
Для лучшего смешивания
For the blending results to be deterministic (reproducible and always consistent), the blended property values must have a specific initial value. For example, in the case of two animations to be blended, if one animation has a property track and the other does not, the blended animation is calculated as if the latter animation had a property track with the initial value.
При использовании Дорожки Позиционирования/Вращения/Масштабирования для костей Skeleton3D начальным значением является Bone Rest. Для других свойств начальными значениями являются 0, и если дорожка присутствует в анимации RESET, вместо него используется значение первого ключевого кадра этой дорожки.
Например, в AnimationPlayer, представленном ниже, есть две анимации, но в одной из них отсутствует дорожка свойств для позиционирования.
Это означает, что анимация, в которой отсутствует это, будет обрабатывать эти позиции как Vector2(0, 0).
Эту проблему можно решить, добавив Дорожку свойств для позиционирования в качестве начального значения в анимацию RESET.
Примечание
Имейте в виду, что анимация RESET предназначена для определения позы по умолчанию при первоначальной загрузке объекта. Предполагается, что она содержит только один кадр и не будет воспроизводиться с использованием временной шкалы.
Также следует помнить, что дорожки Rotation 3D и дорожки свойств для вращения 2D с типом интерполяции, установленным на «Линейный угол» или «Кубический угол», предотвратят повороты более чем на 180 градусов от начального значения в качестве смешанной анимации.
Это может быть полезно для Skeleton3Ds, чтобы предотвратить проникновение костей в тело при смешивании анимаций. Поэтому значения Bone Rest в Skeleton3D должны быть как можно ближе к середине диапазона перемещения. Это означает, что для моделей гуманоидов предпочтительнее импортировать их в Т-образной позе.
Вы можете видеть, что приоритет отдается кратчайшему пути поворота от Bone Rest, а не кратчайшему пути поворота между анимациями.
Если вам нужно повернуть сам Skeleton3D более чем на 180 градусов, смешав анимацию движения, вы можете использовать Root Motion.
Корневое движение
При работе с 3D-анимацией популярной техникой является использование аниматорами корневой кости скелета для придания движения остальным частям скелета. Это позволяет анимировать персонажей таким образом, что шаги фактически соответствуют полу под ними. Это также позволяет точно взаимодействовать с объектами в кинематографе.
При воспроизведении анимации в Godot можно выбрать эту кость в качестве корневой дорожки движения ** . Это визуально отменит трансформацию кости (анимация останется на месте).
После этого фактическое движение может быть получено через API AnimationTree как преобразование:
# Get the motion delta.
animation_tree.get_root_motion_position()
animation_tree.get_root_motion_rotation()
animation_tree.get_root_motion_scale()
# Get the actual blended value of the animation.
animation_tree.get_root_motion_position_accumulator()
animation_tree.get_root_motion_rotation_accumulator()
animation_tree.get_root_motion_scale_accumulator()
// Get the motion delta.
animationTree.GetRootMotionPosition();
animationTree.GetRootMotionRotation();
animationTree.GetRootMotionScale();
// Get the actual blended value of the animation.
animationTree.GetRootMotionPositionAccumulator();
animationTree.GetRootMotionRotationAccumulator();
animationTree.GetRootMotionScaleAccumulator();
Это может быть передано в такие функции, как CharacterBody3D.move_and_slide для управления перемещением персонажа.
Также имеется узел инструмента RootMotionView, с помощью которого вы можете разместить сцену, которая будет выступать в качестве настраиваемого пола для вашего персонажа и анимаций (этот узел по умолчанию отключен во время игры).
Контроль из кода
После построения дерева и его предварительного просмотра остается только один вопрос: "Как все это управляется из кода?".
Помните, что узлы анимации — это всего лишь ресурсы, поэтому они являются общими для всех экземпляров, использующих их. Установка значений непосредственно в узлах повлияет на все экземпляры сцены, которые используют AnimationTree. Это, как правило, нежелательно, но имеет несколько интересных вариантов использования, например, вы можете копировать и вставлять части своего дерева анимации или повторно использовать узлы со сложной компоновкой (например, StateMachine или пространство смешивания) в различных деревьях анимации.
Фактические данные анимации содержатся в узле AnimationTree и доступны через свойства. Просмотрите раздел "Параметры" узла AnimationTree, чтобы увидеть все параметры, которые можно изменить в режиме реального времени:
Это удобно, поскольку позволяет анимировать их из AnimationPlayer или даже из самого AnimationTree, что позволяет реализовать очень сложную логику анимации.
Чтобы изменить эти значения из кода, необходимо получить путь к свойству. Вы можете найти его, наведя указатель мыши на любой из параметров:
Затем вы можете установить или прочитать их:
animation_tree.set("parameters/eye_blend/blend_amount", 1.0)
# Alternate syntax (same result)
animation_tree["parameters/eye_blend/blend_amount"] = 1.0
animationTree.Set("parameters/eye_blend/blend_amount", 1.0);
Примечание
Выражения Advance из StateMachine не будут найдены в параметрах. Это связано с тем, что они хранятся в другом скрипте, а не в самом AnimationTree. Предварительные Conditions (Условия) можно найти в параметрах.