Dieses Tutorial ist eine fortgeschrittene Szenendiagramm-spezifische Version des Tutorials Animating Prop Movement. Wenn du mehr über die animationsbasierte Bewegung in UEFN außerhalb von Scene Graph erfahren möchtest, schau dir dieses Tutorial an und komm zurück zu diesem!
Bewegungsplattformen sind für Jump‘n‘Run-Spiele üblich und fordern den Spieler heraus, präzise Sprünge zwischen den Hindernissen zu machen, um das Ziel zu erreichen.
Es gibt verschiedene Möglichkeiten, wie du Props in UEFN verschieben kannst. Du kannst Funktionen wie TeleportTo[] oder MoveTo() nutzen, um eine Transformation direkt zu verändern, oder ein anderes Gerät wie einen Objektbeweger verwenden, um ein Prop an einen zuvor festgelegten Pfad zu verschieben. Animationen bieten eine weitere nützliche Option.
Animationen haben gegenüber der Bewegung der Transformation eines Props einige Vorteile. Animationen haben normalerweise eine glattere Bewegung als die Bewegung von Objekten mit MoveTo() oder TeleportTo(), da die Netzwerklatenz des Aufrufs dieser Funktionen bei jedem game tick vermieden wird.
Animationen verfügen außerdem über einheitlichere Kollisionen mit Spielern oder anderen Objekten und du hast im Vergleich zur Verwendung eines Prop Mover eine größere Kontrolle darüber, wo und wie sich Objekte bewegen. Du kannst Animationen in einer Schleife wiedergeben oder mit dem Ping-Pong-Modus vorwärts und rückwärts laufen lassen.
Bei Animationen kannst du auch einen Interpolationstyp wählen. Der Interpolationstyp bestimmt den Abschwächungstyp bzw. die Animationskurve, der deine Animation folgt. Der lineare Interpolationstyp gibt deine Animation z. B. mit einer konstanten Geschwindigkeit wieder, während der Ease-in-Typ langsam startet und dann zum Ende hin Geschwindigkeit aufnimmt.
Durch die Auswahl des richtigen Interpolationstyps für deine Animation kannst du an verschiedenen Punkten festlegen, ob sich das Prop verlangsamen, beschleunigen oder sich linear bewegen soll. Durch das Anwenden einer Komponente zur Implementierung dieser Verhaltensweisen auf Entitäten in deinem Level kannst du bewegliche Plattformen erstellen, über die deine Spieler navigieren können.
Überlege zunächst, welche Verhaltensweisen eine bewegliche Plattform ausführen können sollte. Sie sollte von einer gegebenen Startposition aus mit der Animation beginnen und sich dann zu mehreren Punkten bewegen. Wenn sie das Ende ihrer Bewegung erreicht, sollte sie entweder zu ihrer Startposition zurückkehren oder an Ort und Stelle bleiben können.
Sie sollte diese Bewegungen über eine bestimmte Dauer ausführen und sich an jedem Punkt ihrer Bewegung entsprechend drehen und die Skalierung anpassen können. Jede dieser Verhaltensweisen erfordert spezifischen Code, aber wenn man von einer einfachen Klasse ausgeht und sie ausbaut, kann man schnell verschiedene Ideen ausprobieren. Befolge diese Schritte, um eine animationsbasierte Bewegungskomponente zu erstellen.
Erstelle eine neue Verse-Komponente mit dem Namen
animate_to_targets_componentund öffne sie in Visual Studio Code. Weitere Informationen zum Erstellung deiner eigenen Verse-Komponenten findest du unter Creating Your Own Verse Component. Fügeusing-Anweisungen für die/Verse.org/SpatialMathhinzu,/Verse.org/SceneGraph/KeyframedMovement, und/UnrealEngine.com/Temporary/SpatialMath-Modulen zu unterscheiden. Du wirst später deren Funktionen benötigen.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):Die in diesem Abschnitt des Tutorials verwendeten Tooltips sind unten enthalten. Du kannst diese über deiner Klassendefinition
animate_to_targets_componentkopieren und einfügen.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."Füge die folgenden Felder zu deiner
animate_to_targets_component-Klassendefinition hinzu:Eine Float-Variable „editable_number“ mit dem Namen
InitialPauseSeconds. Dies ist die Zeitspanne, die vergeht, bevor die Entität mit der Animation beginnt. Setze dies auf10,0,damit die Entität zehn Sekunden wartet, bevor sie mit der Animation beginnt.
Verse# Amount of time to pause before the animation starts. @editable_number(float): ToolTip := SpeedTip MinValue := option{0.0} var InitialPauseSeconds<public>:float = 10.0Ein „editable“
Keyframed_movement_playback_modemit dem NamenAnimationPlaybackMode. Dies ist der Animationsmodus für die Animation der Entität. Lege diesen aufloop_keyframed_movement_playback_modefest. Das bedeutet, dass standardmäßig, wenn die Animation beendet ist, die Entität eine Schleife ausführt und ihre Animation wieder von vorne beginnt.
Verse# The playback mode used by this animation. @editable: ToolTip := PlaybackModeTip var PlaybackMode<public>:keyframed_movement_playback_mode = loop_keyframed_movement_playback_mode{}Speichere deinen Code und kompiliere ihn.
Aufteilen von Animationen in Segmente
Um Animationen im Code zu erstellen, verwendest du Keyframes. Animationen bestehen aus mindestens einem Keyframe und jeder Keyframe gibt die Werte eines Objekts an bestimmten Punkten in der Animation an. Durch das Erstellen einer Animation mit Keyframes kannst du mehrere Punkte festlegen, zu denen sich dein Prop bewegen, drehen und skalieren soll.
Keyframed_movement_component verwendet Keyframed_movement_delta als Keyframe-Typ. Diese Bewegungsdeltas haben drei Werte:
Transform: Gibt die Änderungen der Transformation relativ zum vorherigen Keyframe an.Dauer: Die Zeit in Sekunden, die der Keyframe in Anspruch nimmtEasing: Die Easing-Funktion, die bei der Wiedergabe dieses Keyframes verwendet werden soll.
Da keyframed_movement_component ein Array von Keyframes nimmt und diese dann abspielt, musst du alle Keyframes auf einmal für jede Animation, die du abspielen möchtest, bereitstellen. Es gibt dazu zwei Möglichkeiten:
Du kannst im Code ein Array mit mehreren Keyframes erstellen und es dann an die Keyframe-Bewegungskomponente übergeben, um eine einzelne Animation abzuspielen.
Du kannst mehrere Arrays erstellen, die jeweils einen einzelnen Keyframe enthalten, und diese einzeln an die Keyframe-Bewegungskomponente übergeben, um mehrere Animationen nacheinander abzuspielen.
Beide Optionen haben ihre Vor- und Nachteile. Mit Arrays von einzelnen Keyframes kannst du Vorgänge zwischen Keyframes einfacher durchführen, allerdings ist für die Verarbeitung mehr Code erforderlich. Das Erstellen aller Keyframes auf einmal macht die Verwaltung einfacher, aber es ist schwieriger, Operationen während der Animation durchzuführen. Sowohl das Tutorial Animieren von Objektbewegungen als auch dieses Tutorial decken den ersten Ansatz ab, aber eine Implementierung des zweiten Ansatzes wird auch bereitgestellt.
Um individuelle Keyframes im Code zu erstellen, definierst du eine segment-Klasse. Jedes Segment repräsentiert einen Keyframe, der von keyframed_movement_component verwendet wird, den du im Editor erstellen kannst. Du kannst auch zusätzliche Daten einschließen, z. B. die Zeit, die zwischen den einzelnen Keyframes gewartet wird. Folge den nachstehenden Schritten, um deine „segment“-Klasse zu erstellen.
Füge eine neue Klasse mit dem Namen
segmentzu deiner Dateianimate_to_targets_component.versehinzu. Füge den Bezeichner<concrete>hinzu, damit diese Klasse als@editable-Wert verwendet werden kann.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>:Füge die folgenden Felder zu deiner „segment“-Klassendefinition hinzu:
Eine bearbeitbare
Entitätsoption mit dem NamenSegmentStartPosition. Diese Entität dient als Referenz für die Weltposition, von der aus die Entität mit der Animation beginnen soll.
Verse# An entity that represents the starting position of this entity during the animation segement. @editable: ToolTip := SourceTip SegmentStartPosition:?entity = falseEin bearbeitbarer
Floatmit dem NamenAnimationDuration. Dies ist die Zeit, die dieses Animation-Segment zum Abspielen benötigt. Lege diesen Wert auf2,0fest, sodass die Wiedergabe jedes Animationssegments zwei Sekunden dauert.
Verse# The duration of the animation segment. @editable: ToolTip := AnimationDurationTip AnimationDuration:float = 2.0Eine bearbeitbare Option für die Easing-Funktion mit dem Namen
EasingFunction. Dies ist die Easing-Funktion, die in diesem Segment der Animation verwendet wird.
Verse# The easing function to use during this segment of animation. @editable: ToolTip := EasingFunctionTip EasingFunction:?easing_function = falseJede Easing-Funktion wird durch eine kubische Bézier-Kurve definiert, die aus vier Zahlen besteht, die die Art der Easing-Funktion bestimmen, die die Animation verwendet. Zum Beispiel verlangsamen die Parameter für eine Ease-in-Kurve die Animation zu Beginn und beschleunigen sie danach.
Die Parameter für eine lineare Kurve geben die Animation mit einer konstanten Geschwindigkeit wieder. Du kannst diese Werte selbst definieren, um deine eigenen benutzerdefinierten Animationskurven zu erstellen, aber in diesem Beispiel musst du das nicht, da du die im Modul
KeyframedMovementdefinierten verwenden wirst.Eine bearbeitbare Float-Option mit dem Namen
PauseSeconds. Dies ist die Zeit in Sekunden, die pausiert werden soll, bevor dieses Animationssegment gestartet wird. Du kannst dir das als die Zeit vorstellen, die eine Entität verweilt, bevor sie sich von jedem Punkt auf ihrem Pfad weiterbewegt.
Verse# The number of seconds to pause before starting this animation segment. @editable: ToolTip := PauseTip PauseSeconds:?float = falseFüge in deiner Klassendefinition
animate_to_targets_componentdie folgenden Felder hinzu:Ein bearbeitbares
segment-Array mit dem Namen Segments. Dies verweist auf jedes Segment der Animation, das die gesamte Animation ausmacht, durch die deine Entität läuft.
Verse# Segments of the keyframed movement animation. @editable: ToolTip := SegmentsTip var Segments<private>:[]segment = array{}Eine bearbeitbare
Easing-Funktionmit dem NamenDefaultEasingFunction. Wenn ein Animationssegment keine Easing-Funktion spezifiziert, wird dies als Standardfunktion verwendet. Lege dies aufassert_in_out_cupic_bezier_easing_functionfest.
Verse# Movement easing function between two targets @editable: ToolTip := DefaultEasingFunctionTip var DefaultEasingFunction<public>:easing_function = ease_in_out_cubic_bezier_easing_function{}Speichere deinen Code und kompiliere ihn. Im Editor solltest du sehen, dass das
Segments-Array auf Entitäten erscheint, die animate_to_targets_component angekoppelt haben.
Erstellen von Keyframes mit Code
Nachdem deine Segment-Klasse fertig ist, ist es Zeit, die Keyframes zu erstellen, die deine Segmente definieren. Du erstellst Keyframes einzeln und fügst sie zu einem Array hinzu und übergibst das Array dann an keyframed_movement_component. Dies erfordert einige mathematische Berechnungen, die du jetzt definieren wirst.
Da mathematische Operationen in verschiedenen Szenarien nützlich sind, ist es hilfreich, diese Logik in einer Utility-Datei zu platzieren, um von allen deinen Verse-Komponenten darauf zugreifen zu können. Weitere optimale Vorgehensweisen für Verse beim Arbeiten mit Entitäten findest du unter Creating Your Own Verse Component. Folge diesen Schritten, um deine Hilfsdatei zu erstellen:
Erstelle ein neues Modul in deiner
animate_to_targets.verse-Datei mit dem NamenUtilities. Dadurch wird eine allgemeine Logik gespeichert, die du in deinem Projekt verwenden wirst.Verse# Module containing utility functions. Utilities<public> := module:Füge einen neuen
vector3-Typ-Alias namensVectorOneszu deinem Utilities-Modul hinzu, der einenvector3erstellt, bei demLeft,UpundForwardalle auf1.0gesetzt sind. Dieser Vektor hilft dir später, einen Teil der mathematischen Berechnungen zu vereinfachen. Wenn du also einen Typ-Alias definierst, musst du nicht wiederholtvector3{Left := 1.0, Up := 1.0, Forward := 1.0}schreiben. Da du sowohl das Modul/Verse.org/SpatialMathimportiert hast als auch das Modul/UnrealEngine.com/Temporary/SpatialMath, musst du angeben, dass dies ein/Verse.org/SpatialMathvector3ist, da beide Module eine Definition dafür enthalten.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}Füge eine neue Funktion mit dem Namen
GetDeltaTransform()zu deinemUtilities-Modul hinzu. Diese Funktion berechnet die Differenzierung zwischen zwei Transformationen und gibt das Delta zurück. Füge den Modifikator<transacts>zu dieser Funktion hinzu, damit sie zurückgesetzt werden kann. Geben Sie/Verse.org/SpatialMathan als Modul für jedeTransformieren, da du die Differenz zwischen den Entitätstransformationen berechnen wirst.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=Initialisiere in
GetDeltaTransformein neues/Verse.org/SpatialMathtransform. Lege dieVerschiebungauf die Differenz zwischen der Verschiebung der einzelnen Transformationen fest. Lege dieDrehungauf das Ergebnis des Aufrufs vonMakeComponentWiseDeltaRotation()fest. Da sich diese Funktion im Modul/UnrealEngine.com/Temporary/SpatialMathbefindet, musst du von/Verse.org/SpatialMath- Drehungen in/UnrealEngine.com/Temporary/SpatialMath-Drehungen konvertieren. Das kannst du mit der FunktionFromRotation()tun. RufeMakeComponentWiseDeltaRotation()auf und übergib die Drehung jeder Transformation, nachdem du sie mitFromRotation()konvertiert hast. Dann konvertiere das Ergebnis dieses Funktionsaufrufs mitFromRotation()erneut, um es wieder in eine/Verse.org/SpatialMathzu konvertieren Drehung. Lege abschließend die Skalierung auf das Ergebnis des Addierens vonVectorOneszu der Differenz zwischen der ersten und zweiten Skalierung geteilt durch die erste Skalierung fest. Dadurch wird sichergestellt, dass deine Entität während der Animation korrekt skaliert wird. Deine vollständigeGetDeltaTransform()-Funktion sollte wie folgt aussehen: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)Füge abschließend eine Funktion namens
TryGetvalueOrDefault()zu deinemUtilities-Modul hinzu und füge dieser wiederum den<transacts>-Modifikator hinzu. Diese Funktion nimmt einenOptionswert eines bestimmten Typs und einen Standardwert desselben Typs und gibt entweder den Standardwert oder das Element innerhalb vonValuezurück, falls es existiert. Dies ist nützlich, wenn du überprüfen möchtest, ob ein Wert in einer Klasse tatsächlich initialisiert wurde, und garantiert, dass du einen Wert zurückgibst, wenn dies nicht der Fall ist. Prüfe inTryGetValueOrDefault(), obValueeinen Wert enthält, und gib ihn zurück. Andernfalls gibst duDefaultzurück. Dein vollständigesUtilities-Modul und die FunktionTryGetValurOrDefault()sollten wie folgt aussehen: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=
Nachdem deine mathematische Berechnung definiert ist, kannst du jetzt deine Keyframes aus Code erstellen!
Folge diesen Schritten, um deine Keyframe-Erstellungsfunktionen zu erzeugen:
Füge eine neue Funktion mit dem Namen
ConstructKeyframe()zu deiner Klassendefinitionanimate_to_targetshinzu. Diese Funktion benötigt eine Quell-Entität, eine Ziel-Entität, eine optionale Easing-Funktion und eine Dauer. Sie gibt sogar ein Array vonkeyframed_movements_deltazurück.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=Hole in
ConstructKeyframe()zunächst die Transformationen sowohl derQuell- als auch derZiel-Entitäten, indem duGetGlobalTransform()aufrufst.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()Initialisiere ein Array mit einem einzelnen Mitglied von
keyframed_movement_delta. Lege dieTransformationauf das Ergebnis des Aufrufs vonGetDeltaTransform()fest, übergebe die Quell- und Zieltransformationen und lege dieDauerund dasEasingauf die an diese Funktion übergebenen Werte fest. Deine vollständigeConstructKeyframe()-Funktion sollte wie folgt aussehen: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
Diese Funktion erstellt einzelne Keyframes, aber du benötigst mehr Logik, um vollständige Animationen zu erstellen.
Füge eine neue Funktion mit dem Namen
ConstructAndPlayAnimations()zu deiner Klassendefinitionanimate_to_targetshinzu. Diese Funktion nimmt ein Array von Segmenten und den Animationswiedergabemodus und verwendet sie, um eine vollständige Animation zu erstellen und abzuspielen. Füge den Modifikator<suspends>zu dieser Funktion hinzu, damit sie asynchron ausgeführt werden kann.Verse# Construct and play an animation from an array of animation segments. ConstructAndPlayAnimations<private>(InSegments:[]segment, AnimationPlayback:keyframed_movement_playback_mode)<suspends>:void=Definiere in
ConstructAndPlayAnimations()eine neueLogikvariablemit dem NamenShouldBreakOutund initialisiere sie mitFalse. Bei den drei Keyframe-Bewegungswiedergabemodi musst du jeden einzeln behandeln. Du verwendest einenSchleifenausdruck, um fortlaufend Animationen zu erstellen, um die Ping-Pong-Schleifenmodi zu verarbeiten, aber der One-Shot-Modus sollte aus der Schleife der ersten Iteration ausbrechen. Prüfe, ob der Animations-Wiedergabemodus der One-Shot-Modus ist, und wenn ja, setzeShouldBreakOutauf True.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 = trueHole als Nächstes in einem
if-Ausdruck diekeyframed_movement_componentder Entität in einer VariableKeyframedMovementComponent. Hole dann die Start-Transformation der Animation in einer Variable namensStartingTransform, indem du das erste Element imInSegments-Array holst und dann dessen globale Transformation.Verse# Position this entity in the correct starting position. if: KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component] StartingTransform := FirstSegment := InSegments[0].SegmentStartPosition?.GetGlobalTransform()Schließlich positionierst du die Entität an ihrer Startposition, indem du ihre globale Transformation auf die Starttransformation festlegst und versetze sie für die Dauer von
InitialPauseSecondsin den Ruhezustand, bevor die Animation abgespielt wird.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)Jetzt ist es Zeit, das Array von Keyframes zu erstellen, aus dem du die Animation aufbauen wirst. Initialisiere zunächst ein neues Variablen-Array von
keyframed_movement_deltamit dem NamenKeyframes. Iteriere als Nächstes in einemfor-Ausdruck durch jedes Segment imInSegments-Array und hole sowohl das Segment als auch seinen Index in einer lokalen Variable namensIndex.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?Hole nun die
Dämpfungsfunktion,die für diesen Keyframe verwendet wird, in einer lokalenVariable namens Easing,indemdutryGetValueOrDefault()aufrufst und Segment.EasingFunctionund DefaultEasingFunctionübergibst. Hole außerdem die Dauer des Animationssegments in einer lokalen VariablenDurationausSegment.AnimationDuration. Wenn alle deine Werte vorhanden sind, konstruiere in einemif-Ausdruck den Keyframe, indem du jeden Wert anConstructKeyframe[]übergibstund das Ergebnis demKeyframes-Array hinzufügst. When alle deine Keyframes erstellt sind, setzt du das Array von Keyframes auf die Keyframe-Bewegungskomponente, indem duSetKeyframes()aufrufst und dasKeyframes-Array und denPlaybackModeübergibst.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.Wenn dein Keyframes-Array gesetzt ist, ist es an der Zeit, sie abzuspielen. Deine Animation muss in einer Schleife laufen, sollte aber nach der ersten Iteration stoppen, wenn der Animationsmodus auf One-Shot eingestellt ist. Es muss auch das Pausieren bei jedem Keyframe verarbeiten, wenn das Segment
PauseSecondshat. Um dies zu verarbeiten, richte einenSchleifenausdruckmit einemfor-Ausdruck darin ein. Imfor-Ausdruck wird jeder Keyframe imKeyframes-Array durchlaufen und zusätzlich der Index jedes Keyframes in einer VariablenKeyframeIndexabgerufen.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):Innerhalb des
for-Ausdrucks rufst du das Segment ab, das diesem Keyframe zugeordnet ist, indem du mitKeyframeIndexin das ArrayInSegmentsindizierst. Wenn das Segment dannPauseSecondshat, rufeSleep()für diese Zeit auf. Rufe danachKeyframedMovement.Play()auf, warte anschließend auf dasKeyframeReachedEventund rufeKeyframedMovement.Pause()auf. Dadurch wird die Animation bei jedem Keyframe für eine Zeitspanne vonPauseSecondspausiert, bevor sie abgespielt wird, auf den nächsten Keyframe gewartet und erneut pausiert wird. Schließlich prüfe am Ende desSchleifenausdrucks, obShouldBreakOuttrue ist, und wenn ja, brich die Schleife ab.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()Deine fertige Funktion
ContstructAndPlayAnimations()sollte wie folgt aussehen: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.
Im nächsten Schritt erstellst du ein Prefab deiner animierten Entität und instanziierst es im Level!