Reference Devices compiles

timer_device: Countdowns, Urgency, and Timed Challenges

The `timer_device` is your go-to tool whenever a game moment lives or dies by the clock — a bomb defusal, a speedrun lap, a shrinking safe zone countdown. It fires events on success or failure, supports per-player or global timing, and lets Verse code start, pause, resume, or force-complete the clock at any moment. This article walks you through the full API with a real escape-room scenario and focused pattern snippets.

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

Overview

The timer_device tracks elapsed or remaining time and signals your Verse code when something meaningful happens: the clock hits zero (SuccessEvent or FailureEvent), or the timer enters its final-stretch Urgency Mode (StartUrgencyModeEvent). You can run it as a countdown (time runs out → failure) or a stopwatch (player finishes before time → success), and you control every moment of its lifecycle from Verse: start, pause, resume, reset, or force-complete.

Reach for timer_device when you need:

  • A round countdown that locks doors when it expires
  • A per-player speedrun lap board
  • A bomb-defusal sequence that pauses while a cutscene plays
  • Urgency-mode music or VFX that trigger in the final seconds

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: An escape room. A button_device starts the clock. If the player presses a second button (the "solve" button) before time runs out, Complete fires SuccessEvent and a barrier drops. If the clock hits zero first, FailureEvent fires and the room resets. When urgency mode kicks in, we pause briefly to flash a warning, then resume.

escape_room_manager := class(creative_device):

    # Wire these up in the UEFN editor
    @editable
    RoomTimer : timer_device = timer_device{}

    @editable
    StartButton : button_device = button_device{}

    @editable
    SolveButton : button_device = button_device{}

    @editable
    ExitBarrier : barrier_device = barrier_device{}

    # Called once when the experience begins
    OnBegin<override>()<suspends> : void =
        # Subscribe to timer lifecycle events
        RoomTimer.SuccessEvent.Subscribe(OnRoomSolved)
        RoomTimer.FailureEvent.Subscribe(OnRoomFailed)
        RoomTimer.StartUrgencyModeEvent.Subscribe(OnUrgency)

        # Subscribe to button interactions
        StartButton.InteractedWithEvent.Subscribe(OnStartPressed)
        SolveButton.InteractedWithEvent.Subscribe(OnSolvePressed)

        # Make sure the exit is locked at the start
        ExitBarrier.Enable()

    # Player pressed the START button — kick off the clock
    OnStartPressed(Agent : agent) : void =
        RoomTimer.Start(Agent)   # per-agent start (respects IsStatePerAgent)

    # Player pressed the SOLVE button — force a success
    OnSolvePressed(Agent : agent) : void =
        RoomTimer.Complete(Agent)  # triggers SuccessEvent for this agent

    # Timer fired SuccessEvent — player beat the clock
    OnRoomSolved(Agent : ?agent) : void =
        if (A := Agent?):
            ExitBarrier.Disable()   # drop the barrier for everyone
            Print("Escape room solved!")
        else:
            ExitBarrier.Disable()   # triggered globally

    # Timer fired FailureEvent — clock hit zero
    OnRoomFailed(Agent : ?agent) : void =
        RoomTimer.ResetForAll()    # reset clock for all players
        Print("Time's up! Resetting...")

    # Timer entered urgency mode — pause briefly, then resume
    OnUrgency(Agent : ?agent) : void =
        spawn:
            UrgencyPause()

    UrgencyPause()<suspends> : void =
        RoomTimer.PauseForAll()    # freeze the clock
        Sleep(1.5)                 # hold for dramatic effect
        RoomTimer.ResumeForAll()   # let it tick again

Line-by-line highlights:

Line What it does
RoomTimer.SuccessEvent.Subscribe(OnRoomSolved) Registers a handler that fires when Complete() is called or the stopwatch finishes in time
RoomTimer.FailureEvent.Subscribe(OnRoomFailed) Fires when a countdown reaches zero without being completed
RoomTimer.StartUrgencyModeEvent.Subscribe(OnUrgency) Fires when the timer crosses into its configured urgency threshold
RoomTimer.Start(Agent) Starts the clock for one specific player (per-agent mode)
RoomTimer.Complete(Agent) Immediately marks the timer successful for that player
RoomTimer.ResetForAll() Stops and resets the clock for every player in the session
RoomTimer.PauseForAll() / RoomTimer.ResumeForAll() Freeze and unfreeze the clock globally
if (A := Agent?): Safe unwrap of the ?agent the event sends — always do this before using the agent

Common patterns

Pattern 1 — Dynamic duration: set the clock length at runtime

Sometimes you want difficulty scaling — easy mode gets 120 s, hard mode gets 45 s. Use SetActiveDuration while the timer is running, or set it before Start.

difficulty_timer_manager := class(creative_device):

    @editable
    RaceTimer : timer_device = timer_device{}

    @editable
    HardModeButton : button_device = button_device{}

    # How long the race lasts in easy mode (seconds)
    EasyDuration : float = 120.0
    HardDuration : float = 45.0

    OnBegin<override>()<suspends> : void =
        HardModeButton.InteractedWithEvent.Subscribe(OnHardModeChosen)
        RaceTimer.SuccessEvent.Subscribe(OnRaceSuccess)
        RaceTimer.FailureEvent.Subscribe(OnRaceFailure)

        # Default: start with easy duration for all players
        RaceTimer.StartForAll()

    OnHardModeChosen(Agent : agent) : void =
        # Shrink the remaining time for this specific agent
        RaceTimer.SetActiveDuration(HardDuration, Agent)

    OnRaceSuccess(Agent : ?agent) : void =
        # Report how long the max duration was
        MaxSecs : float = RaceTimer.GetMaxDuration()
        Print("Race complete! Max allowed: {MaxSecs}s")

    OnRaceFailure(Agent : ?agent) : void =
        RaceTimer.ResetForAll()
        Print("Race failed — resetting for all players.")

Key calls: StartForAll(), SetActiveDuration(float, agent), GetMaxDuration().


Pattern 2 — Persistence: save and load a player's best lap

For a speedrun leaderboard you want to persist each player's timer state across sessions. Save and Load handle this.

lap_persistence_manager := class(creative_device):

    @editable
    LapTimer : timer_device = timer_device{}

    @editable
    LapStartTrigger : trigger_device = trigger_device{}

    @editable
    LapEndTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        LapStartTrigger.TriggeredEvent.Subscribe(OnLapStart)
        LapEndTrigger.TriggeredEvent.Subscribe(OnLapEnd)
        LapTimer.SuccessEvent.Subscribe(OnLapSuccess)

    OnLapStart(Agent : ?agent) : void =
        if (A := Agent?):
            # Load any previously saved state for this player
            LapTimer.Load(A)
            LapTimer.Start(A)

    OnLapEnd(Agent : ?agent) : void =
        if (A := Agent?):
            # Force success so SuccessEvent fires
            LapTimer.Complete(A)

    OnLapSuccess(Agent : ?agent) : void =
        if (A := Agent?):
            # Persist the completed lap data for this player
            LapTimer.Save(A)
            ActiveSecs : float = LapTimer.GetActiveDuration(A)
            Print("Lap saved! Active duration was: {ActiveSecs}s")

Key calls: Load(agent), Start(agent), Complete(agent), Save(agent), GetActiveDuration(agent).


Pattern 3 — Per-agent vs global mode check, plus urgency wiring

Before branching on per-player vs. global logic, check IsStatePerAgent. This pattern also shows Enable / Disable gating.

adaptive_timer_manager := class(creative_device):

    @editable
    ChallengeTimer : timer_device = timer_device{}

    @editable
    ActivateTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        # Disable the timer until a trigger activates it
        ChallengeTimer.Disable()
        ActivateTrigger.TriggeredEvent.Subscribe(OnActivate)
        ChallengeTimer.StartUrgencyModeEvent.Subscribe(OnUrgencyStarted)

    OnActivate(Agent : ?agent) : void =
        ChallengeTimer.Enable()   # allow the device to receive signals
        # Branch on whether state is tracked per-player or globally
        if (ChallengeTimer.IsStatePerAgent[]):
            if (A := Agent?):
                ChallengeTimer.Start(A)   # per-player start
        else:
            ChallengeTimer.StartForAll()  # global start

    OnUrgencyStarted(Agent : ?agent) : void =
        # Urgency mode began — could trigger VFX, music, etc.
        Print("Urgency mode active!")
        # Demonstrate Pause then Resume for all
        spawn:
            UrgencySequence()

    UrgencySequence()<suspends> : void =
        ChallengeTimer.PauseForAll()
        Sleep(0.5)
        ChallengeTimer.ResumeForAll()

Key calls: Disable(), Enable(), IsStatePerAgent[], Start(agent), StartForAll(), PauseForAll(), ResumeForAll().

Gotchas

1. ?agent events always need unwrapping

SuccessEvent, FailureEvent, and StartUrgencyModeEvent all send ?agent (an optional agent). If you try to use the value directly without unwrapping, the compiler rejects it. Always guard:

OnSuccess(Agent : ?agent) : void =
    if (A := Agent?):
        # A is now a concrete `agent` — safe to use

2. IsStatePerAgent uses the [] failure-context syntax

IsStatePerAgent is a <decides> function — it succeeds or fails rather than returning a bool. Call it inside an if with square brackets:

if (ChallengeTimer.IsStatePerAgent[]):
    # per-agent path

Not if (ChallengeTimer.IsStatePerAgent() = true) — that won't compile.

3. SetActiveDuration only works while the timer is active

Calling SetActiveDuration on a timer that hasn't been started yet has no effect. Start the timer first, then adjust the remaining time.

4. PauseForAll / ResumeForAll inside a spawn

If you call PauseForAll() followed by Sleep() followed by ResumeForAll() in a handler, you must spawn a coroutine — event handlers are not <suspends> contexts. Forgetting the spawn causes a compile error.

5. message parameters need localized wrappers

If you ever call button_device.SetInteractionText alongside your timer logic, remember that message is not a plain string. Declare a localizer helper:

MyText<localizes>(S : string) : message = "{S}"

Then pass MyText("Press to start timer"). There is no StringToMessage function.

6. Enable / Disable gate ALL signals

A disabled timer_device will not fire SuccessEvent, FailureEvent, or StartUrgencyModeEvent — even if the underlying clock would have expired. Always call Enable() before Start() if you previously disabled the device.

7. ResetForAll stops the clock

ResetForAll() both resets the time AND stops the timer. If you want to restart immediately after a reset, call Start() or StartForAll() right after.

Device Settings & Options

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

Timer Device settings and options panel in the UEFN editor — Duration, Timer Name, Timer Direction, Start Timer At Game Start, Reset Timer If Failing Team or Cla..., Disable Timer If Failing Team or C..
Timer Device — User Options in the UEFN editor: Duration, Timer Name, Timer Direction, Start Timer At Game Start, Reset Timer If Failing Team or Cla..., Disable Timer If Failing Team or C..
⚙️ Settings on this device (6)

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

Duration
Timer Name
Timer Direction
Start Timer At Game Start
Reset Timer If Failing Team or Cla...
Disable Timer If Failing Team or C..

Guides & scripts that use timer_device

Step-by-step tutorials that put this object to work.

Build your own lesson with timer_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 →