Overview
The timer_device tracks how long something takes, either counting down toward a deadline (countdown mode) or counting up as a stopwatch. When the clock hits zero the device fires SuccessEvent or FailureEvent depending on how it was configured, and a separate StartUrgencyModeEvent fires when the remaining time drops into the "urgency" threshold you set in the device properties panel.
Reach for timer_device when you need:
- A timed challenge (swim from the dock to the buoy before the wave hits)
- A survival window (hold the clifftop for 60 seconds)
- A per-player clock that can be paused mid-round and resumed later
- Persistent best-times saved between sessions via
Save/Load
Because the device manages its own UI widget and urgency flash, you get a polished countdown HUD for free — no custom UI code required for the basic case.
API Reference
timer_device
Provides a way to keep track of the time something has taken, either for scoreboard purposes, or to trigger actions. It can be configured in several ways, either acting as a countdown to an event that is triggered at the end, or as a stopwatch for an action that needs to be completed before a set time runs out.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
timer_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
SuccessEvent |
SuccessEvent<public>:listenable(?agent) |
Signaled when the timer completes or ends with success. Sends the agent that activated the timer, if any. |
FailureEvent |
FailureEvent<public>:listenable(?agent) |
Signaled when the timer completes or ends with failure. Sends the agent that activated the timer, if any. |
StartUrgencyModeEvent |
StartUrgencyModeEvent<public>:listenable(?agent) |
Signaled when the timer enters Urgency Mode. Sends the agent that activated the timer, if any. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>(Agent:agent):void |
Enables this device for Agent. |
Enable |
Enable<public>():void |
Enables this device. |
Disable |
Disable<public>(Agent:agent):void |
Disables this device for Agent. While disabled this device will not receive signals. |
Disable |
Disable<public>():void |
Disables this device. While disabled this device will not receive signals. |
ResetForAll |
ResetForAll<public>(Agent:agent):void |
Resets the timer back to its base time and stops it for all agents. |
ResetForAll |
ResetForAll<public>():void |
Resets the timer back to its base time and stops it for all agents. |
Start |
Start<public>(Agent:agent):void |
Starts the timer for Agent. |
Start |
Start<public>():void |
Starts the timer. |
Pause |
Pause<public>(Agent:agent):void |
Pauses the timer for Agent. |
Pause |
Pause<public>():void |
Pauses the timer. |
Resume |
Resume<public>(Agent:agent):void |
Resumes the timer for Agent. |
Resume |
Resume<public>():void |
Resumes the timer. |
Complete |
Complete<public>(Agent:agent):void |
Completes the timer for Agent. |
Complete |
Complete<public>():void |
Completes the timer. |
StartForAll |
StartForAll<public>(Agent:agent):void |
Starts the timer for all agents. |
StartForAll |
StartForAll<public>():void |
Starts the timer for all agents. |
PauseForAll |
PauseForAll<public>(Agent:agent):void |
Pauses the timer for all agents. |
PauseForAll |
PauseForAll<public>():void |
Pauses the timer for all agents. |
ResumeForAll |
ResumeForAll<public>(Agent:agent):void |
Resumes the timer for all agents. |
ResumeForAll |
ResumeForAll<public>():void |
Resumes the timer for all agents. |
CompleteForAll |
CompleteForAll<public>(Agent:agent):void |
Completes the timer for all agents. |
CompleteForAll |
CompleteForAll<public>():void |
Completes the timer for all agents. |
Save |
Save<public>(Agent:agent):void |
Saves this device's data for Agent. |
Load |
Load<public>(Agent:agent):void |
Loads this device's saved data for Agent. |
ClearPersistenceData |
ClearPersistenceData<public>(Agent:agent):void |
Clears this device's saved data for Agent. |
ClearPersistenceDataForAll |
ClearPersistenceDataForAll<public>(Agent:agent):void |
Clears this device's saved data for all agents. |
ClearPersistenceDataForAll |
ClearPersistenceDataForAll<public>():void |
Clears this device's saved data for all agents. |
SetActiveDuration |
SetActiveDuration<public>(Time:float, Agent:agent):void |
Sets the remaining time (in seconds) on the timer, if active, on Agent. |
SetActiveDuration |
SetActiveDuration<public>(Time:float):void |
Sets the remaining time (in seconds) on the timer, if active. Use this function if the timer is set to use the same time for all agent's. |
GetActiveDuration |
GetActiveDuration<public>(Agent:agent)<transacts>:float |
Returns the remaining time (in seconds) on the timer for Agent. |
GetActiveDuration |
GetActiveDuration<public>()<transacts>:float |
Returns the remaining time (in seconds) on the timer if it is set to be global. |
SetLapTime |
SetLapTime<public>(Agent:agent):void |
Sets the lap time indicator for Agent. |
SetLapTimeForAll |
SetLapTimeForAll<public>(Agent:agent):void |
Sets the lap time indicator for all agents. |
SetLapTimeForAll |
SetLapTimeForAll<public>():void |
Sets the lap time indicator for all agents. |
SetMaxDuration |
SetMaxDuration<public>(Time:float):void |
Sets the maximum duration of the timer (in seconds). |
GetMaxDuration |
GetMaxDuration<public>()<transacts>:float |
Returns the maximum duration of the timer (in seconds). |
IsStatePerAgent |
IsStatePerAgent<public>()<transacts><decides>:void |
Succeeds if this device is tracking timer state for each individual agent independently. Fails if state is being tracked globally for all agent's. |
Walkthrough
Scenario — Dock Sprint: Beat the Tide
The player steps onto a pressure plate at the end of the sunny dock. A 30-second countdown starts. If they reach the buoy trigger before time runs out the timer is completed (success). If the clock hits zero first, failure fires and they're sent back. While the timer is running, stepping off the dock into the water pauses the clock (you're swimming, not sprinting); stepping back on resumes it.
Devices needed in your UEFN scene:
- 1 ×
timer_device— the countdown clock (set Max Duration = 30 s in properties) - 1 ×
button_device(the dock start plate — we model it as a trigger for simplicity) - 1 ×
trigger_device(the buoy finish zone) - 1 ×
trigger_device(the water zone — pause trigger) - 1 ×
trigger_device(return-to-dock zone — resume trigger)
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
# dock_sprint_manager — attach to a Verse Device actor in your scene.
dock_sprint_manager := class(creative_device):
# Wire these up in the UEFN Details panel.
@editable
DockTimer : timer_device = timer_device{}
@editable
StartPlate : trigger_device = trigger_device{}
@editable
BuoyFinish : trigger_device = trigger_device{}
@editable
WaterZone : trigger_device = trigger_device{}
@editable
ReturnZone : trigger_device = trigger_device{}
# Called once when the island session begins.
OnBegin<override>()<suspends> : void =
# Subscribe to the dock start plate — a player stepping on it starts the clock.
StartPlate.TriggeredEvent.Subscribe(OnDockStart)
# Subscribe to the buoy finish zone — reaching it completes the timer (success).
BuoyFinish.TriggeredEvent.Subscribe(OnBuoyReached)
# Pause the clock when the player splashes into the water zone.
WaterZone.TriggeredEvent.Subscribe(OnEnterWater)
# Resume when they climb back onto the dock.
ReturnZone.TriggeredEvent.Subscribe(OnReturnToDock)
# React to the timer's own success/failure events.
DockTimer.SuccessEvent.Subscribe(OnTimerSuccess)
DockTimer.FailureEvent.Subscribe(OnTimerFailure)
# React when urgency mode kicks in (e.g. last 10 seconds).
DockTimer.StartUrgencyModeEvent.Subscribe(OnUrgency)
# --- Event handlers ---
# Player stepped on the dock start plate — start the countdown for that player.
OnDockStart(MaybeAgent : ?agent) : void =
if (Agent := MaybeAgent?):
DockTimer.Start(Agent)
# Player reached the buoy — mark the timer complete (triggers SuccessEvent).
OnBuoyReached(MaybeAgent : ?agent) : void =
if (Agent := MaybeAgent?):
DockTimer.Complete(Agent)
# Player fell into the water — pause their personal clock.
OnEnterWater(MaybeAgent : ?agent) : void =
if (Agent := MaybeAgent?):
DockTimer.Pause(Agent)
# Player climbed back onto the dock — resume their clock.
OnReturnToDock(MaybeAgent : ?agent) : void =
if (Agent := MaybeAgent?):
DockTimer.Resume(Agent)
# SuccessEvent sends ?agent — unwrap before use.
OnTimerSuccess(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
# Save the winning time so it persists across sessions.
DockTimer.Save(A)
# FailureEvent sends ?agent — unwrap before use.
OnTimerFailure(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
# Reset the clock so they can try again immediately.
DockTimer.ResetForAll(A)
# StartUrgencyModeEvent — clock is in the red zone.
OnUrgency(MaybeAgent : ?agent) : void =
# You could trigger a visual effect device here.
# The timer's built-in urgency flash already fires automatically.
if (A := MaybeAgent?):
DockTimer.Resume(A) # ensure it's still ticking (no-op if already running)```
### Line-by-line explanation
| Lines | What's happening |
|---|---|
| `@editable` fields | Every device reference must be declared as an editable field on the class — you cannot reference a placed device any other way. Wire them in the UEFN Details panel. |
| `OnBegin<override>()<suspends>` | The island entry point. All subscriptions happen here before any player arrives. |
| `StartPlate.TriggeredEvent.Subscribe(OnDockStart)` | `trigger_device.TriggeredEvent` sends a plain `agent` (not `?agent`), so the handler signature is `(Agent : agent)`. |
| `DockTimer.Start(Agent)` | Starts the countdown **for that specific player only** — other players' clocks are unaffected. |
| `DockTimer.Complete(Agent)` | Immediately ends the timer in success state, firing `SuccessEvent`. |
| `DockTimer.Pause(Agent)` / `Resume(Agent)` | Freezes and unfreezes the per-player clock without resetting it. |
| `OnTimerSuccess(MaybeAgent : ?agent)` | `SuccessEvent` is a `listenable(?agent)` — the payload is optional. Always unwrap with `if (A := MaybeAgent?):`. |
| `DockTimer.Save(A)` | Persists the current timer state for that agent so their best time survives a session restart. |
| `DockTimer.ResetForAll(A)` | Resets **all** players' clocks back to the configured max duration and stops them. |
---
## Common patterns
### Pattern 1 — Global countdown: everyone races together
A cove treasure hunt where every player on the island shares one visible clock. When time runs out for the group, the whole island fails.
```verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
cove_treasure_hunt := class(creative_device):
@editable
HuntTimer : timer_device = timer_device{}
@editable
TreasureChest : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
# Start the clock for EVERY player simultaneously.
HuntTimer.StartForAll()
TreasureChest.TriggeredEvent.Subscribe(OnChestOpened)
HuntTimer.FailureEvent.Subscribe(OnHuntFailed)
OnChestOpened(Agent : agent) : void =
# One player found the treasure — complete for everyone (all win).
HuntTimer.CompleteForAll()
OnHuntFailed(MaybeAgent : ?agent) : void =
# Time ran out — reset the whole island's timer so the round can restart.
HuntTimer.ResetForAll()
Key calls: StartForAll(), CompleteForAll(), ResetForAll() — the no-argument overloads operate on every player at once.
Pattern 2 — Dynamic duration: adjust the clock mid-round
A clifftop survival mode where surviving players get bonus seconds added to their personal clock each time they eliminate an opponent.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
clifftop_survival := class(creative_device):
@editable
SurvivalTimer : timer_device = timer_device{}
@editable
EliminationTrigger : trigger_device = trigger_device{}
# Bonus seconds awarded per elimination.
BonusSeconds : float = 10.0
OnBegin<override>()<suspends> : void =
SurvivalTimer.StartForAll()
EliminationTrigger.TriggeredEvent.Subscribe(OnElimination)
OnElimination(Agent : agent) : void =
# Read how much time is left, then add the bonus.
CurrentDuration : float = SurvivalTimer.GetActiveDuration(Agent)
SurvivalTimer.SetActiveDuration(CurrentDuration + BonusSeconds, Agent)
Key calls: GetActiveDuration(Agent) reads the remaining seconds; SetActiveDuration(Time, Agent) writes a new remaining time — both are per-player.
Pattern 3 — Persistence: load a saved best time on session start
A shore-side time trial that remembers each player's personal best across sessions.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
shore_time_trial := class(creative_device):
@editable
TrialTimer : timer_device = timer_device{}
@editable
StartGate : trigger_device = trigger_device{}
@editable
FinishGate : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
StartGate.TriggeredEvent.Subscribe(OnRaceStart)
FinishGate.TriggeredEvent.Subscribe(OnRaceFinish)
TrialTimer.SuccessEvent.Subscribe(OnNewBestTime)
OnRaceStart(Agent : agent) : void =
# Load any previously saved state for this player, then start.
TrialTimer.Load(Agent)
TrialTimer.Start(Agent)
OnRaceFinish(Agent : agent) : void =
TrialTimer.Complete(Agent)
OnNewBestTime(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
# Persist the finishing time for next session.
TrialTimer.Save(A)
Key calls: Load(Agent) restores persisted state before starting; Save(Agent) commits the result after success.
Gotchas
1. SuccessEvent and FailureEvent send ?agent, not agent
SuccessEvent and FailureEvent are listenable(?agent). Your handler must accept (?agent) and unwrap it:
# CORRECT
OnSuccess(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
DockTimer.Save(A)
# WRONG — compile error: type mismatch
OnSuccess(Agent : agent) : void = ...
StartUrgencyModeEvent is also listenable(?agent) — same rule applies.
2. trigger_device.TriggeredEvent sends plain agent, not ?agent
The trigger's event is listenable(agent) (non-optional), so its handler takes (Agent : agent) directly — no unwrap needed. Don't mix these up.
3. Per-agent vs. global overloads
Methods like Start, Pause, Resume, Complete, and ResetForAll each have two overloads: one that takes an Agent (per-player) and one with no argument (global). The global ResetForAll() resets all players even when called with an agent argument — read the signature carefully. StartForAll() / PauseForAll() / ResumeForAll() / CompleteForAll() are the explicit all-player variants.
4. SetActiveDuration only works while the timer is active
Calling SetActiveDuration on a timer that hasn't been started yet has no effect. Always call Start(Agent) first, then adjust the duration.
5. int ↔ float is never automatic
GetActiveDuration returns a float. If you do arithmetic with an integer literal, write it as a float literal (10.0, not 10) or you'll get a type-mismatch compile error.
6. Devices must be @editable fields
You cannot write timer_device{}.Start() inline — a bare constructor gives you a disconnected object with no island presence. Every device reference must be an @editable field wired in the UEFN Details panel.
7. ResetForAll(Agent) still resets everyone
Despite accepting an Agent parameter, ResetForAll(Agent) resets the timer for all agents, not just the one passed in. If you want a single-player reset, use Start(Agent) to restart just that player's clock.