Este tutorial es una versión avanzada específica de Scene Graph del tutorial Cómo animar el movimiento de la utilería. Si quieres obtener más información sobre el movimiento basado en animaciones en UEFN fuera de Scene Graph, echa un vistazo a ese tutorial y, luego, vuelve a este.
Las plataformas móviles son comunes en la mayoría de los juegos de plataformas, y desafían al jugador a realizar saltos precisos entre objetivos para alcanzar la meta.
Hay varias maneras en las que se pueden mover utilerías en UEFN. Puedes usar funciones como TeleportTo[] o MoveTo() para modificar una transformación directamente, o usar otro dispositivo como un movedor de utilería para mover una utilería en una trayectoria preestablecida. Sin embargo, hay otra opción útil a través de las animaciones.
Las animaciones tienen un par de beneficios con respecto a mover la transformación de una utilería. Las animaciones suelen tener un movimiento más suave que los objetos en movimiento con MoveTo() o TeleportTo()porque evitan la latencia de red de tener que llamar a estas funciones en cada tic del juego.
Las animaciones también tienen colisiones más consistentes con jugadores u otros objetos, y tienes un mayor nivel de control sobre dónde y cómo se mueve un objeto en comparación con un dispositivo movedor de utilería. Puedes reproducir animaciones en bucle o reproducirlas hacia adelante o hacia atrás con el modo ping-pong.
Las animaciones también te permiten elegir un tipo de interpolación. El tipo de interpolación determina el tipo de suavizado, o la curva de animación, que tu animación sigue. Por ejemplo, el tipo de interpolación lineal reproduce tu animación a una velocidad constante, mientras que el tipo de entrada suave comienza despacio y luego acelera hacia el final.
Al escoger el tipo de interpolación correcto para tu animación, puedes especificar en diferentes puntos si la utilería debería ralentizarse, acelerarse o moverse linealmente. Si aplicas un componente para implementar estos comportamientos en las entidades en tu nivel, puedes crear plataformas móviles por las que tus jugadores pueden navegar.
Primero, considera qué tipo de comportamientos debe poder realizar una plataforma móvil. La animación debería comenzar desde una posición inicial determinada y, luego, moverse a varios puntos. Cuando llegue al final de su movimiento, debería poder volver a su posición inicial o quedarse donde está.
Debería realizar estos movimientos durante una duración específica, y ser capaz de rotar y escalar adecuadamente en cada punto a lo largo de su recorrido. Cada uno de estos comportamientos requiere un código específico para lograrlo, pero si comienzas con una clase simple y construyes, puedes iterar rápidamente sobre diferentes ideas. Sigue estos pasos para crear un componente de movimiento basado en animación.
Crea un nuevo componente de Verse denominado
animate_to_targets_componenty ábrelo en Visual Studio Code. Para obtener más información sobre cómo crear tus propios componentes de Verse, consulta Cómo crear tu propio componente de Verse. Añade instruccionesusingpara/Verse.org/SpatialMath,/Verse.org/SceneGraph/KeyframedMovement, y/UnrealEngine.com/Temporary/SpatialMath. Necesitarás las funciones de cada uno de estos más adelante.Verseusing { /Verse.org } using { /Verse.org/Native } using { /Verse.org/SceneGraph } using { /Verse.org/Simulation } using { /Verse.org/SpatialMath } using { /Verse.org/SceneGraph/KeyframedMovement } using { /UnrealEngine.com/Temporary/SpatialMath } # Place this component to an entity to move between preset targets. animate_to_targets_component<public> := class<final_super>(component):La información sobre las herramientas utilizada en esta sección del tutorial se incluye a continuación. Puedes copiarlos y pegarlos encima de la definición de la clase
animate_to_targets_component.Verse# Editor Tool Tips DefaultSpeedTip<localizes><public>:message = "Default speed simulation entity moves during any segment that does not specify a speed." SpeedTip<localizes><public>:message = "Speed simulation entity moves during this segment." AnimationDurationTip<localizes><public>:message = "The duration of the animation segment in seconds." EasingFunctionTip<localizes><public>:message = "Movement cubic-bezier easing function during this segment." DefaultEasingFunctionTip<localizes><public>:message = "Default movement cubic-bezier easing function during any segment that does not specify an easing function." PlaybackModeTip<localizes><public>:message = "Animation playback mode\n\tOneshot: Animation plays once.\n\tPingPong: Animation plays in order and reverse order repeating.\n\tLoop: Animation repeats in a loop. Tip: To construct an animation in a closed loop, make the last segment starting position the same as the first segment starting position." TargetsTip<localizes><public>:message = "Entities that are targets for the parent entity." RandomizeTargetsTip<localizes><public>:message = "Randomize the order of the segments.\n\tNever: Always use the order specified.\n\tOnBegin: Only randomize the order on begin simulation.\n\tEveryIteration: Randomize the order of the segments every loop iteration." PauseTip<localizes><public>:message = "Duration simulation entity pauses at the beginning of this segment."Añade los siguientes campos a la definición de la clase
animate_to_targets_component:Una variable float de número editable denominada
InitialPauseSeconds. Esta es la cantidad de tiempo que transcurre antes de que la entidad comience a animarse. Define este valor en10.0para que la entidad espere diez segundos antes de comenzar a animarse.
Verse# Amount of time to pause before the animation starts. @editable_number(float): ToolTip := SpeedTip MinValue := option{0.0} var InitialPauseSeconds<public>:float = 10.0Un
keyframed_movement_playback_modeeditable denominadoAnimationPlaybackMode. Este es el modo de animación para la animación de la entidad. Establece esto enloop_keyframed_movement_playback_mode. Esto significa que, de forma predeterminada, cuando la animación se completa, la entidad hace un bucle y comienza su animación nuevamente desde el principio.
Verse# The playback mode used by this animation. @editable: ToolTip := PlaybackModeTip var PlaybackMode<public>:keyframed_movement_playback_mode = loop_keyframed_movement_playback_mode{}Guarda tu código y compílalo.
Cómo dividir animaciones en segmentos
Para crear animaciones en el código, usarás fotogramas clave. Las animaciones están compuestas por uno o más fotogramas clave, y cada fotograma clave especifica los valores de un objeto en puntos específicos de la animación. Al crear una animación usando fotogramas clave, puedes especificar múltiples puntos a los que se puede mover, rotar y escalar tu utilería.
El keyframed_movement_component utiliza un keyframed_movement_delta como su tipo de fotograma clave. Estos deltas de movimiento tienen tres valores:
Transformar: Especifica los cambios en la transformar en relación con el fotograma clave anterior.Duración: el tiempo en segundos que dura el fotograma clave.Suavizado: la función de aceleración que se utilizará al reproducir este fotograma clave.
Dado que el keyframed_movement_component toma una matriz de fotogramas clave y luego los reproduce, debes proporcionar todos los fotogramas clave a la vez para cada animación que desees reproducir. Hay dos formas de hacer esto:
Podrías construir una matriz de múltiples fotogramas clave en el código, luego pasarla al componente de movimiento de fotogramas clave y reproducir una sola animación.
Puedes crear varias matrices que contengan un único fotograma clave y pasarlas de forma individual al componente de movimiento de fotogramas clave para reproducir varias animaciones en secuencia.
Ambas opciones tienen sus compensaciones. Las matrices de fotogramas clave únicos te permiten realizar operaciones entre fotogramas clave con mayor facilidad, pero requieren más código para su manejo. La construcción de todos los fotogramas clave a la vez facilita la administración, pero es más difícil realizar operaciones mientras se reproduce la animación. Tanto el tutorial de animación de movimiento de utilería como este tutorial cubren el primer enfoque, pero también se proporcionará una implementación del segundo enfoque.
Para construir fotogramas clave individuales en el código, vas a definir una clase de segmento. Cada segmento representará un fotograma clave utilizado por el keyframed_movement_component que puedes construir desde el editor. También podrás incluir datos adicionales, como la cantidad de tiempo de espera entre cada fotograma clave. Sigue los pasos a continuación para compilar tu clase de segmento.
Agrega una nueva clase llamada
segmentoa tu archivoanimate_to_targets_component.verse. Añade el especificador<concrete>a fin de permitir que esta clase se use como un valor@editable.Verse# Defines a segment of animation, which includes a starting position, animation speed and duration, and easing function. # Each segment acts as a single animation, and multiple segments make up an animation sequence. segment<public> := class<concrete>:Añade los siguientes campos a tu definición de clase de segmento:
Una opción de
entidad editabledenominada SegmentStartPosition. Esta entidad actuará como referencia para la posición en el entorno desde donde la entidad debe comenzar a animar.
Verse# An entity that represents the starting position of this entity during the animation segement. @editable: ToolTip := SourceTip SegmentStartPosition:?entity = falseUn
floateditable denominadoAnimationDuration. Este es el tiempo que tardará en reproducirse este segmento de animación. Define esto en2.0para que cada segmento de animación tarde dos segundos en reproducirse.
Verse# The duration of the animation segment. @editable: ToolTip := AnimationDurationTip AnimationDuration:float = 2.0Una opción de función de aceleración editable denominada
EasingFunction. Esta es la función de aceleración utilizada durante este segmento de la animación.
Verse# The easing function to use during this segment of animation. @editable: ToolTip := EasingFunctionTip EasingFunction:?easing_function = falseCada función de suavizado se define mediante una curva de Bézier cúbica, que consta de cuatro números que crean el tipo de función de suavizado que utiliza la animación. Por ejemplo, los parámetros para una curva de entrada suave hacen que la animación se ralentice al inicio y se acelere después.
Los parámetros para una curva lineal hacen que la animación se reproduzca a una velocidad constante. Puedes definir estos valores tú mismo para crear tus propias curvas de animación personalizadas, pero no es necesario en este ejemplo, ya que usarás los definidos en el
móduloKeyframedMovement.Una opción float editable denominada
PauseSeconds. esta es la cantidad de tiempo en segundos para hacer una pausa antes de comenzar este segmento de animación. Puedes pensar en esto como la cantidad de tiempo que una entidad se detiene antes de moverse desde cada punto a lo largo de su ruta.
Verse# The number of seconds to pause before starting this animation segment. @editable: ToolTip := PauseTip PauseSeconds:?float = falseDe vuelta en la definición de tu clase
animate_to_targets_component, añade los siguientes campos:Una matriz editable de segmento denominada Segments. Esto hará referencia a cada segmento de animación que conforma la animación general por la que se ejecuta tu entidad.
Verse# Segments of the keyframed movement animation. @editable: ToolTip := SegmentsTip var Segments<private>:[]segment = array{}Una
función de aceleracióneditable denominadaDefaultEasingFunction. Si un segmento de animación no especifica una función de aceleración, esta será la que se utilice de forma predeterminada. Ajústalo a laEase_in_out_cubic_bezier_easing_function.
Verse# Movement easing function between two targets @editable: ToolTip := DefaultEasingFunctionTip var DefaultEasingFunction<public>:easing_function = ease_in_out_cubic_bezier_easing_function{}Guarda tu código y compílalo. En el editor, deberías ver que la
matriz de segmentosaparece en las entidades que tienen adjunto el componente animate_to_targets_component.
Creación de fotogramas clave con código
Con tu clase de segmento completa, es hora de construir los fotogramas clave que definen tus segmentos. Construirás fotogramas clave de forma individual y los añadirás a una matriz; luego, pasarás la matriz a tu keyframed_movement_component. Esto requerirá algo de matemática, que definirás ahora.
Debido a que las operaciones matemáticas son útiles en una variedad de escenarios, es útil colocar esa lógica en un archivo de utilidad para acceder a ella desde cualquiera de tus componentes de Verse. Para conocer más prácticas recomendadas de Verse al trabajar con entidades, consulta Cómo crear tu propio componente de Verse. Sigue estos pasos para crear tu archivo de utilidad:
Crea un nuevo módulo en el archivo
animate_to_targets.verseque se llameUtilidades. esto almacenará la lógica común que usarás en todo el proyecto.Verse# Module containing utility functions. Utilities<public> := module:Agrega un nuevo alias de tipo
vector3llamadoVectorOnesa tu módulo Utilidades que haga unvector3dondeLeft,UpyForwardestén establecidos en1.0. Usarás este vector luego para hacer cálculos matemáticos con más facilidad, de modo que definir un alias de tipo significa que no tienes que escribirvector3{Left := 1.0, Up := 1.0, Forward := 1.0}repetidamente. Porque importaste ambos/Verse.org/SpatialMathy/UnrealEngine.com/Temporary/SpatialMathmódulos, deberá especificar que se trata de un/Verse.org/SpatialMathvector3ya que ambos módulos incluyen una definición para él.Verse# Utility function for the identity of component-wise vector multiplication. VectorOnes<public>()<transacts>:(/Verse.org/SpatialMath:)vector3 = (/Verse.org/SpatialMath:)vector3{Left := 1.0, Up := 1.0, Forward := 1.0}Agrega una nueva función llamada
GetDeltaTransform()a tu móduloUtilidades. Esta función calculará la diferencia entre dos transformar y return la delta. Agrega el modificador<transacts>a esta función para permitir su reversión. Especifica/Verse.org/SpatialMathcomo módulo para cadatransformar, ya que calcularás la diferencia entre las transformaciones de Entidad.Verse# Get the delta transform between two given transforms. GetDeltaTransform<public>(TransformOne:(/Verse.org/SpatialMath:)transform, TransformTwo:(/Verse.org/SpatialMath:)transform)<transacts>:(/Verse.org/SpatialMath:)transform=En
GetDeltaTransform, inicializa un nuevo/Verse.org/SpatialMathtransformar. Define latraslacióna la diferencia entre la traslación de cada transformación. Define larotaciónal resultado de la llamadaMakeComponentWiseDeltaRotation(). Como esta función se encuentra en el módulo/UnrealEngine.com/Temporary/SpatialMath, deberás realizar la conversión desde las rotaciones de/Verse.org/SpatialMatha las rotaciones de/UnrealEngine.com/Temporary/SpatialMath. Puedes hacerlo con la funciónFromRotation(). Llama aMakeComponentWiseDeltaRotation()pasando la rotación de cada transformación después de convertirla conFromRotation(). A continuación, convierte el resultado de esta llamada a función conFromRotation()de nuevo para convertirlo de nuevo a/Verse.org/SpatialMathrotación. Por último, establece la escala en el resultado de añadirVectorOnesa la diferencia entre la primera y la segunda escala dividida por la primera. Esto garantiza que tu Entidad se escala correctamente mientras se anima. La funciónGetDeltaTransform()completa debería verse así:Verse# Get the delta transform between two given transforms. GetDeltaTransform<public>(TransformOne:(/Verse.org/SpatialMath:)transform, TransformTwo:(/Verse.org/SpatialMath:)transform)<transacts>:(/Verse.org/SpatialMath:)transform= (/Verse.org/SpatialMath:)transform: Translation := TransformTwo.Translation - TransformOne.Translation Rotation := FromRotation(MakeComponentWiseDeltaRotation( FromRotation(TransformTwo.Rotation), FromRotation(TransformOne.Rotation))) Scale := VectorOnes() + ((TransformTwo.Scale - TransformOne.Scale) / TransformOne.Scale)Por último, añade una función llamada
TryGetvalueOrDefault()a tu móduloUtilidadesy añádele el modificador<transacts>. Esta función toma un valor deopciónde algún tipo y un valor predeterminado del mismo tipo y devuelve el valor predeterminado o el elemento dentro deValuesi existe. Esto es útil cuando deseas comprobar si el valor de una clase está realmente inicializado, y garantiza que devolverás algún valor si no lo está. Dentro deTryGetValueOrDefault(), comprueba siValuecontiene un valor y lo devuelve. De lo contrario, devuelveDefault. El módulo completode Utilidadesy la funciónTryGetValurOrDefault()deberían verse de la siguiente manera:Verse# Module containing utility functions. Utilities<public> := module: # Utility function for the identity of component-wise vector multiplication. VectorOnes<public>()<transacts>:(/Verse.org/SpatialMath:)vector3 = (/Verse.org/SpatialMath:)vector3{Left := 1.0, Up := 1.0, Forward := 1.0} # Get the delta transform between two given transforms. GetDeltaTransform<public>(TransformOne:(/Verse.org/SpatialMath:)transform, TransformTwo:(/Verse.org/SpatialMath:)transform)<transacts>:(/Verse.org/SpatialMath:)transform=
Una vez definidas las matemáticas, ¡ahora puedes crear tus fotogramas clave a partir del código!
Sigue estos pasos para construir tus funciones de creación de fotogramas clave:
Añade una nueva función llamada
ConstructKeyframe()a tu definición de claseanimate_to_targets. Esta función toma una entidad de origen, una entidad de destino, una función de aceleración opcional y una duración. Incluso devuelve una matriz dekeyframed_movements_delta.Verse# Construct a single keyframe that animates between the Source and Destination entity using the given easing function over a set duration. ConstructKeyframe<private>(Source:entity, Destination:entity, Easing:?easing_function, Duration:float)<transacts><decides>:[]keyframed_movement_delta=En
ConstructKeyframe(), obtén primero las transformaciones de las entidades deorigenydestinollamando aGetGlobalTransform().Verse# Construct a single keyframe which animates between the Source and Destination entity using the given easing function over a set duration. ConstructKeyframe<private>(Source:entity, Destination:entity, EasingFunction:easing_function, Duration:float)<transacts><decides>:[]keyframed_movement_delta= var SourceTransform:(/Verse.org/SpatialMath:)transform = Source.GetGlobalTransform() var DestinationTransform:(/Verse.org/SpatialMath:)transform = Destination.GetGlobalTransform()Inicializa una matriz con un solo miembro de
keyframed_movement_delta. Definetransformaral resultado de llamar aGetDeltaTransform()pasando las transformaciones de origen y destino, y define laduracióny laaceleracióna los valores pasados a esta función. Tu funciónConstructKeyframe()completa debería verse así:Verse# Construct a single keyframe which animates between the Source and Destination entity using the given easing function over a set duration. ConstructKeyframe<private>(Source:entity, Destination:entity, EasingFunction:easing_function, Duration:float)<transacts><decides>:[]keyframed_movement_delta= var SourceTransform:(/Verse.org/SpatialMath:)transform = Source.GetGlobalTransform() var DestinationTransform:(/Verse.org/SpatialMath:)transform = Destination.GetGlobalTransform() array: keyframed_movement_delta: Transform := Utilities.GetDeltaTransform(SourceTransform, DestinationTransform) Duration := Duration Easing := EasingFunction
Esta función construye fotogramas clave individuales, pero necesitarás más lógica para construir animaciones completas.
Agrega una nueva función llamada
ConstructAndPlayAnimations()a tu definición de claseanimate_to_targets. Esta función toma una matriz de segmentos y el modo de reproducción de la animación y los utiliza para construir y reproducir una animación completa. Añade el modificador<suspends>a esta función para permitirle que se ejecute de forma asíncrona.Verse# Construct and play an animation from an array of animation segments. ConstructAndPlayAnimations<private>(InSegments:[]segment, AnimationPlayback:keyframed_movement_playback_mode)<suspends>:void=En
ConstructAndPlayAnimations(), define una nueva variablelógicallamada ShouldBreakOute inicialízala enfalse. Dados los tres modos de reproducción de movimiento por fotogramas clave, tendrás que controlar cada uno de forma individual. Utilizarás una expresiónlooppara crear animaciones de forma continua a fin de controlar los modos de bucle Ping Pong, pero el modo one-shot debería salir del loop desde la primera iteración. Comprueba si el modo de reproducción de la animación es de una toma y, de ser así, estableceShouldBreakOuten verdadero.Verse# Construct and play an animation from an array of animation segments. ConstructAndPlayAnimations<private>(InSegments:[]segment, AnimationPlayback:keyframed_movement_playback_mode)<suspends>:void= var ShouldBreakOut:logic = false # If this is a oneshot animation, break out of loop after it plays once. if (oneshot := oneshot_keyframed_movement_playback_mode[AnimationPlayback]): set ShouldBreakOut = trueLuego, en
if(expresión), obtén elkeyframed_movement_componentde la entidad en una variableKeyframedMovementComponent. Luego, obtén la transformación inicial de la animación en una variable denominadaStartingTransformmediante la obtención del primer elemento de la matrizInSegmentsy, luego, su transformación global.Verse# Position this entity in the correct starting position. if: KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component] StartingTransform := FirstSegment := InSegments[0].SegmentStartPosition?.GetGlobalTransform()Por último, posiciona la Entidad en su ubicación inicial al establecer su transformar global en la transformar inicial, y ponla en espera durante los
InitialPauseSecondsantes de que se reproduzca la animación.Verse# Position this entity in the correct starting position. if: KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component] StartingTransform := FirstSegment := InSegments[0].SegmentStartPosition?.GetGlobalTransform() then: Entity.SetGlobalTransform(StartingTransform) # Sleep for initial pause. Sleep(InitialPauseSeconds)Ahora es el momento de construir la matriz de fotogramas clave a partir de la cual construirás la animación. Primero, inicializa una nueva matriz variable de
keyframed_movement_deltadenominadaKeyframes. A continuación, en una expresiónfor, itera a través de cada segmento de la matrizInSegmentsy obtén el segmento y su índice en una variable local denominadaIndex.Verse# Build the array of keyframes to play the animation from. var Keyframes:[]keyframed_movement_delta = array{} for: Index -> Segment:InSegments SourceEntity := Segment.SegmentStartPosition? DestinationEntity := InSegments[Index + 1].SegmentStartPosition?Ahora, obtén la función de aceleración utilizada para este fotograma clave en una variable local llamada
Easingllamando aTryGetValueOrDefault(), pasandoSegment.EasingFunctionyDefaultEasingFunction. Además, obtén la duración del segmento de animación en una variable localDurationdeSegment.AnimationDuration. Con todos tus valores en su lugar, en una expresiónif, construye el fotograma clave pasando cada valor aConstructKeyframe[]y agrega el resultado a la matriz deFotogramas clave. Cuando todos tus fotogramas clave estén construidos, establece la matriz de fotogramas clave en el componente de movimiento de fotogramas clave llamando aSetKeyframes()pasando la matriz defotogramas clavey elPlaybackMode.Verse# Build the array of keyframes to play the animation from. var Keyframes:[]keyframed_movement_delta = array{} for: Index -> Segment:InSegments SourceEntity := Segment.SegmentStartPosition? DestinationEntity := InSegments[Index + 1].SegmentStartPosition? do: Easing := Utilities.TryGetValueOrDefault(Segment.EasingFunction, DefaultEasingFunction) Duration := Segment.AnimationDuration # Construct each keyframe and add it to the array.Con tu matriz de fotogramas clave definida, es hora de comenzar a reproducirlos. Tu animación debe ejecutarse en loop, pero debe detenerse después de la primera iteración si el mode de animación está definido en un solo disparo. También debe controlar la pausa en cada fotograma clave si el segmento tiene
PauseSeconds. Para controlar esto, define una expresiónloopcon una expresiónfordentro de ella. En la expresiónfor, itera a través de cada fotograma clave en la matrizKeyframesy, además, obtiene el índice de cada fotograma clave en una variableKeyframeIndex.VerseKeyframedMovementComponent.SetKeyframes(Keyframes, PlaybackMode) # Loop playing the animation from the keyframed_movement_component, pausing at each # keyframe for a specified duration. Will break out of the loop if the animation mode # is set to oneshot. loop: for(KeyframeIndex -> Keyframe:Keyframes):Dentro de la expresión
for, obtén el segmento asociado con este fotograma clave mediante la indexación en la matrizInSegmentsmedianteKeyframeIndex. Luego, si el segmento tienePauseSeconds, llama aSleep()durante ese tiempo. Después, llama aKeyframedMovementComponent.Play(), espera elKeyframeReachedEventy llama aKeyframedMovementComponent.Pause(). Lo que esto hace es que pausa la animación en cada fotograma clave durante una cantidad de tiempo dePauseSeconds, antes de reproducir y esperar al siguiente fotograma clave y pausar nuevamente. Finalmente, al final de la expresión debucle, comprueba siShouldBreakOutes verdadero y, de ser así, sal del bucle.Verse# Loop playing the animation from the keyframed_movement_component, pausing at each # keyframe for a specified duration. Will break out of the loop if the animation mode # is set to oneshot. loop: for(KeyframeIndex -> Keyframe:Keyframes): if: SegmentReached := InSegments[KeyframeIndex] then: Sleep(SegmentReached.PauseSeconds) KeyframedMovementComponent.Play()Tu función
ContstructAndPlayAnimations()completa debería verse de la siguiente manera:Verse# Construct and play an animation from an array of animation segments. ConstructAndPlayAnimations<private>(InSegments:[]segment, AnimationPlayback:keyframed_movement_playback_mode)<suspends>:void= var ShouldBreakOut:logic = false # If this is a oneshot animation, break out of loop after it plays once. if (oneshot := oneshot_keyframed_movement_playback_mode[AnimationPlayback]): set ShouldBreakOut = true # Position this entity in the correct starting position.
En el siguiente paso, crearás un prefabricado de tu entidad de animación y lo instanciarás en el nivel.