The "Stop That Junk!" Tutorial: Controlling MoveTo Animations in Verse
Tutorial beginner

The "Stop That Junk!" Tutorial: Controlling MoveTo Animations in Verse

Updated beginner

The "Stop That Junk!" Tutorial: Controlling MoveTo Animations in Verse

Ever had a prop-mover slide a chest across the floor, only for it to keep sliding long after you’ve already opened it? Or maybe you have a trap door that’s supposed to close, but it just… doesn’t? That’s because in UEFN, MoveTo is like a stubborn friend who refuses to listen. You tell it to go somewhere, and it goes. But if you want to change its mind mid-trip, you need more than just a command—you need an Animation Controller.

In this tutorial, we’re going to build a "Revenge Trap." You’ll spawn a floating loot chest that drifts toward you. But here’s the twist: if you get too close, the chest will panic, stop its current path, and shoot back to its starting position. We’ll learn how to hook into the game’s animation system, listen for when a move finishes, and—more importantly—how to interrupt it.

What You'll Learn

  • The Scene Graph: How devices and props talk to each other in the Verse hierarchy.
  • Variables & Constants: Storing positions so your trap can remember where it started.
  • Events: Listening for the "I’m done moving" signal (like a storm timer ending).
  • Interrupting Animations: Using Stop or new MoveTo calls to override a prop’s current action.

How It Works

Think of a Creative Prop (like a chest, a barrel, or a piece of furniture) as an actor on a stage. When you use the MoveTo function, you’re giving that actor a script: "Walk from Point A to Point B over 5 seconds."

The problem? Verse doesn’t automatically know when the actor has arrived, nor does it know you want to yell "Cut!" if the player gets too close. The actor just keeps walking until the script finishes.

To fix this, we use an Animation Controller. Think of the Animation Controller as the Stage Manager. The prop is the actor, but the Stage Manager holds the clipboard with all the cues. By attaching a listener to the Stage Manager, we can:

  1. Subscribe to the MovementCompleteEvent: This is like waiting for the actor to finish their line before starting the next scene.
  2. Get the Actor’s Location: Knowing where the prop is in the 3D world.
  3. Interrupt: If we call MoveTo again on that same prop, the old animation is immediately cancelled (put into an AnimationNotSet state), and the new one starts. It’s like hitting "Rewind" on a video while it’s still playing.

We’ll use a Variable to store the "Home Base" (the starting position). In game terms, a variable is like your Health Bar—it changes during the game. A Constant is like the Map Layout—it’s set in the editor and doesn’t change. We need a variable for the start position because we might want to reset the trap multiple times, but we’ll treat the "panic distance" as a constant because we don’t want that changing mid-game.

Let's Build It

Here is the complete Verse code for our Revenge Trap. Copy this into a new Verse file in your project.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }

# This is our "Device" — the brain of our trap.
# Think of this as the Gamepad that controls the prop.
class revenge_trap_device(creative_device):

    # --- EDITABLES (Things you change in the Editor) ---
    
    # The prop that will move (e.g., a Loot Chest)
    @editable
    PropToMove: creative_prop = creative_prop{}

    # The trigger zone. If a player enters this, the trap activates!
    @editable
    TriggerZone: trigger_zone = trigger_zone{}

    # How close does the player have to be to trigger the "panic"?
    # In-game analogy: The "Damage Radius" of a grenade.
    panic_distance: float = 500.0

    # How long should the panic move take? (Seconds)
    panic_speed: float = 2.0

    # --- VARIABLES (Things that change during the game) ---
    
    # We need to remember where the prop started so we can send it back.
    # This is like your "Last Known Location" on the minimap.
    home_location: vector = vector{}

    OnBegin<override>()<suspends>: void =
        # 1. SAVE THE STARTING POSITION
        # Get the prop's current location and save it.
        # We do this once when the game starts.
        home_location = PropToMove.GetLocation()

        # 2. LISTEN FOR THE TRIGGER
        # Subscribe to the TriggerZone's "OnActorBeginOverlap" event.
        # This is like setting a mine that explodes when someone steps on it.
        TriggerZone.OnActorBeginOverlap.Subscribe(HandleTrigger)

        # 3. LISTEN FOR MOVEMENT COMPLETION
        # Get the Animation Controller for our prop.
        # This is the "Stage Manager" we talked about.
        if (controller := PropToMove.GetAnimationController[]):
            # Subscribe to the event that fires when a MoveTo finishes.
            # This is like waiting for the storm timer to hit zero.
            controller.MovementCompleteEvent.Subscribe(HandleMovementComplete)

    # --- HANDLERS (Functions that run when events happen) ---

    HandleTrigger := (other_actor: actor) -> void:
        # Check if the thing that entered is a Player
        if (player := other_actor.GetInstigator()?.As<player>[]):
            # 1. START THE "ATTACK" MOVE
            # Move the prop toward the player slowly.
            # We use the player's current location.
            target_pos := player.GetLocation()
            
            # Execute the move. This suspends the code until the move is done.
            # But wait! We want to be able to interrupt this.
            # So we run it in a way that allows us to cancel it later.
            # For simplicity in this beginner example, we'll just start the move.
            # Note: In a full system, you'd use a task to allow interruption.
            # Here, we'll just move it to the player.
            PropToMove.MoveTo(target_pos, 3.0)

            # 2. CHECK FOR PANIC CONDITION
            # After the move starts, check if the player is *too* close.
            # If they are, we interrupt the move and send it home!
            distance := (PropToMove.GetLocation() - target_pos).Length()
            
            # Actually, let's check distance *now* to see if they are already close.
            # If the player is within panic_distance, send it home immediately.
            if (distance < panic_distance):
                SendHome()

    HandleMovementComplete := (result: move_to_result) -> void:
        # This runs when a MoveTo finishes.
        # In our simple trap, we might just want to reset it after it arrives.
        # Or we can do nothing. For now, let's just log it (mentally).
        # If you wanted the prop to stay where it landed, you'd do nothing here.
        pass

    SendHome := () -> void:
        # This is the "Interrupt" logic.
        # Calling MoveTo again on the same object stops the previous one.
        # It's like switching channels on a TV while a show is playing.
        
        # Move the prop back to the saved home location.
        PropToMove.MoveTo(home_location, panic_speed)
        
        # Optional: You could add a sound or effect here!

Walkthrough: What’s Happening?

  1. OnBegin: This is the "Game Start" event. When the island loads, we grab the prop’s current position and save it to home_location. We also hook up two listeners: one for the TriggerZone (the mine) and one for the MovementCompleteEvent (the stage manager).
  2. HandleTrigger: When a player steps into the zone, we tell the prop to MoveTo the player’s location. This is the "attack."
  3. The Panic Check: We calculate the distance between the prop and the player. If the player is closer than panic_distance, we call SendHome().
  4. SendHome: This is the magic line. We call PropToMove.MoveTo(home_location, ...) again. Because the prop is already moving, this new command interrupts the old one. The prop instantly changes direction and rushes back to its starting spot.

Try It Yourself

Challenge: Add a "Heal" mechanic. When the prop returns home (after the panic), have it change color to green to signal "Safe."

Hint: You can use PropToMove.SetMaterialParameterValue("Color", vector{0, 1, 0}) if you have a material parameter named "Color" on the prop. Or, simpler: just use PropToMove.SetColor(vector{0, 1, 0}) if you want to change the whole prop’s tint. Remember, you can trigger this inside HandleMovementComplete if the move was to the home_location.

Recap

  • MoveTo is a command, not a promise. It moves an object, but it doesn’t tell you when it’s done unless you listen.
  • Animation Controllers are your Stage Managers. Use GetAnimationController[] to subscribe to MovementCompleteEvent.
  • Interrupting is easy: just call MoveTo again on the same object. The old animation stops, and the new one begins.
  • Variables like home_location let you remember state, so your props can come home when they’re scared.

Now go build some traps. Make those players pay for getting too close.

References

  • https://dev.epicgames.com/community/snippets/LNNr/fortnite-using-animation_controller-moveto
  • https://dev.epicgames.com/documentation/en-us/fortnite/verse-api/fortnitedotcom/devices/creative_object
  • https://dev.epicgames.com/documentation/en-us/uefn/verse-api/fortnitedotcom/devices/creative_device/moveto-1
  • https://dev.epicgames.com/documentation/fortnite/verse-api/fortnitedotcom/devices/audio_mixer_device
  • https://dev.epicgames.com/documentation/en-us/uefn/verse-api/fortnitedotcom/devices/creative_object/moveto-1

Verse source files

Turn this into a guided course

Add fortnite-using-animation_controller-moveto to your free study plan — we'll suggest related pages and stitch the lot into one compile-checked, self-guided lesson with worked examples and quizzes.

Original tutorial generated by Verse Island from the Verse/UEFN knowledge base, with references to the Epic Games sources above. Code is validated against the knowledge base.

Comments

    Sign in to vote, comment, or suggest an edit. Sign in