Existem diversas maneiras de mover adereços no UEFN. Você pode usar funções como TeleportTo[] ou MoveTo() para modificar uma transformação diretamente ou usar outro dispositivo, como um propulsor de adereços, para mover um adereço em um caminho predefinido. No entanto, existe também uma outra opção bastante útil: as animações.
Cada adereço do Modo Criativo inclui um play_animation_controller que você pode usar para reproduzir animações. As animações têm algumas vantagens em relação a mover o transformar dos adereços. Animações geralmente têm movimentos mais suaves do que mover objetos com MoveTo() ou TeleportTo(), pois evitam a latência de rede de ter que chamar essas funções a cada marca de jogo. As animações também contam com colisões mais consistentes com jogadores ou outros objetos, e você tem um maior nível de controle sobre onde e como um objeto se move, se comparado ao uso de um Propulsor de Adereços. Você pode reproduzir animações em loop ou reproduzi-las no sentido normal e inverso com o modo ping-pong.
As animações também deixam você escolher um tipo de interpolação. O tipo de interpolação determina o tipo de suavização, ou curva de animação, que sua animação segue. Por exemplo, o tipo de interpolação linear reproduz sua animação a uma velocidade constante, enquanto o tipo ease-in começa devagar e acelera chegando ao final. Ao escolher o tipo de interpolação certo para a sua animação, você pode especificar em diferentes pontos se o adereço deve desacelerar, acelerar ou se mover linearmente.
Ao alterar entre diferentes animações para os seus obstáculos, você pode criar uma variedade de diferentes desafios para os jogadores usando os mesmos adereços. Nesta seção, você aprenderá a usar essa ferramenta poderosa para criar suas próprias animações e colocar seus adereços em movimento!
Como configurar controles de animação
Siga os passos abaixo para definir controles de animação para os seus adereços:
Com o Explorador do Verse, crie um novo arquivo Verse chamado
movement_behaviors. Ele armazenará as funções de utilidades de que você precisará para animar os adereços.Adicione as declarações
using { /Fortnite.com/Devices },using{ /Fortnite.com/Devices/CreativeAnimation }eusing { /UnrealEngine.com/Temporary/SpatialMath }à parte superior do arquivo para importar esses módulos. Você precisará delas para animar seu adereço.Em
movement_behaviors, crie uma novaenumeraçãochamadamove_to_ease_type. Os valores neste enum correspondem aos diferentes tipos de suavização de animação. Confira cada um desses tipos de suavização no móduloInterpolationTypes.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}Adicione um novo alias de tipo
vector3chamadoVectorOnesque cria umvector3em queX,YeZestão todos definidos como1,0. Você usará este vetor mais tarde para facilitar uma parte dos cálculos, assim, definir um alias de tipo significa que você não precisa escrevervector3{X:=1.0, Y:=1.0, Z:=1.0}repetidamente.Verse# Initializes a vector3 with all values set to 1.0. VectorOnes<public>:vector3 = vector3{X:=1.0, Y:=1.0, Z:=1.0}Adicione um novo método
GetCubicBezierForEaseType()que recebe ummove_to_ease_typee retorna umcubic_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=A curva cúbica (cubic bezier) consiste em quatro números que definem o tipo de função de suavização que a animação utiliza. Por exemplo, os parâmetros para uma curva ease-in fazem a animação começar com velocidade reduzida e acelerar depois. Os parâmetros para uma curva linear fazem a animação ser reproduzida a uma velocidade constante. Você pode definir esses valores manualmente para criar suas próprias curvas de animação, o que não é necessário neste exemplo, pois você utilizará as curvas já definidas no módulo
InterpolationTypes.Em
GetCubicBezierForEaseType(), numa expressãocase(), recupere ocubic_bezier_parametersdo móduloInterpolationTypesbaseado nomove_to_ease_type. Por exemplo,EaseOutdeve retornarInterpolationTypes.EaseOut,Lineardeve retornarInterpolationTypes.Lineare assim por diante. A função completaGetCubicBezierForEaseType()deve ficar assim: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.EaseInOutVoltando para sua classe
movable_prop, adicione um novomove_to_ease_typeeditável chamadoMoveEaseType. Este é o tipo de suavização que o seu adereço aplicará em sua animação.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
Como construir uma animação com keyframes
Para criar animações em código, você usará keyframes. Animações são compostas por um ou mais keyframes, e cada keyframe especifica os valores de um objeto em pontos específicos na animação. Ao criar uma animação usando keyframes, você pode especificar vários pontos para seu adereço se mover, girar ou até se redimensionar.
Cada keyframe possui cinco valores. O DeltaLocation, DeltaRotation e DeltaScale especificam as alterações em cada valor que o adereço faz desde o início até o final do keyframe. Existe também o Time, ou a quantidade de tempo, em segundos, que a animação dura, e Interpolation, ou o modo de interpolação para o keyframe. Um exemplo de keyframe pode ser assim:
# 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`
Siga os passos abaixo para criar uma animação usando keyframes:
Adicione um novo método de extensão do
creative_propchamadoMoveToEase()ao seu arquivomovement_behaviors. Essa função recebe a posição, a rotação e a escala para as quais o adereço se move, uma duração que representa quanto tempo dura a animação, um tipo de suavização de movimento e um modo de animação.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=Em
MoveToEase(), obtenha o controlador de animação do adereço de Modo Criativo usandoGetAnimationController[]. O controlador de animação é o que permite reproduzir animações no adereço, além de expor um evento que você aguardará mais tarde.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[]):Calcule a mudança de escala do adereço, chamada de
ScaleMultiplicative. Aescalaé um pouco mais complexa nesta situação. Como a escala é multiplicativa, o transformar final da escala precisa ser o valor pelo qual o transformar original precisa escalar, e não o valor que ela deve atingir. Por exemplo, se a escala da transformação original for1,2e você quiser dimensionar para1,5, na verdade precisa dimensionar para1,25, já que1,2 * 1,25 = 1,5. O valor final éVectorOnesmais a diferença entre a escala nova e a antiga dividida pela escala antiga. Você precisa desse valor para garantir que sua animação continuará sendo animada adequadamente se você alterar seu tamanho durante a animação.Verse# Calculate the multiplicative scale for the keyframe to scale to. ScaleMultiplicative:vector3 = VectorOnes + ((Scale - CreativeProp.GetTransform().Scale) / CreativeProp.GetTransform().Scale)Cada animação precisa de uma matriz de keyframes para executar. Nesta função, você usará apenas um único keyframe e o converterá em uma matriz. Como essa matriz terá apenas um único keyframe, o seu adereço fará um movimento com animação suave até a nova localização. E como você chamará
MoveToEase()dentro de um loop, o adereço pode continuar sua animação sem precisar especificar várias animações seguidas.Defina uma nova matriz de
keyframe_deltachamadaKeyframes. Defina essa matriz igual a uma nova matriz e, dentro dela, crie um novokeyfram_delta.Verse# Build the keyframe array from a single keyframe_delta of the given values. Keyframes:[]keyframe_delta = array: keyframe_delta:Dentro da definição do
keyframe_delta, inicialize cada valor necessário para criar seu keyframe. DefinaDeltaLocationcomo a diferença entre a novaPositione a translação do adereço de Modo Criativo. DefinaDeltaRotationcomoRotationeDeltaScalecomo oScaleMultiplicativeque você calculou anteriormente. DefinaTimecomoDuratione obtenha aInterpolationcorreta chamandoGetCubicBezierForEaseType().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)Com sua matriz
Keyframescriada, defina-a como a animação do controlador de animação usandoSetAnimation()e passando oAnimationMode. Reproduza a animação usandoPlay(), em seguida, useAwait()para aguardar oMovementCompletedEvent. Seu método de extensãoMoveToEase()completo deve ficar assim: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)
Sobrecarga de funções
Embora a função MoveToEase() que você escreveu seja útil, pode ser complicado passar um número tão grande de variáveis para a função toda vez que você quiser chamá-la. Pode haver situações em que você queira apenas alterar uma parte do transformar do adereço, como a translação ou rotação, e seria útil ter uma função mais simples a ser chamada nesse caso.
Para solucionar isso, você pode aproveitar a sobrecarga de funções. Ao sobrecarregar a função MoveToEase(), você pode definir vários métodos com o mesmo nome para cuidar de diferentes tipos de entrada. Siga os passos abaixo para definir suas funções sobrecarregadas.
No seu arquivo
movement_behaviors, sobrecarregue a funçãoMoveToEase()criando outro método de extensão com o mesmo nome, mas entradas diferentes. EsteMoveToEase()só atualizará a translação de um adereço, deixando a rotação e a escala inalteradas. Isso significa que você precisa apenas dos argumentosPosition,Duration,EaseTypeeAnimationMode.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=Em seu novo método
MoveToEase(), chame a função originalMoveToEase(), passando todas as entradas maisIdentityRotation()eVectorOnescomoScale. OIdentityRotation()retorna uma rotação com todo valor em0, ou seja,(0,0,0). Você precisa doIdentityRotation()aqui porque não quer adicionar uma rotação à sua animação. Sua funçãoMoveToEase()sobrecarregada deve ficar assim: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)Repita este processo para criar outro método sobrecarregado que altera apenas a rotação, mantendo a translação e escala inalteradas. Este novo método sobrecarregado deve ficar assim:
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)Repita o processo novamente para criar um método que altera apenas a escala, mantendo a translação e rotação inalteradas. No entanto, como
translationeescalasão ambas ovector3, a função resultante teria a mesma assinatura que a sobrecarga que você fez anteriormente, gerando um erro. Para resolver isso, reordene os parâmetros da função. Inverta a posição deDurationeScalepara obter sua função sobrecarregada. O método sobrecarregado deve ficar assim: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)É útil ter uma versão de
MoveToEase()que pode cuidar de vários keyframes. Para fazer isso, você pode configurar seus keyframes antecipadamente e passá-los todos aMoveToEase(). Dessa forma, a função precisa apenas definir e reproduzir a animação no adereço de Modo Criativo. Adicione uma nova sobrecarga deMoveToEase()que recebe uma matriz de keyframes e o modo de animação como entrada.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()Salve o código e compile-o.
Com os seus métodos prontos, é hora de colocar tudo em movimento! Na próxima seção, você transladará adereços para criar plataformas móveis!
Código completo
Aqui está o código completo feito nesta seção:
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}