Overview
The pinball_flipper_device solves a very specific game problem: you need a physical surface that launches players. By default any player who walks into the flipper's front face triggers it — the flipper rotates counterclockwise and sends the player flying. In Verse you get three levers:
| Need | API |
|---|---|
| Turn the flipper on/off (e.g. only active during a wave) | Enable() / Disable() |
| Force-fire the flipper at a specific player from code | Activate(Agent) |
| React every time a player gets hit | ActivatedEvent |
Typical use-cases include:
- Pinball arenas — multiple flippers that players must dodge or ride.
- Trap corridors — flippers that are hidden until a pressure plate is stepped on.
- Score multipliers — award bonus points every time a player survives a flipper hit.
- Timed gauntlets — flippers that cycle on/off in waves.
API Reference
pinball_flipper_device
Used to move, damage, and give scores to players that interact with it. By default, it is activated by any player touching its front face, which rotates it counterclockwise and knocks those players away from it and slightly upward.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
pinball_flipper_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
ActivatedEvent |
ActivatedEvent<public>:listenable(agent) |
Signaled when this device is activated by an agent. Sends the agent that activated this device. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>():void |
Enables this device. |
Disable |
Disable<public>():void |
Disables this device. |
Activate |
Activate<public>(Agent:agent):void |
Causes Agent to activate this device. |
Walkthrough
Scenario: The Trap Corridor
A player steps on a trigger_device pressure plate. Three pinball_flipper_devices that were previously disabled suddenly enable. Whenever a flipper hits a player, a score manager awards bonus points. When the round ends (a second trigger fires) the flippers disable again.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
# Localized helper — needed any time a method expects a `message` param.
# (pinball_flipper_device has no message params, so this is here as a reminder
# pattern; remove if unused in your own project.)
trap_corridor_manager := class(creative_device):
# ── Editable references ─────────────────────────────────────────────
# Wire these to your placed devices in the UEFN Details panel.
@editable
ActivatePlate : trigger_device = trigger_device{}
@editable
DeactivatePlate : trigger_device = trigger_device{}
@editable
Flipper1 : pinball_flipper_device = pinball_flipper_device{}
@editable
Flipper2 : pinball_flipper_device = pinball_flipper_device{}
@editable
Flipper3 : pinball_flipper_device = pinball_flipper_device{}
@editable
ScoreManager : score_manager_device = score_manager_device{}
# ── Lifecycle ────────────────────────────────────────────────────────
OnBegin<override>()<suspends> : void =
# Start with all flippers disabled — players can walk through safely.
Flipper1.Disable()
Flipper2.Disable()
Flipper3.Disable()
# Subscribe to the pressure plates.
ActivatePlate.TriggeredEvent.Subscribe(OnActivatePlateTriggered)
DeactivatePlate.TriggeredEvent.Subscribe(OnDeactivatePlateTriggered)
# Subscribe to each flipper's activation event to award score.
Flipper1.ActivatedEvent.Subscribe(OnFlipperHit)
Flipper2.ActivatedEvent.Subscribe(OnFlipperHit)
Flipper3.ActivatedEvent.Subscribe(OnFlipperHit)
# ── Event handlers ───────────────────────────────────────────────────
# Called when a player steps on the "arm the trap" plate.
# trigger_device sends ?agent, so we unwrap it before use.
OnActivatePlateTriggered(TriggeringAgent : ?agent) : void =
Flipper1.Enable()
Flipper2.Enable()
Flipper3.Enable()
# Called when a player steps on the "disarm" plate.
OnDeactivatePlateTriggered(TriggeringAgent : ?agent) : void =
Flipper1.Disable()
Flipper2.Disable()
Flipper3.Disable()
# ActivatedEvent sends a plain `agent` (not optional).
# Award score to whoever survived the hit.
OnFlipperHit(HitAgent : agent) : void =
ScoreManager.AddScore(HitAgent, 50)
Line-by-line explanation
| Lines | What's happening |
|---|---|
@editable fields |
Declare every device as an editable field so UEFN can wire the placed instances. A bare Flipper1.Enable() without this declaration fails with Unknown identifier. |
Flipper1.Disable() × 3 |
Called immediately in OnBegin — the corridor is safe at round start. |
ActivatePlate.TriggeredEvent.Subscribe(...) |
trigger_device.TriggeredEvent sends ?agent; the handler signature must match (TriggeringAgent : ?agent). |
Flipper1.ActivatedEvent.Subscribe(OnFlipperHit) |
ActivatedEvent is listenable(agent) — it sends a plain (non-optional) agent, so the handler takes (HitAgent : agent). |
ScoreManager.AddScore(HitAgent, 50) |
Rewards the player who survived the flipper. Swap for any follow-up logic you need. |
Common patterns
Pattern 1 — Force-activate a flipper on a specific player
Use Activate(Agent) to programmatically fire the flipper at a player — useful for a boss encounter where the boss "slaps" a player with a flipper on command.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
boss_slap_device := class(creative_device):
# The flipper that acts as the boss's "hand".
@editable
BossFlipper : pinball_flipper_device = pinball_flipper_device{}
# A trigger the boss AI (or a button) fires to slap the nearest player.
@editable
SlapTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
SlapTrigger.TriggeredEvent.Subscribe(OnSlapTriggered)
# trigger_device sends ?agent — unwrap to get the real agent.
OnSlapTriggered(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
# Force the flipper to activate AS IF this agent touched it.
BossFlipper.Activate(A)
Key point:
Activate(Agent)fires the flipper's full physics response — the rotation, the knockback, and theActivatedEvent— exactly as if the agent walked into it naturally.
Pattern 2 — Timed wave: flippers cycle on/off every few seconds
Demonstrates Enable and Disable in a loop, covering the on/off lifecycle without any player input.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
flipper_wave_controller := class(creative_device):
@editable
WaveFlipper : pinball_flipper_device = pinball_flipper_device{}
# How long (seconds) each state lasts.
@editable
OnDuration : float = 3.0
@editable
OffDuration : float = 2.0
OnBegin<override>()<suspends> : void =
# Cycle forever for the duration of the round.
loop:
WaveFlipper.Enable()
Sleep(OnDuration)
WaveFlipper.Disable()
Sleep(OffDuration)
Why
loopworks here:OnBeginis a suspending context, soSleepyields without blocking other game logic. The flipper pulses on/off for the entire match.
Pattern 3 — Track total hits across all flippers and end the round
Subscribes to ActivatedEvent on multiple flippers and ends the game after a target hit count, showing event aggregation.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
flipper_hit_counter := class(creative_device):
@editable
FlipperA : pinball_flipper_device = pinball_flipper_device{}
@editable
FlipperB : pinball_flipper_device = pinball_flipper_device{}
@editable
EndGameDevice : end_game_device = end_game_device{}
# Mutable hit counter.
var HitCount : int = 0
# End the round after this many total flipper hits.
@editable
HitTarget : int = 10
OnBegin<override>()<suspends> : void =
FlipperA.ActivatedEvent.Subscribe(OnAnyFlipperHit)
FlipperB.ActivatedEvent.Subscribe(OnAnyFlipperHit)
OnAnyFlipperHit(HitAgent : agent) : void =
set HitCount = HitCount + 1
if (HitCount >= HitTarget):
EndGameDevice.Activate(HitAgent)
setkeyword: Verse requiressetto mutate avarfield — writingHitCount = HitCount + 1withoutsetis a compile error.
Gotchas
1. @editable is non-negotiable
Every pinball_flipper_device (and any other placed device) must be declared as an @editable field inside your creative_device class. Referencing a device by name without this annotation causes an Unknown identifier compile error — the Verse runtime has no way to find the placed instance.
2. ActivatedEvent sends agent, not ?agent
ActivatedEvent : listenable(agent) — the handler signature is (HitAgent : agent), not (HitAgent : ?agent). Contrast this with trigger_device.TriggeredEvent which sends ?agent and requires an if (A := MaybeAgent?): unwrap. Getting these mixed up causes a type-mismatch compile error.
3. Activate(Agent) requires a valid agent
Activate takes a plain agent — if you're calling it from a trigger_device handler you must unwrap the ?agent first (see Pattern 1). Passing an optional directly is a type error.
4. Disable does not reset mid-swing
If a flipper is mid-rotation when you call Disable(), the physics for that swing still completes. Disable prevents future activations; it does not freeze an in-progress rotation.
5. No message params on this device
pinball_flipper_device has no methods that accept message. If you add a billboard_device or similar alongside it and need localized text, remember: Verse has no StringToMessage function. Declare a helper like MyText<localizes>(S:string):message = "{S}" and pass MyText("...") instead of a raw string.
6. Sleep requires a float, not an int
When using Sleep with an editable duration field, declare it as float (e.g. OnDuration : float = 3.0). Verse does not auto-convert int to float — Sleep(3) is a compile error; Sleep(3.0) is correct.