MoveTo: The Swinging Gate Hazard

In this lesson you'll learn to

  • ✓Animate a creative_prop with MoveTo/TeleportTo — the first rung of the animation arc (→ ease → prop_mover → control rig).

Animate a creative_prop with MoveTo/TeleportTo — the first rung of the animation arc (→ ease → prop_mover → control rig).

📖 Reference & full walkthrough: MoveTo: The Swinging Gate Hazard

🔁 Builds on: south-shores spawn-prop lesson (the prop being animated)

🧩 Your capstone piece: Swinging gate hazard on the rope-bridge section


Move It! Glide an Entity to a Target Over Time

Welcome to Scene Graph: Animation. In the Verse Scene Graph series you learned what entities and components are, how they hang off each other in a tree, and that every entity carries a transform — its position, rotation, and scale. That was the still photograph. This series is the movie: we're going to make things move.

We'll do it the way real games do — not by teleporting things frame by frame, but by handing the engine a short animation and letting it play. By the end of these four lessons you'll have built a moving platform a player can ride. This first lesson is the foundation: glide one thing smoothly from where it is to somewhere new.

We'll set everything in an Art Deco bank vault — gold-capped pillars, a teller wall, and a polished marble floor — and animate the vault's contents.

The Art Deco Vault scene, built in UEFN with real Fortnite assets

The one idea: a transform is "where a thing sits"

<!-- section-art:the-one-idea-a-transform-is-where-a-thing-sits --> Move It! Glide an Entity to a Target Over Time: The one idea: a transform is "where a thing sits"

Transform Bundle

Everything that moves, moves its transform. A transform bundles three facts:

  • Translation — where it is (a vector3 of Left, Up, Forward)
  • Rotation — which way it faces
  • Scale — how big it is

You can read the current transform off any entity. Notice the axis names — Fortnite uses Forward, Left, and Up, not X/Y/Z:

# The transform is just three facts about where a thing sits:
# where (Translation), which way it faces (Rotation), and how big (Scale).
Here := Entity.GetLocalTransform()
Forward := Here.Translation.Forward
Up := Here.Translation.Up
Left := Here.Translation.Left

GetLocalTransform() uses round brackets — it can't fail, it always hands back a transform (the identity transform if the entity has no transform_component yet). That bracket-shape rule is exactly the one from The Grammar of Verse: round brackets never fail, square brackets can.

Don't move things by hand — let the mover do it

You could nudge an entity every tick with SetLocalTransform. But hand-animating is fiddly and easy to get wrong. The Scene Graph ships a purpose-built tool: the keyframed movement component.

A keyframed movement component plays a list of keyframe deltas for an entity. Each delta says: "change the transform THIS much, over THIS many seconds, with THIS easing." You describe the motion once; the engine animates it for you.

You add this component to an entity in the editor (select the entity → Add Component → Keyframed Movement), then your code finds it and presses Play. That split — component in the editor, behavior in code — is the whole Scene Graph philosophy.

Glide a safe across the vault

Here's the complete component. Bolt it onto the vault's safe (which also has a Keyframed Movement component), and when the round begins, the safe glides forward and eases to a stop:

using { /Verse.org/SceneGraph }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }

# Bolt this onto any entity that ALSO has a Keyframed Movement component
# (add one in the editor: Add Component -> Keyframed Movement). When the
# game begins, this glides the entity GlideDistance units forward, smoothly.
glide_to_target_component := class<final_super>(component):

    # How far forward to glide, in units. A knob you can tune in the editor.
    @editable
    var GlideDistance<public>:float = 500.0

    # How long the glide should take, in seconds.
    @editable
    var GlideSeconds<public>:float = 3.0

    OnBeginSimulation<override>():void =
        # Be polite: let the base class do its own startup first.
        (super:)OnBeginSimulation()

        # Find the mover sitting on this same entity. Square brackets because
        # it can fail — if nobody added a Keyframed Movement component, there's
        # nothing to drive, so we just stop.
        if (Mover := Entity.GetComponent[keyframed_movement_component]):
            # Describe ONE keyframe: "move GlideDistance forward, taking
            # GlideSeconds, easing in and out so it starts and stops gently."
            # Translation is relative to where the entity is right now.
            Step := keyframed_movement_delta:
                Duration := GlideSeconds
                Easing := ease_in_out_cubic_bezier_easing_function{}
                Transform := transform:
                    Translation := vector3:
                        Forward := GlideDistance
                        Left := 0.0
                        Up := 0.0

            # Hand the keyframe list to the mover and tell it to play once.
            Mover.SetKeyframes(array{Step}, oneshot_keyframed_movement_playback_mode{})
            Mover.Play()

Read it top to bottom:

  1. OnBeginSimulation runs once when the component wakes up in the game. We call (super:)OnBeginSimulation() first so the base component does its own setup — always be a polite subclass.
  2. Entity.GetComponent[keyframed_movement_component] asks "is there a mover on me?" The square brackets mean it can fail; the if guards it. No mover → nothing to do.
  3. The delta is the heart of it. Duration is how long; Easing shapes the speed curve (more on that next); Transform.Translation says move 500 units forward — relative to the start, not an absolute spot in the world.
  4. SetKeyframes loads the list. We pass oneshot_keyframed_movement_playback_mode{} — play once and stop.
  5. Play() starts it. The engine handles every frame from here.

Easing: the difference between "robotic" and "alive"

The Easing value decides how the speed changes across the move:

  • linear_easing_function{} — constant speed. Mechanical, like a conveyor.
  • ease_in_out_cubic_bezier_easing_function{} — starts slow, speeds up, slows to a stop. This is what real, weighty objects do, and it's why our safe feels heavy instead of teleport-y.

Swapping one word changes the whole feel of the motion. That's the payoff of letting the mover animate for you.

A safe positioned in the vault, ready to glide to its target

"Relative" is your friend

<!-- section-art:relative-is-your-friend --> Move It! Glide an Entity to a Target Over Time: "Relative" is your friend

Relative Motion

Because the delta's translation is relative, this one component works on any entity, anywhere in the vault — it always moves 500 units forward from wherever it starts. You never hard-code world coordinates. Drop the safe anywhere, the glide just works.

Recap

  • A transform is position + rotation + scale; moving something means changing its transform.
  • The keyframed movement component animates an entity for you — you describe keyframe deltas, it plays them.
  • A delta = Duration + Easing + a relative Transform.
  • SetKeyframes(..., oneshot...{}) then Play() runs it once.
  • Easing turns stiff motion into believable motion — ease_in_out for weight, linear for machines.

Next up — Part 2: Spin to Win — we keep the entity in place and turn it instead, and meet a subtle gotcha about rotations taking the shortest path.

🔒

Keep going — free

You've read the intro. The rest of this lesson is free for members. Sign in to continue and track your progress.

Sign in free to continue this lesson
Pass the quiz above to chart this quest in your Journal.

Sources

/guides/verse-anim-move-to-target
Source

Biloxi Studios original lesson

Guided course

Add this lesson to your free study plan.

🧭 The Keeper’s log

Quest complete? Chart your next heading from the 🐻 North Jungle expedition.

⛵ Take me there