Questo tutorial è una versione avanzata del tutorial Animazione del movimento degli oggetti scenografici, specifica per il Scene Graph. Se vuoi saperne di più sul movimento basato sulle animazioni in UEFN al di fuori di Scene Graph, dai un'occhiata al relativo tutorial e poi torna a questo!
Le piattaforme mobili sono comuni alla maggior parte dei giochi platform e sfidano il giocatore a fare salti precisi tra i bersagli per raggiungere l'obiettivo.
Esistono diversi modi per muovere gli oggetti scenografici in UEFN. Puoi utilizzare funzioni come TeleportTo[] o MoveTo() per modificare direttamente una trasformazione o utilizzare un altro dispositivo come un motore di oggetti scenografici per spostare un oggetto scenografico su un percorso preimpostato. Tuttavia, esiste un'altra opzione utile sotto forma di animazioni.
Le animazioni offrono un paio di vantaggi rispetto al movimento della trasformazione di un oggetto scenografico. Le animazioni hanno di solito un movimento più fluido rispetto allo spostamento di oggetti con MoveTo() o TeleportTo() perché evitano la latenza di rete dovuta alla necessità di chiamare queste funzioni a ogni tick di gioco.
Le animazioni offrono anche collisioni più coerenti con i giocatori o altri oggetti e disporrai di un maggior livello di controllo su dove e come si muove un oggetto rispetto all'utilizzo di un dispositivo Traslatore oggetti. Puoi riprodurre le animazioni in loop o mandarle avanti e indietro con la modalità ping-pong.
Le animazioni ti permettono anche di scegliere un tipo di interpolazione. Il tipo di interpolazione determina il tipo di attenuazione, o curva di animazione, che l'animazione segue. Ad esempio, il tipo di interpolazione lineare riproduce l'animazione a una velocità costante, mentre il tipo ease-in inizia lentamente per poi accelerare verso la fine.
Scegliendo il tipo giusto di interpolazione per la tua animazione, puoi specificare in diversi punti se l'oggetto scenografico deve rallentare, accelerare o muoversi linearmente. Applicando un componente per implementare questi comportamenti alle entità del tuo livello, puoi creare piattaforme mobili su cui i tuoi giocatori possono spostarsi.
Innanzitutto, considera che tipo di comportamenti dovrebbe essere in grado di eseguire una piattaforma mobile. Deve iniziare ad animare da una determinata posizione iniziale, quindi spostarsi in più punti. Quando raggiunge la fine del movimento, deve essere in grado di tornare alla posizione iniziale o di rimanere al suo posto.
Deve eseguire questi movimenti per una durata specifica ed essere in grado di ruotare e scalare in modo appropriato in ogni punto del percorso. Ognuno di questi comportamenti richiede un codice specifico da realizzare, ma partendo da una classe semplice e costruendo puoi iterare rapidamente in base a idee diverse. Segui questi passaggi per creare un componente di movimento basato su animazione.
Crea un nuovo componente Verse denominato
animate_to_targets_componente aprilo in Visual Studio Code. Per ulteriori informazioni sulla creazione dei tuoi componenti Verse, consulta Creazione del proprio componente Verse. Aggiungi le affermazioniusingper/Verse.org/SpatialMath,/Verse.org/SceneGraph/KeyframedMovement, e/UnrealEngine.com/Temporary/SpatialMath. Ti serviranno delle funzioni di ognuno di questi più avanti.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):I tooltip utilizzati in questa sezione del tutorial sono inclusi di seguito. Puoi copiarli e incollarli sopra la definizione della tua classe
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."Aggiungi i seguenti campi alla definizione della tua classe
animate_to_targets_component:Una variabile float con numero modificabile denominata
InitialPauseSeconds. Questa è la quantità di tempo necessaria prima che l'entità inizi l'animazione. Impostala su10.0in modo che l'entità attenda dieci secondi prima di iniziare ad animare.
Verse# Amount of time to pause before the animation starts. @editable_number(float): ToolTip := SpeedTip MinValue := option{0.0} var InitialPauseSeconds<public>:float = 10.0keyframed_movement_playback_modemodificabile denominataAnimationPlaybackMode. Questa è la modalità di animazione dell'entità. Impostala suloop_keyframed_movement_playback_mode. Ciò significa che, per impostazione predefinita, quando l'animazione è terminata, l'entità esegue un loop e ricomincia l'animazione dall'inizio.
Verse# The playback mode used by this animation. @editable: ToolTip := PlaybackModeTip var PlaybackMode<public>:keyframed_movement_playback_mode = loop_keyframed_movement_playback_mode{}Salva il codice e compilalo.
Suddivisione delle animazioni in segmenti
Per compilare le animazioni nel codice, utilizzerai i keyframe. Le animazioni sono costituite da uno o più keyframe e ciascuno di essi specifica i valori di un oggetto in determinati punti dell'animazione. Costruendo un'animazione tramite keyframe, puoi specificare più punti in cui il tuo oggetto scenografico si muove, ruota e si ridimensiona.
Il keyframed_movement_component utilizza un keyframed_movement_delta come tipo di keyframe. Questi delta di movimento hanno tre valori:
Trasformazione: specifica le modifiche della trasformazione rispetto al keyframe precedente.Durata: la quantità di tempo in secondi impiegata dal keyframe.Alleggerimento: la funzione di alleggerimento da utilizzare durante la riproduzione di questo keyframe.
Poiché keyframed_movement_component prende un array di keyframe e poi lo riproduce, devi fornire tutti i keyframe contemporaneamente per ogni animazione che vuoi riprodurre. Ci sono due modi per farlo:
Potresti creare un array di più keyframe nel codice, quindi passarlo al componente di movimento con keyframe e riprodurre una singola animazione.
Puoi creare più array contenenti un singolo keyframe e passarli singolarmente al componente di movimento con keyframe per riprodurre più animazioni in sequenza.
Entrambe queste opzioni hanno dei compromessi. Gli array di singoli keyframe consentono di eseguire più facilmente le operazioni tra keyframe, ma richiedono più codice da gestire. La costruzione di tutti i keyframe in una volta rende la gestione più facile, ma è più difficile eseguire operazioni durante la riproduzione dell'animazione. Sia il tutorial sull'animazione del movimento dell'oggetto scenografico che questo tutorial trattano il primo approccio, ma sarà fornita anche un'implementazione del secondo approccio.
Per compilare singoli keyframe nel codice, definirai una classe di segmento. Ogni segmento rappresenterà un keyframe utilizzato dal keyframed_movement_component che puoi compilare dall'editor. Potrai anche includere dati supplementari, come ad esempio la quantità di tempo di attesa tra un keyframe e l'altro. Segui i passaggi seguenti per compilare la tua classe di segmento.
Aggiungi una nuova classe denominata
segmentoal fileanimate_to_targets_component.verse. Aggiungi lo specificatore<concrete>per permettere l'utilizzo di questa classe come valore@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>:Aggiungi i seguenti campi alla definizione della classe di segmento:
Opzione di
entitàmodificabile denominataSegmentStartPosition. Questa entità fungerà da riferimento per la posizione nel mondo da cui l'entità deve iniziare l'animazione.
Verse# An entity that represents the starting position of this entity during the animation segement. @editable: ToolTip := SourceTip SegmentStartPosition:?entity = falseFloatmodificabile denominatoAnimationDuration. Questa è la quantità di tempo necessaria per la riproduzione di questo segmento di animazione. Impostalo su2.0in modo che ciascun segmento di animazione richieda due secondi per essere riprodotto.
Verse# The duration of the animation segment. @editable: ToolTip := AnimationDurationTip AnimationDuration:float = 2.0Opzione della funzione di alleggerimento modificabile denominata
EasingFunction. Questa è la funzione di alleggerimento utilizzata durante questo segmento dell'animazione.
Verse# The easing function to use during this segment of animation. @editable: ToolTip := EasingFunctionTip EasingFunction:?easing_function = falseOgni funzione di alleggerimento è definita da una curva di Bézier cubica, che consiste in quattro numeri che creano il tipo di funzione di alleggerimento utilizzata dall'animazione. Ad esempio, i parametri di una curva ease-in fanno sì che l'animazione rallenti all'inizio e acceleri successivamente.
I parametri di una curva lineare fanno sì che l'animazione venga riprodotta a velocità costante. Puoi definire questi valori tu stesso per creare le tue curve di animazione personalizzate, ma in questo esempio non è necessario poiché utilizzerai quelli definiti nel modulo
KeyframedMovement.Opzione float modificabile denominata
PauseSeconds. questa è la quantità di tempo in secondi di pausa prima di avviare questo segmento di animazione. Puoi considerarlo come la quantità di tempo in cui un'entità si ferma prima di proseguire da ciascun punto lungo il suo percorso.
Verse# The number of seconds to pause before starting this animation segment. @editable: ToolTip := PauseTip PauseSeconds:?float = falseTornando alla definizione della classe
animate_to_targets_component, aggiungi i seguenti campi:Array modificabile di
segmentodenominato Segments. Questo farà riferimento a ogni segmento dell'animazione che costituisce l'animazione complessiva attraverso cui è eseguita l'entità.
Verse# Segments of the keyframed movement animation. @editable: ToolTip := SegmentsTip var Segments<private>:[]segment = array{}Una
funzione di alleggerimentomodificabile denominataDefaultEasingFunction. Se un segmento di animazione non specifica una funzione di alleggerimento, questa sarà quella utilizzata in modo predefinito. Impostala suease_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{}Salva il codice e compilalo. Nell'editor, dovresti vedere l'array
Segmentsapparire su entità a cui è allegato animate_to_targets_component.
Compilazione di keyframe con il codice
Una volta completata la classe del segmento, è il momento di costruire i keyframe definiti dai segmenti. Compilerai i keyframe singolarmente e li aggiungerai a un array, quindi passerai l'array al tuo keyframed_movement_component. Ciò richiederà un po' di matematica, che ora definirai.
Poiché le operazioni matematiche sono utili in una varietà di scenari, è utile inserire tale logica in un file di utilità per accedervi da qualsiasi tuo componente Verse. Per altre buone prassi di Verse quando si utilizzano le entità, vedi Creazione del proprio componente Verse. Segui questa procedura per creare il tuo file di utilità:
Crea un nuovo modulo nel file
animate_to_targets.versedenominatoUtilità. l'operazione servirà a memorizzare la logica comune che utilizzerai in tutto il progetto.Verse# Module containing utility functions. Utilities<public> := module:Aggiungi un nuovo alias di tipo
vector3denominatoVectorOnesal modulo Utilities che crea unvector3in cuiLeft,UpeForwardsono tutti impostati su1.0. Utilizzerai questo vettore in seguito per semplificare alcuni calcoli, quindi definendo il relativo alias tipo non dovrai scrivere ripetutamentevector3{Left := 1.0, Up := 1.0, Forward := 1.0}. Poiché hai importato sia/Verse.org/SpatialMathe/UnrealEngine.com/Temporary/SpatialMathmoduli, dovrai specificare che si tratta di un/Verse.org/SpatialMathvector3poiché entrambi i moduli includono una definizione per esso.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}Aggiungi una nuova funzione denominata
GetDeltaTransform()al moduloUtilities. Questa funzione calcolerà la differenza fra due trasformazioni e restituirà il delta. Aggiungi il modificatore<transacts>a questa funzione per consentirne il rollback. Specifica/Verse.org/SpatialMathcome Modulo per ogniTrasformapoiché c'è da calcolare la differenza tra le Trasforma di Entità.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=In
GetDeltaTransform, inizializza un nuovo/Verse.org/SpatialMathtrasforma. Imposta laTraslazionesulla differenza fra le traslazioni di ciascuna trasformazione. Imposta laRotazionesul risultato della chiamata aMakeComponentWiseDeltaRotation(). Poiché questa funzione si trova nel modulo/UnrealEngine.com/Temporary/SpatialMathche ti servirà per la conversione da rotazioni/Verse.org/SpatialMatha rotazioni/UnrealEngine.com/Temporary/SpatialMath. Puoi farlo utilizzando la funzioneFromRotation(). ChiamaMakeComponentWiseDeltaRotation()passando la rotazione di ogni trasformazione dopo averla convertita conFromRotation(). Quindi converti il risultato di questa chiamata di funzione utilizzando di nuovoFromRotation()per riconvertire in un/Verse.org/SpatialMathrotazione. Infine, imposta la Scala sul risultato dell'aggiunta diVectorOnesalla differenza tra la prima e la seconda scala divisa per la prima scala. Ciò garantisce che l'entità sia ridimensionata correttamente durante l'animazione. La funzioneGetDeltaTransform()completa deve avere le seguenti caratteristiche: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)Infine, aggiungi una funzione denominata
TryGetvalueOrDefault()al moduloUtilitàe aggiungi ad esso il modificatore<transacts>. Questa funzione prende il valore di un'opzionedi un certo tipo e un valore predefinito dello stesso tipo e restituisce il valore predefinito o l’oggetto all'interno diValue, se esiste. È utile quando si desidera verificare se un valore in una classe è effettivamente inizializzato e garantisce di restituirlo in parte in caso contrario. All'interno diTryGetValueOrDefault(), controlla seValuecontiene un valore e restituiscilo. In caso contrario, restituisceDefault. Il moduloUtilitiescompleto e la funzioneTryGetValueOrDefault()devono avere le seguenti caratteristiche: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 volta definita la matematica, ora puoi compilare i tuoi keyframe dal codice!
Segui questi passaggi per compilare le tue funzioni di creazione dei keyframe:
Aggiungi una nuova funzione denominata
ConstructKeyframe()alla definizione della classeanimate_to_targets. La funzione accetta un'entità di origine, un'entità di destinazione, una funzione di alleggerimento opzionale e una durata. Restituisce anche un array dikeyframed_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=In
ConstructKeyframe(), ottieni prima le trasformazioni di entrambe le entità diOrigineeDestinazioenchiamandoGetGlobalTransform().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()Inizializza un array con un singolo membro di
keyframed_movement_delta. ImpostaTrasformazionesul risultato della chiamata aGetDeltaTransform()passando le trasformazioni di origine e destinazione e impostaDurataeAttenuazionesui valori passati a questa funzione. La funzioneConstructKeyframe()completa deve avere le seguenti caratteristiche: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
Questa compila singoli keyframe, ma avrai bisogno di più logica per costruire animazioni complete.
Aggiungi una nuova funzione denominata
ConstructAndPlayAnimations()alla definizione della classeanimate_to_targets. Questa funzione prende un array di segmenti e la modalità di riproduzione dell'animazione e li utilizza per costruire e riprodurre un'animazione completa. Aggiungi il modificatore<suspends>a questa funzione per consentirne un'esecuzione asincrona.Verse# Construct and play an animation from an array of animation segments. ConstructAndPlayAnimations<private>(InSegments:[]segment, AnimationPlayback:keyframed_movement_playback_mode)<suspends>:void=In
ConstructAndPlayAnimations(), definisci una nuova variabilelogicadenominataShouldBreakOute inizializzala sufalso. Date le tre modalità di riproduzione del movimento con keyframe, dovrai gestire ognuna di esse singolarmente. Utilizzerai un'espressioneloopper costruire continuamente animazioni per gestire le modalità di loop ping pong, ma la modalità one-shot dovrebbe uscire dal loop della prima iterazione. Controlla se la modalità di riproduzione dell'animazione è la modalità one-shot e, in caso affermativo, impostaShouldBreakOutsu vero.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 = trueSuccessivamente, in un'espressione
if, ottienikeyframed_movement_componentdell'entità in una variabileKeyframedMovementComponent. Quindi ottieni la trasformazione iniziale dell'animazione in una variabile denominataStartingTransformdeterminando il primo elemento nell'arrayInSegments, quindi la sua trasformazione globale.Verse# Position this entity in the correct starting position. if: KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component] StartingTransform := FirstSegment := InSegments[0].SegmentStartPosition?.GetGlobalTransform()Infine, posiziona l'entità nella sua posizione iniziale impostandone la trasformazione globale sulla trasformazione iniziale e sospendi per
InitialPauseSecondsprima della riproduzione dell'animazione.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)Ora è il momento di compilare l'array di keyframe da cui costruirai l'animazione. Innanzitutto, inizializza un nuovo array della variabile di
keyframed_movement_deltadenominatoKeyframes. A questo punto, in un'espressionefor, itera attraverso ciascun segmento dell'arrayInSegments, ottenendo sia il segmento che il relativo indice in una variabile locale denominataIndex.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?Ora, ottieni la funzione di andamento utilizzata per questo keyframe in una variabile locale denominata
EasingchiamandoTryGetValueOrDefault(), passando laSegment.EasingFunctione laDefaultEasingFunction. Inoltre, ottieni la durata del segmento di animazione in una variabile localeDurationdaSegment.AnimationDuration. Una volta impostati tutti i valori, in un'espressioneifcostruisci il keyframe passando ogni valore aConstructKeyframe[]e aggiungi il risultato all'arrayKeyframes. Quando tutti i keyframe sono stati compilati, imposta l'array di keyframe sul componente di movimento con keyframe chiamandoSetKeyframes()passando l'arrayKeyframese ilPlaybackMode.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.Una volta impostato l'array di keyframe, è ora di iniziare a riprodurli. L'animazione deve essere eseguita in loop, ma deve interrompersi dopo la prima iterazione se la modalità di animazione è impostata su una ripresa. Deve anche gestire le pause a ogni keyframe se il segmento ha
PauseSeconds. Per gestire questo problema, imposta un'espressioneloopcon un'espressioneforal suo interno. Nell'espressionefor, scorri ogni keyframe nell'arrayKeyframes, ottenendo inoltre l'indice di ogni keyframe in una variabileKeyframeIndex.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):All'interno dell'espressione
for, ottieni il segmento associato a questo keyframe eseguendo l'indicizzazione nell'arrayInSegmentsutilizzandoKeyframeIndex. Quindi, se il segmento ha unPauseSeconds, chiamaSleep()per quel periodo di tempo. Successivamente, chiamaKeyframedMovementComponent.Play(), quindi attende l'eventoKeyframeReachedEvente chiamaKeyframedMovementComponent.Pause(). Ciò che fa è mettere in pausa l'animazione a ogni keyframe per un periodo di tempoPauseSeconds, prima di riprodurla e attendere il keyframe successivo per poi fare una nuova pausa. Infine, alla fine dell'espressioneloop, controlla seShouldBreakOutè vero e, in caso affermativo, interrompi il loop.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()La funzione completa
ContstructAndPlayAnimations()deve essere simile alla seguente: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.
Nel passaggio successivo, creerai un prefab della tua entità di animazione e lo istanzierai nel livello!