The step above objects that move, rotate, or scale is doing all three concurrently. However, there are some important challenges to consider when building animations for props that move in multiple ways at the same time.
Since the animation controller can only play one animation at a time, you can’t do moving, rotating, and scaling in separate animations. These animations also need multiple keyframes, since you may want to rotate a prop multiple times per animation. Because you need to build all the keyframes ahead of time, you also need to calculate the position, rotation, and scale at each point in the prop’s journey. What happens if your prop doesn’t make an even amount of rotations? How do you handle half a rotation?
There’s a lot of math involved in this next section, but by the end, you’ll be able to move, rotate, and scale props to multiple points, and create complex, dynamic, and (most important!) fun platforming challenges to make the Fall Guys course of your dreams.
Building Animations that Move, Rotate, and Scale
Follow these steps to start putting things together:
Create a new Verse class named
animating_propthat inherits frommovable_propusing Verse Explorer. Add the<concrete>specifier to this class to expose its properties to UEFN.Verse# A prop that translates, rotates, and scales to a destination using animation. animating_prop<public> := class<concrete>(movable_prop):Add the
using { /Fortnite.com/Devices/CreativeAnimation }andusing { /UnrealEngine.com/Temporary/SpatialMath }statements to the top of the file to import these modules. You’ll need these to animate your prop. The tooltips used in this section are also included here.Verseusing { /Fortnite.com/Devices } using { /Fortnite.com/Devices/CreativeAnimation } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/SpatialMath } RotationRateTip<localizes>:message := "The time it takes to make one AdditionalRotation in seconds." UseEasePerKeyframeTip<localizes>:message := "Whether this prop should use the MoveEaseType for each keyframe. False will use the Linear ease type on each frame." # A prop that translates, rotates, and scales to a destination using animation. animating_prop<public> := class<concrete>(movable_prop):At the top of the
animating_propclass definition, add the following fields:An editable
rotationnamedAdditionalRotation. This is the rotation to apply to theRootPropper keyframe.Verse# The additional rotation to apply to the RootProp per keyframe. @editable {ToolTip := AdditionalRotationTip} AdditionalRotation:rotation = rotation{}An editable
floatnamedRotationRate. This is the amount of time it takes to make oneAdditionalRotation, in seconds.Verse# The time it takes to make one AdditionalRotation in seconds. @editable {ToolTip := RotationRateTip} var RotationRate:float = 1.0An editable
logicnamedUseEasePerKeyFrame. This dictates whether each keyframe uses theMoveEaseTypefor interpolation. In this example, setting this tofalsewill default to using the linear interpolation type for each frame.Verse# Whether this prop should use the MoveEaseType per each frame of animation. # Setting this to false will use the linear MoveEaseType on each frame. @editable UseEasePerKeyframe:logic = trueAn editable array of
creative_propnamedMoveTargets. These are the different Creative props your root prop will travel to.Verse# The Creative prop target for the RootProp to move toward. @editable {ToolTip := MoveTargetsTip} var MoveTargets:[]creative_prop = array{}A variable
transformnamedTargetTransform. This is the transform your root prop is currently traveling to.Verse# The transform the prop is currently targeting. var TargetTransform:transform = transform{}
Your class definition should look like this:
Verseusing { /Fortnite.com/Devices } using { /Fortnite.com/Devices/CreativeAnimation } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/SpatialMath } RotationRateTip<localizes>:message := "The time it takes to make one AdditionalRotation in seconds." UseEasePerKeyframeTip<localizes>:message := "Whether this prop should use the MoveEaseType for each keyframe. False will use the Linear ease type on each frame." # A prop that translates, rotates, and scales to a destination using animation. animating_prop<public> := class<concrete>(movable_prop):Override the
Move()function in youranimating_propclass. Then in aforexpression, iterate through eachMoveTargetin theMoveTargetsarray. Check if eachMoveTargetis valid, and if so set theTargetTransformto the transform of theMoveTarget.Verse# Move and rotate the RootProp toward the MoveTarget, or MoveTransform if one is set. Move<override>()<suspends>:void= # Move to each target in the MoveTargets array. for: MoveTarget:MoveTargets do: # Set the TargetTransformto the MoveTarget if the # MoveTarget is set. Otherwise set it to the MoveTransform.Back in your
movement_behaviorsfile, add a new method namedBuildMovingAnimationKeyframes(). This function will build and return an array of keyframes that animate a prop moving and rotating to a target transform. This function takes several parameters fromanimating_prop— theMoveDuration,RotationRate,AdditionalRotation, theOriginalTransform(the starting transform of the prop), theTargetTransform, theMoveEaseType, and a newlogicvalue calledUseEasePerKeyframe. This determines whether you use theMoveEaseTypefor each keyframe. Your function signature should look like this:Verse# Builds an array of keyframes that animate movement and rotation from the OriginalTransform to the TargetTransform. BuildMovingAnimationKeyframes(MoveDuration:float, RotationRate:float, AdditionalRotation:rotation, OriginalTransform:transform, TargetTransform:transform,MoveEaseType:move_to_ease_type, UseEasePerKeyframe:logic):[]keyframe_delta=In
BuildMovingAnimationKeyframes(), initialize the following variables:A variable array of
keyframe_deltanamedKeyframes. This is the array you’ll return at the end.Verse# The array of keyframes to return. var KeyFrames:[]keyframe_delta = array{}A variable
floatnamedTotalTime. This is the total amount of time spent animating so far.Verse# The total amount of time spent animating. var TotalTime:float = 0.0Two
transformvariables namedStartTransformandEndTransform. These are the starting and ending transforms of the prop at the start and end of each keyframe. Initialize both to theOriginalTransform.Verse# The starting transform for building keyframes. This is the # transform of the RootProp at the start of each keyframe. var StartTransform:transform = OriginalTransform # The ending transform for building keyframes. This is the # transform of the RootProp at the end of each keyframe. var EndTransform:transform = OriginalTransformA variable
rotationnamedRotationToApply, initialized to theAdditionalRotation. This is the actual rotation you’ll apply to the prop for each keyframe. Usually, this will be theAdditionalRotation, but if you need to make a fractional rotation you’ll change this value.Verse# The actual rotation to apply to the RootProp. Usually this is the # AdditionalRotation, but will change in cases with fractional rotations. var RotationToApply:rotation = AdditionalRotationA variable
floatnamedAnimationTime. This is the amount of time in seconds each keyframe takes. Initialize this to1.0 / RotationRate, since the RootProp needs to make aRotationRatenumber of rotations per second.Verse# The time it takes for each keyframe of animation to complete. # This is initialized to 1.0/Rotation rate since the RootProp needs to make a # RotationRate number of rotations per second. var AnimationTime:float = 1.0 / RotationRateA
floatvalue namedTotalRotations. This is the total number of rotations to make across the entire animation, and is initialized toMoveDuration * RotationRate. The reason this is afloatand not anintis to deal with situations where you don’t need to make a full rotation, such as at the end of an animation.Verse# The total number of rotations to make. TotalRotations:float = MoveDuration * RotationRateA
floatvalue namedTimePerRotation. This is the amount of time in seconds it takes one rotation to complete. Your function should now look like this.Verse# Builds an array of keyframes that animate movement and rotation from the OriginalTransform to the TargetTransform. BuildMovingAnimationKeyframes(MoveDuration:float, RotationRate:float, AdditionalRotation:rotation, OriginalTransform:transform, TargetTransform:transform,MoveEaseType:move_to_ease_type, UseEasePerKeyframe:logic):[]keyframe_delta= # The array of keyframes to return. var KeyFrames:[]keyframe_delta = array{} # The total amount of time spent animating. var TotalTime:float = 0.0 # The starting transform for building keyframes. This is the
That’s a lot of values to keep track of, so let’s do an example calculation, using 2.5 as the RotationRate and 5.0 as the MoveDuration.
Rotation Rate = 2.5 rotations/second
Move Duration = 5.0 seconds
Animation Time =
1.0 seconds/Rotation Rate =
1.0/2.5 = 0.4 seconds
Total Rotations =
Move Duration /Rotation Rate =
With a RotationRate of 2.5 and a MoveDuration of 5.0, you’ll make 12.5 rotations in total, with each rotation taking 0.4 seconds. This means you’ll have to make an extra half-rotation at the end of the animation. You might also notice that the animation time and time per rotation are the same. This is almost always the case, except when you need to make less than a full rotation. Even though they’re initially the same value, you’ll need to keep track of both variables to handle some later math.
Building Keyframes on a Loop
It’s time to get building! Follow the steps below to set up the loop that builds your keyframes.
Add a
loopexpression toBuildMovingAnimationKeyframes(). On each iteration of the loop, you’ll build a new keyframe and add it to the keyframes array. At the start of the loop, update theTotalTimewith theTimePerRotation.Verse# Build each keyframe of animation and add it to the Keyframes array. # The loop breaks when the TotalTime goes past the MoveDuration. loop: # Add the TimePerRotation to the TotalTime. set TotalTime += TimePerRotationBuild the
EndTransform, which is where the prop should be at the end of this keyframe. Set theEndTransformto a new transform with the following parameters:Set the
Translationto the result of callingLerp()between theOriginalTransformand theTargetTransform. TheLerp()function takes two values and a Lerp ratio between0.0and1.0. It then generates a new value somewhere between the two based on the Lerp ratio. The closer the Lerp ratio is to 1.0, the closer the transform will be to theTargetTransform, and vice versa.Verse# Build the ending transform for the RootProp to move to. set EndTransform = transform: # Use Lerp() to find how far between the StartingTransform and the TargetTransform the RootProp should translate. # Do the same for scale, and rotate the starting transform by the RotationToApply. # The Lerp Parameter is based on the total number of rotations since the RootProp should guarantee that it makes # at least that many rotations over the whole animation. Translation := Lerp(OriginalTransform.Translation, TargetTransform.Translation, (TotalTime * RotationRate) / (TotalRotations))Set the
Rotationto the result ofMakeShortestRotationBetween(), passing the rotation of the original transform and the original transform rotated by theRotationToApply.VerseTranslation := Lerp(OriginalTransform.Translation, TargetTransform.Translation, (TotalTime * RotationRate) / (TotalRotations)) Rotation := MakeShortestRotationBetween(OriginalTransform.Rotation, OriginalTransform.Rotation.RotateBy(RotationToApply))~~~Set the
Scaleto the result of callingLerp()between theOriginalTransform.Scaleand theTargetTransform.Scale. Keep in mind this is the scale the prop should scale to, not the amount it needs to scale by. Your completeEndTransformshould look like this:Verse# Build the ending transform for the RootProp to move to. set EndTransform = transform: # Use Lerp() to find how far between the StartingTransform and the TargetTransform the RootProp should translate. # Do the same for scale, and find the shortest rotation between the original transform and a rotation to apply to it. Translation := Lerp(OriginalTransform.Translation, TargetTransform.Translation, LerpParameter) Rotation := MakeShortestRotationBetween(OriginalTransform.Rotation, OriginalTransform.Rotation.RotateBy(RotationToApply)) Scale := Lerp(OriginalTransform.Scale, TargetTransform.Scale, LerpParameter)
With your end transform set, it’s time to build a keyframe! This is largely the same process you did for your
MoveToEase()function. Create a newkeyframe_deltavariable namedKeyFrame. Set theDeltaLocationto the difference between the end and start transform translations. Set theDeltaRotationto the end transform’s rotation. Since you need to calculate the change in scale, set theDeltaScaleto the result of dividing the end transform’s scale by the starting transform’s scale. TheTimeshould be theAnimationTime, and theInterpolationTypeshould be the result of anifexpression. IfUseEasePerKeyframeis true, use theMoveEaseType. Otherwise, use the linear type. Your keyframe expression should look like this:Verse# Build the animation keyframe to animate the RootProp. Keyframe := keyframe_delta: DeltaLocation := EndTransform.Translation - StartTransform.Translation, DeltaRotation := EndTransform.Rotation, DeltaScale := EndTransform.Scale/StartTransform.Scale, Time := AnimationTime, # Use the MoveEaseType for interpolation if UseEasePerKeyframe is true, # otherwise use the Linear movement type. Interpolation := if:With your keyframe built, now you can add it to the
Keyframesarray. Then set theStartTransformto theEndTransformto update it for the next keyframe. Finally, if theTotalTimeis now greater than theMoveDuration, break out of the loop.Verse# Add the new keyframe to the KeyFrames array, and set the # StartTransform to the EndTransform. set Keyframes += array{Keyframe} set StartTransform = EndTransform # Break out of the loop if the TotalTime passes the MoveDuration. if: TotalTime >= MoveDuration then: breakThere’s an important edge case to consider: what happens when you need to make less than a full rotation? Since you’re adding the
TimePerRotationto theTotalTime, this means theTotalTimecould be higher than theMoveDurationat the start of the loop. In this situation, you need to handle the leftover time and make less than full rotation, with a shorter animation time to account for the difference. Follow these steps to account for this situation:Back at the start of the loop, after you update
TotalTime, start anifexpression. Inside, initialize a variableLeftoverTime, and set it equal to the result of subtracting theTotalTimeandMoveDuration, checking if it’s greater than0.0. This expression will only assignLeftoverTimeif the comparison is true.Verseloop: # Add the TimePerRotation to the TotalTime. set TotalTime += TimePerRotation if: # If the TotalTime is greater than the MoveDuration, the final keyframe needs # to be shortened. This means making a fraction of a rotation. LeftoverTime := TotalTime - MoveDuration > 0.0To know what fraction of a rotation you need to make, initialize a new variable
Roation Fraction, and set it equal to taking the difference between theTimePerRotationandLeftoverTime, all divided by theTimePerRotation.Verseif: # If the TotalTime is greater than the MoveDuration, the final keyframe needs # to be shortened. This means making a fraction of a rotation. LeftoverTime := TotalTime - MoveDuration > 0.0 # The fraction of a rotation to make. RotationFraction := (TimePerRotation - LeftoverTime)/TimePerRotationTo build a modified rotation from the
RotationFraction, you’ll useSlerp[]. This is the version ofLerp()that handles spherical interpolation and has similar parameters. It finds the shortest rotation between two different rotations, and returns a rotation based on the lerp parameter. CallSlerp[], interpolating between theIdentityRotation()and theIdentityRotation()rotated byRotationToApply, using theRotationFractionas the lerp parameter. You’re usingIdentityRotation()here because you’re only interested in finding what fractional rotation you need to apply, not what final rotation theEndTransformshould be at.Verseset TotalTime += TimePerRotation if: # If the TotalTime is greater than the MoveDuration, the final keyframe needs # to be shortened. This means making a fraction of a rotation. LeftoverTime := TotalTime - MoveDuration > 0.0 # The fraction of a rotation to make. RotationFraction := (TimePerRotation - LeftoverTime)/TimePerRotation # Make a modified fractional rotation by using Slerp(). The Slerp() function does spherical interpolationWith those values set up, set the
RotationToApplyto theModifiedRotation, multiply theAnimationTimeby theRotationFractionto know how much to shorten your animation by, and finally set theTotalTimeto theMoveDurationsince you don’t want it to be higher than that when calculating theEndTransform.VerseModifiedRotation := Slerp[IdentityRotation(), IdentityRotation().RotateBy(RotationToApply), RotationFraction] then: # Set the RotationToApply to the modified rotation, and multiply the animation time by # the RotationFraction to get the modified animation time. set RotationToApply = ModifiedRotation set AnimationTime = AnimationTime * RotationFraction # Since the TotalTime should not go past the MoveDuration, # set TotalTime to MoveDuration. set TotalTime = MoveDuration
At the very end of your function, after the loop, return the
Keyframesarray. Your completeBuildMovingAnimationKeyframes()function should look like this:Verse# Builds an array of keyframes that animate movement and rotation from the OriginalTransform to the TargetTransform. BuildMovingAnimationKeyframes(MoveDuration:float, RotationRate:float, AdditionalRotation:rotation, OriginalTransform:transform, TargetTransform:transform,MoveEaseType:move_to_ease_type, UseEasePerKeyframe:logic):[]keyframe_delta= # The array of keyframes to return. var Keyframes:[]keyframe_delta = array{} # The total amount of time spent animating. var TotalTime:float = 0.0 # The starting transform for building keyframes. This is theNow that you’ve defined the logic to build your keyframes, it’s time to animate them. You’ll use a separate function to build and call your animation. Add a new function
BuildAndPlayAnimation()to youranimating_propclass. Add the<suspends>modifier to this function to allow it to call other asynchronous functions.Verse# Builds an animation from an array of keyframes, then calls MoveToEase() # to animate the prop. BuildAndPlayAnimation()<suspends>:void=In
BuildAndPlayAnimation(), initialize a newkeyframe_deltaarray namedKeyframes. Then setKeyframesto the result of callingBuildMovingAnimationKeyframes(). UseRootProp.GetTransform()as the starting transform since the prop’s position will change betweenMove()calls. Initialize ananimation_modevariable toanimation_mode.OneShot, and callMoveToEase(), passing theKeyframesarray and theAnimationMode.Your complete
BuildAndPlayAnimation()function should look like this:Verse# Builds an animation from an array of keyframes, then calls MoveToEase() # to animate the prop. BuildAndPlayAnimation()<suspends>:void= var Keyframes:[]keyframe_delta = array{} # Build the animation, using the RootProp as the target transform. set Keyframes = BuildMovingAnimationKeyframes(MoveDuration, RotationRate, AdditionalRotation, RootProp.GetTransform(), TargetTransform, MoveEaseType, UseEasePerKeyframe) # Set the animation mode to OneShot. var AnimationMode:animation_mode := animation_mode.OneShotBack in
Move(), callBuildAndPlayAnimation()after you set theTargetTransformto the move targets transform.Verse# Move to each target in the MoveTargets array. for: MoveTarget:MoveTargets do: # Set the TargetTransform to the MoveTarget if the # MoveTarget is set. Otherwise set it to the MoveTransform. if: MoveTarget.IsValid[] then: set TargetTransform = MoveTarget.GetTransform()
There’s one more case to consider. What should happen if you don’t set any move targets? In this situation, your prop should continue to rotate in place, without moving to a new destination. To handle this, add an if expression to the top of your Move() function. In the if, check if MoveTargets.Length = 0, and if so set the TargetTransform to the root prop’s transform. Then call BuildAndPlayAnimation(). This way, your prop will continue to animate even if you didn’t set a move target. Your complete Move() animation should look like this:
# Move and rotate the RootProp toward the MoveTarget, or MoveTransform if one is set.
Move<override>()<suspends>:void=
# If there are no targets to move to, this prop will rotate in place.
if:
MoveTargets.Length = 0
then:
set TargetTransform = RootProp.GetTransform()
# Build and play the animation.
BuildAndPlayAnimation()
Now you need to reference animating_prop in your prop_animator class. In prop_animator, add an editable array of animating_prop named MoveAndRotateProps. In OnBegin(), in another for expression, initialize each prop in MoveAndRotateProps by calling Setup(). Your complete prop_animator class should look like this:
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
TranslatingPropsTip<localizes>:message = "The props that translate (move) using animation."
RotatingPropsTip<localizes>:message = "The props that rotate using animation."
ScalingPropsTip<localizes>:message = "The props that scale using animation."
AnimatingPropsTip<localizes>:message = "The props that both move and rotate using animation."
# Coordinates moving props through animation by calling each movable_prop's Setup() method.
Save your code and compile it.
Congratulations, that’s all the code out of the way! Now it’s time to get everything linked together.
The animating_prop class you just created can move, rotate, and scale props. However, it is still reliant on the moveable_prop class because it needs to inherit several functions such as ManageMovement(). Given that the animating_prop class can perform all three types of movement, you may find it useful to refactor animating_prop to include all the logic of moveable_prop so that the class can stand alone. An example refactor is included here, which merges animating_prop and moveable_prop into a single file. It also includes the prop_animator verse device class. You will still need the functionality from movement_behaviors for your code to run, but this refactor reduces the number of files needed from five to two. This code is also included in the Complete Code section.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Devices/CreativeAnimation }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }
EasingCategory<localizes>:message := "These control the type of movement easing applied to the prop."
LogicCategory<localizes>:message := "These control different aspects of the prop's logic."
PropsCategory<localizes>:message := "These are the props that move associated with this device."
RotationCategory<localizes>:message := "These control how the prop rotates."
TimingCategory<localizes>:message := "These control the timing of parts of the prop's movmement."
Linking Props to Devices
Back in the editor, delete a section of the course after the rotating props section to create a gap before the end goal. Add another FG01 SpinningBar Double S and FG01 Hover Platform M to your level. Name them SpinningMovingBar and TranslatingPlatform, then add several FG01 Button Bulb props, which will be the targets each prop will move to. Name these PlatformTarget. Place the platforms and bar over the gap, and make sure to place the targets where you want the platforms to move. In this example, the spinning bar moves side-to-side, while the platform moves back and forth.
Setup of the spinning bar and moving platform. The arrows indicate in what directions each prop moves. Both the spinning bar and the platform move back and forth, and the spinning bar spins as it moves.
Select your prop animator in the Outliner. Add an array element to AnimatingProps for your spinning bar. Set each value to the following:
| Option | Value | Explanation |
|---|---|---|
Additional Rotation | 90.0 | This prop will make a 90-degree rotation each time. |
Rotation Rate | 1.5 | This prop will make a rotation every |
Use Ease Per Keyframe | false | This will use the Linear easing type on each keyframe to move and rotate the prop at a consistent speed. |
MoveTargets | 2 elements, assigned to platform targets. | These are the targets you want the bar to move to. |
RootProp | SpinningMovingBar | This is the prop you’re animating. |
Add another array element to TranslatingProps for your moving platform. Assign the MoveTargets to your platform targets, and the RootProp to your TranslatingPlatform.
Hit Launch Session and try running through your complete obstacle course!
On Your Own
And that’s it! Now you’ve got everything you need to make your own Fall Guys obstacle course using Verse!
You can use the code here to animate Creative props in any of your experiences, and even beyond Fall Guys projects!
Using what you’ve learned, try the following:
Make obstacles that rotate in a variety of directions, or rotate randomly across keyframes.
Make obstacles that activate only when a player stands on them or gets within a certain distance.
Work out how to make platforms that disappear after a certain duration, or move the player into dangerous positions if they stay on too long.
Complete Code
Here is the complete code built in this section, including an example refactor for animating_prop.
movable_prop.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }
MoveDurationTip<localizes>:message = "The amount of time the prop takes to move to its destination."
MoveEaseTypeTip<localizes>:message = "The animation easing applied to the movement."
MoveEndDelayTip<localizes>:message = "The delay after the movement finishes."
MoveOnceAndStopTip<localizes>:message = "Whether the RootProp should stop in place after it finishes moving."
MoveStartDelayTip<localizes>:message = "The delay before the movement starts."
MoveTargetsTip<localizes>:message = "The array of CreativeProp to move toward. These targets can be children of the RootProp."
translating_prop.verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/Devices/CreativeAnimation }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }
MovePositionTip<localizes>:message = "The optional position to move to World Space. Use this if you do not want to set a MoveTarget."
# A prop that moves (translates) toward either a Creative prop target
# or a position in world space.
translating_prop<public> := class<concrete>(movable_prop):
rotating_prop.verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/Devices/CreativeAnimation }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }
AdditionalRotationTip<localizes>:message = "The rotation to apply to the RootProp."
ShouldRotateForeverTip<localizes>:message = "Whether the RootProp should rotate forever."
MatchRotationTargetTip<localizes>:message = "The optional prop whose rotation the RootProp should rotate to. Use this if you do not want to set an Additional Rotation."
# A prop that rotates by an additional rotation or rotates to match
scaling_prop.verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/Devices/CreativeAnimation }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }
MatchScaleTargetTip<localizes>:message = "The optional position to move to World Space. Use this if you do not want to set a MoveTarget."
# A prop that scales toward either a given scale or a Creative prop's scale.
scaling_prop<public> := class<concrete>(movable_prop):
# The array of vector3 targets for the RootProp to scale to.
animating_prop.verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/Devices/CreativeAnimation }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }
RotationRateTip<localizes>:message := "The time it takes to make one AdditionalRotation in seconds."
UseEasePerKeyframeTip<localizes>:message := "Whether this prop should use the MoveEaseType for each keyframe. False will use the Linear ease type on each frame."
# A prop that translates, rotates, and scales to a destination using animation.
animating_prop<public> := class<concrete>(movable_prop):
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}
prop_animator.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
TranslatingPropsTip<localizes>:message = "The props that translate (move) using animation."
RotatingPropsTip<localizes>:message = "The props that rotate using animation."
ScalingPropsTip<localizes>:message = "The props that scale using animation."
MoveAndRotatePropsTip<localizes>:message = "The props that both move and rotate using animation."
# Coordinates moving props through animation by calling each moveable_prop's Setup() method.
animating_props.verse (example refactor of animating_prop)
using { /Fortnite.com/Devices }
using { /Fortnite.com/Devices/CreativeAnimation }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }
EasingCategory<localizes>:message := "These control the type of movement easing applied to the prop."
LogicCategory<localizes>:message := "These control different aspects of the prop's logic."
PropsCategory<localizes>:message := "These are the props that move associated with this device."
RotationCategory<localizes>:message := "These control how the prop rotates."
TimingCategory<localizes>:message := "These control the timing of parts of the prop's movmement."