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()andDismiss(). - 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, andSootheEvent.
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:
- The Roly Poly spawns.
- That player is automatically placed in the saddle.
- If the creature gets frightened, a cinematic plays and a timer starts.
- When soothed, the timer stops and the player earns a score bonus.
- On dismount, the creature is dismissed and the plate is re-armed.
Place in your level:
- One
roly_poly_spawner_device→RolyPolySpawner - One
trigger_device(pressure plate) →ArenaTrigger - One
cinematic_sequence_device→FrightenCinematic - One
timer_device→SootheTimer - One
score_manager_device→ScoreManager
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:
GetActiveRolyPolyhas the<transacts><decides>effect specifier, so it must be called inside a failable context (anifexpression). 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.