Reference Devices compiles

roly_poly_spawner_device: Rideable Critters That React to Your World

The `roly_poly_spawner_device` gives you full programmatic control over Fortnite's lovable armadillo-like creatures — spawn them on demand, force a player into the saddle, react when they curl up in fear, and dismiss them when the moment is right. Whether you're building a creature-taming mini-game, a mount-rush challenge, or a puzzle where players must soothe a frightened critter to unlock a door, this device is your entry point. Six events and four methods cover the entire Roly Poly lifecycle

Updated Examples verified on the live UEFN compiler
Watch the Knotroly_poly_spawner_device in ~90 seconds.

Overview

The Roly Poly Spawner Device (roly_poly_spawner_device) manages a single Roly Poly creature on your island. Drop one in UEFN, wire it to a Verse device, and you can:

  • Spawn / Dismiss the creature at any time with Spawn() and Dismiss().
  • Force a rider onto the creature with Ride(Agent).
  • Query the live creature reference with GetActiveRolyPoly() (failable — it returns ?roly_poly).
  • React to every lifecycle moment via six events: SpawnEvent, FleeEvent, RideEvent, DismountEvent, FrightenEvent, and SootheEvent.

When to reach for it

Scenario Key API
Spawn a mount when a player enters a zone Spawn() + RideEvent
Unlock a door only after the Roly Poly is soothed SootheEvent
Award points for staying mounted RideEvent / DismountEvent
Reset the creature between rounds Dismiss() then Spawn()
Read the creature's health mid-game GetActiveRolyPoly()

The device inherits from creative_device_base and enableable, so you can also Enable() / Disable() it through the standard interface.

API Reference

roly_poly_spawner_device

Device for spawning a Roly Poly

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).

roly_poly_spawner_device<public> := class<concrete><final>(creative_device_base, enableable):

Events (subscribe a handler to react):

Event Signature Description
SpawnEvent SpawnEvent<public>:listenable(tuple()) Signaled when a Roly Poly is spawned or respawned by this device.
FleeEvent FleeEvent<public>:listenable(tuple()) Signaled when a spawned Roly Poly flees.
RideEvent RideEvent<public>:listenable(agent) Signaled when an 'agent' enters a spawned Roly Poly. Sends the 'agent' that entered the Roly Poly.
DismountEvent DismountEvent<public>:listenable(agent) Signaled when an 'agent' exits a spawned Roly Poly. Sends the 'agent' that exited the Roly Poly.
FrightenEvent FrightenEvent<public>:listenable(?agent) Signaled when an 'agent' causes the spawned Roly Poly to curl up and hide.
SootheEvent SootheEvent<public>:listenable(?agent) Signaled when an 'agent' causes the spawned Roly Poly to open up after being scared.

Methods (call these to make the device act):

Method Signature Description
Spawn Spawn<public>():void Spawn a Roly Poly now. If one is active, dismiss it first, then spawn. If the spawner is disabled, this call does nothing.
Dismiss Dismiss<public>():void Dismiss the active Roly Poly if present. If 'AutomaticRespawn' is true, a respawn timer for 'RespawnDelay' seconds will be created.
Ride Ride<public>(Agent:agent):void Attempt to set 'agent' as the spawned Roly Poly's rider.
GetActiveRolyPoly GetActiveRolyPoly<public>()<transacts><decides>:?roly_poly

Walkthrough

Scenario: The Roly Poly Rodeo

A pressure plate sits in the arena. When a player steps on it:

  1. The Roly Poly spawns.
  2. That player is automatically placed in the saddle.
  3. If the creature gets frightened, a cinematic plays and a timer starts.
  4. When soothed, the timer stops and the player earns a score bonus.
  5. On dismount, the creature is dismissed and the plate is re-armed.

Place in your level:

  • One roly_poly_spawner_deviceRolyPolySpawner
  • One trigger_device (pressure plate) → ArenaTrigger
  • One cinematic_sequence_deviceFrightenCinematic
  • One timer_deviceSootheTimer
  • One score_manager_deviceScoreManager
roly_poly_rodeo_device := class(creative_device):

    # ── Editable references ──────────────────────────────────────────
    @editable
    RolyPolySpawner : roly_poly_spawner_device = roly_poly_spawner_device{}

    @editable
    ArenaTrigger : trigger_device = trigger_device{}

    @editable
    FrightenCinematic : cinematic_sequence_device = cinematic_sequence_device{}

    @editable
    SootheTimer : timer_device = timer_device{}

    @editable
    ScoreManager : score_manager_device = score_manager_device{}

    # Track who stepped on the plate so we can put them in the saddle
    var CurrentRider : ?agent = false

    # ── Lifecycle ────────────────────────────────────────────────────
    OnBegin<override>()<suspends> : void =
        # Subscribe to the pressure plate — handler receives the triggering agent
        ArenaTrigger.TriggeredEvent.Subscribe(OnPlateTriggered)

        # Roly Poly lifecycle events
        RolyPolySpawner.SpawnEvent.Subscribe(OnRolyPolySpawned)
        RolyPolySpawner.RideEvent.Subscribe(OnRide)
        RolyPolySpawner.DismountEvent.Subscribe(OnDismount)
        RolyPolySpawner.FrightenEvent.Subscribe(OnFrighten)
        RolyPolySpawner.SootheEvent.Subscribe(OnSoothe)
        RolyPolySpawner.FleeEvent.Subscribe(OnFlee)

    # ── Plate handler ────────────────────────────────────────────────
    # trigger_device sends ?agent — unwrap before use
    OnPlateTriggered(MaybeAgent : ?agent) : void =
        if (Agent := MaybeAgent?):
            set CurrentRider = MaybeAgent
            # Spawn the Roly Poly (dismisses any existing one first)
            RolyPolySpawner.Spawn()
            # Disable the plate until this round ends
            ArenaTrigger.Disable()

    # ── SpawnEvent handler (listenable(tuple()) — no payload) ────────
    OnRolyPolySpawned(Unused : tuple()) : void =
        # Now that the creature is alive, force our rider into the saddle
        if (Rider := CurrentRider?):
            RolyPolySpawner.Ride(Rider)

    # ── RideEvent handler (listenable(agent)) ────────────────────────
    OnRide(Rider : agent) : void =
        # Start the soothe timer as a riding challenge clock
        SootheTimer.Start(Rider)

    # ── DismountEvent handler (listenable(agent)) ────────────────────
    OnDismount(Rider : agent) : void =
        SootheTimer.Reset(Rider)
        # Dismiss the creature and re-arm the plate for the next player
        RolyPolySpawner.Dismiss()
        ArenaTrigger.Enable()
        set CurrentRider = false

    # ── FrightenEvent handler (listenable(?agent)) ───────────────────
    OnFrighten(MaybeAgent : ?agent) : void =
        # Play the cinematic regardless of who caused the fright
        FrightenCinematic.Play()
        # If we know the frightener, reset their timer as a penalty
        if (Agent := MaybeAgent?):
            SootheTimer.Reset(Agent)

    # ── SootheEvent handler (listenable(?agent)) ─────────────────────
    OnSoothe(MaybeAgent : ?agent) : void =
        FrightenCinematic.Stop()
        if (Agent := MaybeAgent?):
            # Award bonus points to whoever soothed the Roly Poly
            ScoreManager.Activate(Agent)

    # ── FleeEvent handler (listenable(tuple()) — no payload) ─────────
    OnFlee(Unused : tuple()) : void =
        # Creature fled — clean up state and re-arm the plate
        ArenaTrigger.Enable()
        set CurrentRider = false

Line-by-line highlights

Line / block Why it matters
@editable fields Every device reference MUST be declared here — bare identifiers fail to compile.
ArenaTrigger.TriggeredEvent.Subscribe(OnPlateTriggered) trigger_device.TriggeredEvent sends ?agent; the handler signature must match.
OnRolyPolySpawned(Unused : tuple()) SpawnEvent and FleeEvent are listenable(tuple()) — the handler takes a tuple() param even though there's no useful payload.
RolyPolySpawner.Ride(Rider) Called after spawn so the creature actually exists to receive a rider.
if (Agent := MaybeAgent?) FrightenEvent and SootheEvent send ?agent — always unwrap before use.
RolyPolySpawner.Dismiss() Cleans up the creature; if AutomaticRespawn is true in the device settings, a respawn timer starts automatically.

Common patterns

Pattern 1 — Query the live Roly Poly and adjust its health

Use GetActiveRolyPoly() (failable, <transacts><decides>) to read or modify the creature mid-game.

roly_poly_health_check_device := class(creative_device):

    @editable
    RolyPolySpawner : roly_poly_spawner_device = roly_poly_spawner_device{}

    @editable
    HealTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        RolyPolySpawner.Spawn()
        HealTrigger.TriggeredEvent.Subscribe(OnHealTriggered)

    OnHealTriggered(MaybeAgent : ?agent) : void =
        # GetActiveRolyPoly is failable — use if to unwrap
        if (RolyPoly := RolyPolySpawner.GetActiveRolyPoly[]?):
            # Restore the Roly Poly to full health (SetHealth is on roly_poly)
            RolyPoly.SetHealth(RolyPoly.GetMaxHealth())

Note: GetActiveRolyPoly has the <transacts><decides> effect specifier, so it must be called inside a failable context (an if expression). It returns ?roly_poly, so unwrap the option with ? after the call.


Pattern 2 — Spawn on demand and auto-ride the first player who mounts

Demonstrates Spawn() + RideEvent without a pressure plate.

roly_poly_auto_mount_device := class(creative_device):

    @editable
    RolyPolySpawner : roly_poly_spawner_device = roly_poly_spawner_device{}

    @editable
    ScoreManager : score_manager_device = score_manager_device{}

    OnBegin<override>()<suspends> : void =
        RolyPolySpawner.RideEvent.Subscribe(OnFirstMount)
        RolyPolySpawner.DismountEvent.Subscribe(OnPlayerDismounted)
        # Spawn immediately when the game starts
        RolyPolySpawner.Spawn()

    # RideEvent sends a plain `agent` (not optional)
    OnFirstMount(Rider : agent) : void =
        # Give the rider a point for successfully mounting
        ScoreManager.Activate(Rider)

    OnPlayerDismounted(Rider : agent) : void =
        # Respawn a fresh Roly Poly for the next contestant
        RolyPolySpawner.Spawn()

Pattern 3 — Frighten / Soothe puzzle gate

A door stays locked until the Roly Poly is soothed. Uses FrightenEvent and SootheEvent to drive a cinematic lock/unlock.

roly_poly_puzzle_gate_device := class(creative_device):

    @editable
    RolyPolySpawner : roly_poly_spawner_device = roly_poly_spawner_device{}

    @editable
    DoorOpenCinematic : cinematic_sequence_device = cinematic_sequence_device{}

    @editable
    DoorCloseCinematic : cinematic_sequence_device = cinematic_sequence_device{}

    var DoorOpen : logic = false

    OnBegin<override>()<suspends> : void =
        RolyPolySpawner.SpawnEvent.Subscribe(OnSpawned)
        RolyPolySpawner.FrightenEvent.Subscribe(OnFrightened)
        RolyPolySpawner.SootheEvent.Subscribe(OnSoothed)
        RolyPolySpawner.Spawn()

    OnSpawned(Unused : tuple()) : void =
        # Ensure door starts closed whenever a new Roly Poly appears
        if (DoorOpen?):
            DoorCloseCinematic.Play()
            set DoorOpen = false

    # FrightenEvent payload is ?agent — unwrap if you need the instigator
    OnFrightened(MaybeAgent : ?agent) : void =
        if (DoorOpen?):
            DoorCloseCinematic.Play()
            set DoorOpen = false

    # SootheEvent payload is ?agent — the soother gets credit
    OnSoothed(MaybeAgent : ?agent) : void =
        if (not DoorOpen?):
            DoorOpenCinematic.Play()
            set DoorOpen = true

Gotchas

1. SpawnEvent and FleeEvent send tuple(), not agent

These two events carry no agent payload. Their handler signature must be:

OnSpawned(Unused : tuple()) : void = ...

Writing OnSpawned(Agent : agent) will cause a type mismatch at compile time.

2. FrightenEvent and SootheEvent send ?agent — always unwrap

The instigating agent is optional (the creature can be frightened by environmental triggers with no player involved). Always guard with if (Agent := MaybeAgent?): before calling any agent-typed API.

3. GetActiveRolyPoly is failable — call it inside if

GetActiveRolyPoly is marked <transacts><decides> and returns ?roly_poly. You must call it in a failable context:

# CORRECT
if (RP := RolyPolySpawner.GetActiveRolyPoly[]?):
    RP.SetHealth(100.0)

# WRONG — compiler error: expression is not failable here
let RP := RolyPolySpawner.GetActiveRolyPoly[]

4. Ride() does nothing if no Roly Poly is alive

Call Ride(Agent) only after SpawnEvent fires. Calling it immediately after Spawn() (synchronously) may race the spawn — subscribe to SpawnEvent and call Ride from the handler, as shown in the Walkthrough.

5. Spawn() dismisses an existing creature first

If a Roly Poly is already active, Spawn() dismisses it before spawning a new one. If AutomaticRespawn is enabled in the device properties, this also starts the respawn timer — which may cause an unexpected second spawn. Disable AutomaticRespawn when you want full manual control.

6. Dismiss() respects AutomaticRespawn

Calling Dismiss() while AutomaticRespawn = true will queue a respawn after RespawnDelay seconds. If you want a one-shot dismissal, turn off AutomaticRespawn in the device settings panel before calling Dismiss().

7. The device must be enabled for Spawn() to work

If the spawner is disabled (via Disable() or the editor setting), Spawn() silently does nothing. Call Enable() first if you've disabled the device programmatically.

Device Settings & Options

The roly_poly_spawner_device User Options panel in the UEFN editor — every setting you can tune.

Roly Poly Spawner Device settings and options panel in the UEFN editor — Enabled During Phase, Enable Respawn
Roly Poly Spawner Device — User Options in the UEFN editor: Enabled During Phase, Enable Respawn
⚙️ Settings on this device (2)

Setting names read from the panel above — may be partial; the screenshots are the source of truth.

Enabled During Phase
Enable Respawn

Build your own lesson with roly_poly_spawner_device

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →