В UEFN есть несколько способов перемещения объектов окружения. Вы можете использовать такие функции, как TeleportTo[] или MoveTo(), чтобы напрямую изменить преобразование, или же другое устройство, например устройство перемещения объектов окружения, чтобы переместить объект окружения по заданной траектории. Однако для этого также можно использовать анимации.
У каждого объекта окружения творческого режима есть play_animation_controller, который можно использовать для воспроизведения анимаций объекта. Анимации имеют несколько преимуществ по сравнению с перемещением преобразования объекта окружения. Анимации обычно обеспечивают более плавное перемещение объектов по сравнению с функциями MoveTo() или TeleportTo(), поскольку позволяют избежать сетевой задержки, возникающей при вызове этих функций во время каждого игрового такта. Также анимации обеспечивают более последовательные коллизии с игроками и другими объектами, и вы можете лучше контролировать, куда и как перемещаются объекты, по сравнению с использованием устройства перемещения объектов. Вы можете воспроизводить анимации в цикле или же проигрывать их вперёд и назад в стиле «пинг-понг».
Кроме того, анимации позволяют выбрать тип интерполяции. Тип интерполяции определяет тип замедления, или кривую анимации, которой будет следовать анимация. Например, при линейной интерполяции анимация воспроизводится с постоянной скоростью, тогда как при интерполяции с замедлением в начале анимация начинается медленно и постепенно ускоряется. Выбрав тип интерполяции, подходящий вашей анимации, вы можете задать разные точки, в которых объект окружения должен замедляться, ускоряться или перемещаться линейно.
Переключаясь между разными анимациями препятствий, вы можете создавать для игроков различные испытания, используя одни и те же объекты окружения. В этом разделе вы узнаете, как использовать этот мощный инструмент для создания собственных анимаций и перемещения объектов окружения!
Настройка элементов управления анимацией
Чтобы настроить элементы управления анимацией для объектов окружения, выполните следующие действия:
Создайте новый файл Verse с помощью проводника Verse и назовите его
movement_behaviors. В нём будут определены вспомогательные функции, необходимые для анимирования объектов окружения.Добавьте выражения
using { /Fortnite.com/Devices },using { /Fortnite.com/Devices/CreativeAnimation }иusing { /UnrealEngine.com/Temporary/SpatialMath }в начало файла, чтобы импортировать эти модули. Они потребуются для анимирования вашего объекта окружения.В
movement_behaviorsсоздайте новое перечислениеenumи назовите егоmove_to_ease_type. Элементы этого перечисления соответствуют разным типам замедления анимации. Вы можете ознакомиться с этими типами замедления в модулеInterpolationTypes.Verse# This file stores functions common to animating creative props using keyframes. # It also defines the move_to_ease_type enum to help in building animations. using { /Fortnite.com/Devices } using { /UnrealEngine.com/Temporary/SpatialMath } using { /Fortnite.com/Devices/CreativeAnimation } # Represents the different movement easing types. move_to_ease_type<public> := enum {Linear, Ease, EaseIn, EaseOut, EaseInOut}Добавьте новый псевдоним типа
vector3с названиемVectorOnes: это будет вектор типаvector3, в котором для параметровX,YиZбудет задано значение1,0. Мы будем использовать этот вектор позже для упрощения вычислений: после определения псевдонима типа нам не придётся постоянно записыватьvector3{X:=1.0, Y:=1.0, Z:=1.0}.Verse# Initializes a vector3 with all values set to 1.0. VectorOnes<public>:vector3 = vector3{X:=1.0, Y:=1.0, Z:=1.0}Добавим новый метод
GetCubicBezierForEaseType(), который будет приниматьmove_to_ease_typeи возвращатьcubic_bezier_parameters.Verse# Return the cubic_bezier_parameters based on the given move_to_ease_type. GetCubicBezierForEaseType(EaseType:move_to_ease_type):cubic_bezier_parameters=Кубическая кривая Безье состоит из четырёх чисел, которые определяют тип функции замедления для анимации. Например, параметры кривой перемещения с замедлением в начале замедляют анимацию в начале, а затем ускоряют её. Параметры линейной кривой обеспечивают воспроизведение анимации с постоянной скоростью. Вы можете определить эти параметры самостоятельно, чтобы создать собственные кривые анимации, но в данном примере это не требуется, поскольку мы будем использовать значения из модуля
InterpolationTypes.В
GetCubicBezierForEaseType()с помощью выраженияcase()получимcubic_bezier_parametersиз модуляInterpolationTypesна основанииmove_to_ease_type. Например, в случаеEaseOutвозвращаются параметрыInterpolationTypes.EaseOut, в случаеLinear—InterpolationTypes.Linearи т. д. Готовая функцияGetCubicBezierForEaseType()должна выглядеть следующим образом:Verse# Return the cubic_bezier_parameters based on the given move_to_ease_type. GetCubicBezierForEaseType(EaseType:move_to_ease_type):cubic_bezier_parameters= case (EaseType): move_to_ease_type.Linear => InterpolationTypes.Linear move_to_ease_type.Ease => InterpolationTypes.Ease move_to_ease_type.EaseIn => InterpolationTypes.EaseIn move_to_ease_type.EaseOut => InterpolationTypes.EaseOut move_to_ease_type.EaseInOut => InterpolationTypes.EaseInOutВернёмся в класс
movable_propи добавим новое редактируемое полеmove_to_ease_typeс названиемMoveEaseType. Это требуемый тип замедления объекта окружения в анимации.Verse# The type of animation easing to apply to the RootProp's movement. The easing type # changes the speed of the animation based on its animation curve. @editable {ToolTip := MoveEaseTypeTip} MoveEaseType:move_to_ease_type = move_to_ease_type.EaseInOut
Создание анимации с помощью опорных кадров
Для создания анимаций в коде будут использоваться опорные кадры. Анимация состоит из одного или нескольких опорных кадров, и каждый опорный кадр определяет параметры объекта в определённых точках анимации. Создав анимацию с использованием опорных кадров, можно установить несколько точек, до которых объект окружения будет перемещаться, вращаться или даже масштабироваться.
Опорные кадры имеют пять параметров. DeltaLocation, DeltaRotation и DeltaScale определяют изменения каждого значения при перемещении объекта окружения между началом и концом опорного кадра. Также есть параметр Time, задающий время воспроизведения анимации в секундах, а также параметр Interpolation, определяющий режим интерполяции для опорного кадра. Вот пример опорного кадра:
# An example keyframe.
KeyFrame := keyframe_delta:
# The target position of the `creative_prop`. This is the difference between the starting and ending translation of the prop.
DeltaLocation := EndTransform.Translation - StartTransform.Translation,
# The target rotation for the `creative_prop` to rotate to.
DeltaRotation := EndTransform.Rotation,
# The target scale for the `creative_prop`. Scale is multiplicative to the starting Scale of the `creative_prop`
Чтобы создать анимацию с использованием опорных кадров, выполните следующие шаги:
Добавьте новый метод расширения
creative_propв свой файлmovement_behaviorsи назовите егоMoveToEase(). Эта функция принимает данные о положении, вращении и масштабе перемещаемого объекта окружения, а также продолжительность воспроизведения анимации, тип замедления перемещения и режим анимации.Verse# Animate a creative_prop by constructing an animation from a single keyframe, and then playing that animation on the prop. # This method takes a Position, Rotation, and Scale for the prop to end at, the duration of the animation, # the type of easing to apply to the movement, and the animation mode of the animation. (CreativeProp:creative_prop).MoveToEase<public>(Position:vector3, Rotation:rotation, Scale:vector3, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void=В
MoveToEase()получим контроллер анимации объекта окружения творческого режима с помощьюGetAnimationController[]. Контроллер анимации позволяет воспроизводить анимации на объекте окружения, а также выдаёт событие, которое мы будем ожидать далее.Verse(CreativeProp:creative_prop).MoveToEase<public>(Position:vector3, Rotation:rotation, Scale:vector3, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void= # Get the animation controller for the CreativeProp to move. if (AnimController := CreativeProp.GetAnimationController[]):Рассчитаем изменение масштаба объекта окружения с помощью
ScaleMultiplicative. В данном случаеScaleопределяется несколько сложнее. Поскольку масштаб представляет собой произведение отдельных коэффициентов масштабирования, то масштаб конечного преобразования — это величина, на которую необходимо умножить исходное преобразование, а не само целевое значение масштаба. Например, если исходный масштаб преобразования равен1,2, а объект нужно масштабировать до1,5, то на самом деле его нужно масштабировать на1,25поскольку1,2 * 1,25 = 1,5. Конечное значение равно суммеVectorOnesи разности между новым и старым масштабом, делённой на старый масштаб. Это значение необходимо для того, чтобы анимация продолжала воспроизводиться правильно, если мы изменим её размер по ходу воспроизведения.Verse# Calculate the multiplicative scale for the keyframe to scale to. ScaleMultiplicative:vector3 = VectorOnes + ((Scale - CreativeProp.GetTransform().Scale) / CreativeProp.GetTransform().Scale)Для воспроизведения каждой анимации требуется массив опорных кадров. В этой функции мы будем использовать единственный опорный кадр, представленный в виде элемента массива. Поскольку данный массив будет содержать только один опорный кадр, объект окружения совершит единственное перемещение в новое место с плавной анимацией. А поскольку мы будем вызывать
MoveToEase()в цикле, анимация объекта окружения может продолжаться без необходимости задавать несколько анимаций подряд.Определим новый массив элементов типа
keyframe_deltaи назовём егоKeyframes. Присвоим этому массиву новый массив и внутри данного массива создадим новый элементkeyframe_delta.Verse# Build the keyframe array from a single keyframe_delta of the given values. Keyframes:[]keyframe_delta = array: keyframe_delta:В определении
keyframe_deltaинициализируем все значения, необходимые для создания опорного кадра. ОпределимDeltaLocationкак разницу между новым значениемPositionи переносом объекта окружения творческого режима. ОпределимDeltaRotationкакRotation, аDeltaScale— как значениеScaleMultiplicative, рассчитанное ранее. Установим времяTimeравнымDurationи получим требуемый типInterpolation, вызвавGetCubicBezierForEaseType().Verse# Build the keyframe array from a single keyframe_delta of the given values. Keyframes:[]keyframe_delta = array: keyframe_delta: DeltaLocation := Position - CreativeProp.GetTransform().Translation, DeltaRotation := Rotation, DeltaScale := ScaleMultiplicative, Time := Duration, Interpolation := GetCubicBezierForEaseType(EaseType)Теперь зададим созданный массив
Keyframesв качестве анимации, воспроизводимой контроллером анимации, с помощьюSetAnimation(), передавAnimationMode. Воспроизведём анимацию посредствомPlay()и будем с помощьюAwait()ожидать событияMovementCompletedEvent. Готовый код метода расширенияMoveToEase()должен выглядеть следующим образом:Verse# Animate a creative_prop by constructing an animation from a single keyframe, and then playing that animation on the prop. # This method takes a Position, Rotation, and Scale for the prop to end at, the duration of the animation, # the type of easing to apply to the movement, and the animation mode of the animation. (CreativeProp:creative_prop).MoveToEase<public>(Position:vector3, Rotation:rotation, Scale:vector3, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void= # Get the animation controller for the CreativeProp to move. if (AnimController := CreativeProp.GetAnimationController[]): # Calculate the multiplicative scale for the keyframe to scale to. ScaleMultiplicative:vector3 = VectorOnes + ((Scale - CreativeProp.GetTransform().Scale) / CreativeProp.GetTransform().Scale)
Перегрузка функций
Хотя написанная нами функция MoveToEase() полезна, в неё не слишком удобно передавать так много параметров при каждом вызове. Бывает, что нужно изменить только какую-то одну часть преобразования объекта окружения, например перенос или вращение, и в этом случае было бы удобней вызывать более простую функцию.
Для решения этой задачи можно воспользоваться перегрузкой функций. Перегружая функцию MoveToEase(), можно создать несколько методов с одинаковыми названиями, которые будут обрабатывать входные данные разных типов. Чтобы настроить перегруженные функции, выполните следующие действия:
В файле
movement_behaviorsперегрузите функциюMoveToEase(), создав другой метод расширения с тем же названием, но с другими входными данными. Эта функцияMoveToEase()будет обновлять только перенос объекта окружения, оставляя вращение и масштаб без изменений. Поэтому ей нужны только аргументыPosition,Duration,EaseTypeиAnimationMode.Verse# An overload of MoveToEase() that changes the position of the prop while keeping the rotation and scale the same. (CreativeProp:creative_prop).MoveToEase<public>(Position:vector3, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void=В новой функции
MoveToEase()вызовем исходную функциюMoveToEase(), передав в неё все входные данные, а такжеIdentityRotation()иVectorOnesв качествеScale. ФункцияIdentityRotation()возвращает вращение, все значения которого равны0, то есть(0,0,0). В данном случае нам требуетсяIdentityRotation(), так как нет нужды добавлять вращение в анимацию. Перегруженная функцияMoveToEase()будет выглядеть следующим образом:Verse# An overload of MoveToEase() that changes the position of the prop while keeping the rotation and scale the same. (CreativeProp:creative_prop).MoveToEase<public>(Position:vector3, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void= CreativeProp.MoveToEase(Position, IdentityRotation(), VectorOnes, Duration, EaseType, AnimationMode)Аналогичным образом создадим другой перегруженный метод, который изменяет только вращение, оставляя перенос и масштаб без изменений. Этот новый перегруженный метод должен выглядеть следующим образом:
Verse# An overload of MoveToEase() that changes the rotation of the prop while keeping the position and scale the same. (CreativeProp:creative_prop).MoveToEase<public>(Rotation:rotation, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void= CreativeProp.MoveToEase(CreativeProp.GetTransform().Translation, Rotation, VectorOnes, Duration, EaseType, AnimationMode)Таким же образом создадим метод, изменяющий только масштаб, сохраняя перенос и вращение без изменений. Однако, поскольку
translationиscaleявляются переменными типаvector3, результирующая функция будет иметь ту же сигнатуру, что и перегрузка, созданная для переноса, что приведёт к ошибке. Эту проблему можно решить, изменив порядок параметров функции. Поменяем местамиDurationиScale, чтобы получить перегруженную функцию. Перегруженный метод должен выглядеть следующим образом:Verse# An overload of MoveToEase() that changes the position and scale of the prop while keeping the rotation the same. (CreativeProp:creative_prop).MoveToEase<public>(Duration:float, Scale:vector3, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void= CreativeProp.MoveToEase(CreativeProp.GetTransform().Translation, IdentityRotation(), Scale, Duration, EaseType, AnimationMode)Полезно иметь версию
MoveToEase(), которая способна обрабатывать несколько опорных кадров. Для этого можно предварительно определить опорные кадры и передать их все вMoveToEase(). Таким образом, в функции нужно будет только настроить и воспроизвести анимацию на объекте окружения творческого режима. Добавим новую перегрузку функцииMoveToEase(), которая будет принимать в качестве входных данных массив опорных кадров и режим анимации.Verse# An overload of MoveToEase() that takes a pre-built array of keyframes and plays an animation. (CreativeProp:creative_prop).MoveToEase<public>(Keyframes:[]keyframe_delta, AnimationMode:animation_mode)<suspends>:void= if (AnimController := CreativeProp.GetAnimationController[]): AnimController.SetAnimation(Keyframes, ?Mode:=AnimationMode) AnimController.Play() AnimController.MovementCompleteEvent.Await()Сохраните свой код и скомпилируйте его.
Методы настроены, и можно перемещать объекты! В следующем разделе мы будем переносить объекты окружения, чтобы создать перемещающиеся платформы!
Полный код
Ниже приведён полный код, написанный в этом разделе:
movement_behaviors.verse
# This file stores functions common to animating creative props using keyframes.
# It also defines the move_to_ease_type enum to help in building animations.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Fortnite.com/Characters}
using { /Fortnite.com/Devices/CreativeAnimation }
# Represents the different movement easing types.
move_to_ease_type<public> := enum {Linear, Ease, EaseIn, EaseOut, EaseInOut}