Reference Verse

Clamp: Keeping Numbers in Bounds on Your Island

Every game eventually needs to stop a number from flying off the rails — a health bar that won't go above 100, a speed that won't drop below zero, a countdown that won't tick past its limit. Verse's built-in `Clamp` function is the one-liner that handles all of that. In this article you'll learn exactly how `Clamp` works and wire it into a real pirate-island moment: a player races across a sun-bleached dock before the tide timer runs out, and the remaining-time display is clamped so it never sho

Updated
The code on this reference page is provided as-is and did not pass the latest compile check — treat the examples as a starting point and verify in your project.
Watch the Knottrigger_device in ~90 seconds.

Overview

Clamp is a pure math function built into Verse's core library. It takes three floats — a value, a lower bound, and an upper bound — and returns the value pinched into that range:

  • If Val is below A, you get A.
  • If Val is above B, you get B.
  • If Val is between them, you get Val unchanged.

The function is robust: it handles the case where A > B by returning the median of all three arguments, and it treats NaN as greater than positive infinity, so you never get a silent bad value slipping through.

When to reach for it:

  • Clamping a health or shield value before displaying it.
  • Keeping a spawn-delay or reset-delay inside a legal device range (e.g., SetMaxTriggerCount only accepts 0–20).
  • Normalising a score multiplier so it never goes below 1× or above 5×.
  • Sanitising any float that comes from player input, physics, or arithmetic before passing it to a device method.

Clamp lives in /Verse.org/Verse — no extra import needed beyond the standard creative-device boilerplate.

API Reference

trigger_device

Used to relay events to other linked devices.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse). Inherited members are merged from trigger_base_device.

trigger_device<public> := class<concrete><final>(trigger_base_device):

Events (subscribe a handler to react):

Event Signature Description
TriggeredEvent TriggeredEvent<public>:listenable(?agent) Signaled when an agent triggers this device. Sends the agent that used this device. Returns false if no agent triggered the action (ex: it was triggered through code).

Methods (call these to make the device act):

Method Signature Description
Trigger Trigger<public>(Agent:agent):void Triggers this device with Agent being passed as the agent that triggered the action. Use an agent reference when this device is setup to require one (for instance, you want to trigger the device only with a particular agent.
Trigger Trigger<public>():void Triggers this device, causing it to activate its TriggeredEvent event.
Enable Enable<public>():void Enables this device.
Disable Disable<public>():void Disables this device.
SetMaxTriggerCount SetMaxTriggerCount<public>(MaxCount:int):void Sets the maximum amount of times this device can trigger. * 0 can be used to indicate no limit on trigger count. * MaxCount is clamped between [0,20].
GetMaxTriggerCount GetMaxTriggerCount<public>()<transacts>:int Gets the maximum amount of times this device can trigger. * 0 indicates no limit on trigger count.
GetTriggerCountRemaining GetTriggerCountRemaining<public>()<transacts>:int Returns the number of times that this device can still be triggered before hitting GetMaxTriggerCount. Returns 0 if GetMaxTriggerCount is unlimited.
SetResetDelay SetResetDelay<public>(Time:float):void Sets the time (in seconds) after triggering, before the device can be triggered again (if MaxTrigger count allows).
GetResetDelay GetResetDelay<public>()<transacts>:float Gets the time (in seconds) before the device can be triggered again (if MaxTrigger count allows).
SetTransmitDelay SetTransmitDelay<public>(Time:float):void Sets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered.
GetTransmitDelay GetTransmitDelay<public>()<transacts>:float Gets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered.

player

Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse). Inherited members are merged from agent.

player<native><public> := class<unique><persistent><module_scoped_var_weak_map_key><epic_internal>(agent):

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

The Scenario

You have a sun-drenched pirate dock. A player sprints across the planks and steps on a pressure plate at the far end. The moment they step on it, a countdown timer starts — they have 30 seconds to reach the treasure chest trigger at the dock's tip. A second trigger at the tip completes the timer and fires a success fanfare.

The twist: you want to display how many whole seconds remain when the player finishes, clamped between 0 and 30 so a laggy network tick or a sub-zero float never produces a nonsense value. You also use Clamp to safely set the trigger's MaxTriggerCount (which the API clamps to 0–20 internally, but you clamp first so your intent is explicit).

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }

# ─────────────────────────────────────────────────────────────────
# dock_race_device
# Place in your level and wire up the three @editable fields.
# ─────────────────────────────────────────────────────────────────
dock_race_device := class(creative_device):

    # The pressure plate at the START of the dock.
    @editable
    StartPlate : trigger_device = trigger_device{}

    # The trigger zone at the TIP of the dock (the finish).
    @editable
    FinishTrigger : trigger_device = trigger_device{}

    # The countdown timer (configure it for 30 s in the Details panel).
    @editable
    RaceTimer : timer_device = timer_device{}

    # How many seconds the race window lasts.
    RaceDurationSeconds : float = 30.0

    # Tracks when the race started (seconds since level load).
    var RaceStartTime : float = 0.0

    # ── Lifecycle ────────────────────────────────────────────────
    OnBegin<override>()<suspends> : void =
        # Allow the start plate to fire exactly once per race attempt.
        # SetMaxTriggerCount accepts 0-20; we clamp our desired value
        # to that range so the intent is self-documenting.
        var DesiredMax : int = 1
        StartPlate.SetMaxTriggerCount(Clamp(float[DesiredMax], 0.0, 20.0) |> int[...])
        # ↑ simpler: just pass 1 directly — shown here to illustrate Clamp on the path
        StartPlate.SetMaxTriggerCount(1)

        # Wire events.
        StartPlate.TriggeredEvent.Subscribe(OnDockStart)
        FinishTrigger.TriggeredEvent.Subscribe(OnDockFinish)
        RaceTimer.FailureEvent.Subscribe(OnTimerFailed)

    # ── Handlers ─────────────────────────────────────────────────

    # Called when the player steps on the start plate.
    OnDockStart(MaybeAgent : ?agent) : void =
        # Record the wall-clock start time.
        set RaceStartTime = GetSimulationElapsedTime()
        # Start the visual countdown for everyone.
        RaceTimer.Start()
        # Re-enable the finish trigger (it starts disabled in the Details panel).
        FinishTrigger.Enable()

    # Called when the player hits the finish trigger at the dock tip.
    OnDockFinish(MaybeAgent : ?agent) : void =
        # How many seconds elapsed?
        var Elapsed : float = GetSimulationElapsedTime() - RaceStartTime

        # Clamp elapsed to [0, RaceDurationSeconds] — guards against
        # a negative delta if clocks drift, or a value beyond the window.
        var ClampedElapsed : float = Clamp(Elapsed, 0.0, RaceDurationSeconds)

        # Remaining time, also clamped so it can never be negative.
        var Remaining : float = Clamp(RaceDurationSeconds - ClampedElapsed, 0.0, RaceDurationSeconds)

        # Mark the timer as a success for the finishing agent.
        if (A := MaybeAgent?):
            RaceTimer.Complete(A)
        else:
            RaceTimer.Complete()

        # Disable the finish trigger until the next race.
        FinishTrigger.Disable()

    # Called when the countdown hits zero before the player finishes.
    OnTimerFailed(MaybeAgent : ?agent) : void =
        # Reset everything so the player can try again.
        RaceTimer.ResetForAll()
        FinishTrigger.Disable()
        StartPlate.Enable()
        StartPlate.SetMaxTriggerCount(1)

Line-by-line highlights

Line / concept Why it matters
Clamp(Elapsed, 0.0, RaceDurationSeconds) Core usage — pins the raw elapsed float so downstream math is always safe.
Clamp(RaceDurationSeconds - ClampedElapsed, 0.0, RaceDurationSeconds) Second clamp on the result of subtraction — belt-and-suspenders against float imprecision.
StartPlate.SetMaxTriggerCount(1) Limits the plate to one fire per attempt; SetMaxTriggerCount internally clamps to [0,20].
RaceTimer.Complete(A) Signals success on the timer for the specific agent, triggering any linked fanfare devices.
RaceTimer.ResetForAll() Resets the countdown for everyone so the next attempt starts clean.
if (A := MaybeAgent?): Safe unwrap of the ?agent the event sends — never assume an agent is present.

Common patterns

Pattern 1 — Clamping a reset delay before applying it

You want players to be able to re-trigger the dock bell, but only after a cooldown. The cooldown comes from a variable that might have been set to an out-of-range value. Clamp it before calling SetResetDelay.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }

dock_bell_device := class(creative_device):

    @editable
    BellTrigger : trigger_device = trigger_device{}

    # Desired cooldown supplied by a designer — could be anything.
    @editable
    DesiredCooldownSeconds : float = 5.0

    OnBegin<override>()<suspends> : void =
        # SetResetDelay expects a non-negative float.
        # Clamp to [0.5, 60.0] — sane dock-bell range.
        var SafeCooldown : float = Clamp(DesiredCooldownSeconds, 0.5, 60.0)
        BellTrigger.SetResetDelay(SafeCooldown)

        # Confirm what was actually set.
        var ActualDelay : float = BellTrigger.GetResetDelay()

        BellTrigger.TriggeredEvent.Subscribe(OnBellRung)

    OnBellRung(MaybeAgent : ?agent) : void =
        # Fire a chained trigger (e.g. linked to a sound cue device).
        BellTrigger.Trigger()

Pattern 2 — Clamping a transmit delay for a cinematic trigger

A cannon fires on the pirate ship. You want a transmit delay so the explosion sound device fires slightly after the visual — but the delay must stay between 0.1 s and 2.0 s regardless of what a config float says.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }

cannon_fire_device := class(creative_device):

    @editable
    CannonTrigger : trigger_device = trigger_device{}

    # Designer-supplied delay in seconds.
    @editable
    ExplosionDelaySeconds : float = 0.75

    OnBegin<override>()<suspends> : void =
        # Clamp the transmit delay to a sensible cinematic window.
        var SafeDelay : float = Clamp(ExplosionDelaySeconds, 0.1, 2.0)
        CannonTrigger.SetTransmitDelay(SafeDelay)

        # Also cap how many times the cannon can fire per round.
        # SetMaxTriggerCount clamps internally to [0,20]; we mirror that.
        var MaxShots : int = 3
        CannonTrigger.SetMaxTriggerCount(Clamp(float[MaxShots], 0.0, 20.0) |> int[...])
        # Simpler direct call — shown both ways for illustration:
        CannonTrigger.SetMaxTriggerCount(3)

        CannonTrigger.TriggeredEvent.Subscribe(OnCannonFired)

    OnCannonFired(MaybeAgent : ?agent) : void =
        # How many shots remain? 0 means unlimited, so guard that.
        var Remaining : int = CannonTrigger.GetTriggerCountRemaining()
        # Clamp remaining to [0, 3] for a display-safe value.
        var DisplayRemaining : int = int[Clamp(float[Remaining], 0.0, 3.0)]

Pattern 3 — Clamping timer duration before starting a lap race

A lagoon lap race lets the host set a custom duration. Clamp it before calling SetActiveDuration so the timer never gets a zero or absurdly large value.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }

lagoon_lap_device := class(creative_device):

    @editable
    LapTimer : timer_device = timer_device{}

    @editable
    StartGate : trigger_device = trigger_device{}

    # Host-configurable lap duration; might be 0 or 9999 by mistake.
    @editable
    DesiredLapSeconds : float = 45.0

    OnBegin<override>()<suspends> : void =
        StartGate.TriggeredEvent.Subscribe(OnRaceStart)
        LapTimer.SuccessEvent.Subscribe(OnLapComplete)
        LapTimer.FailureEvent.Subscribe(OnLapFailed)

    OnRaceStart(MaybeAgent : ?agent) : void =
        if (A := MaybeAgent?):
            # Clamp the designer value to [10.0, 120.0] seconds.
            var SafeDuration : float = Clamp(DesiredLapSeconds, 10.0, 120.0)
            LapTimer.SetActiveDuration(SafeDuration, A)
            LapTimer.Start(A)

    OnLapComplete(MaybeAgent : ?agent) : void =
        LapTimer.ResetForAll()
        StartGate.Enable()

    OnLapFailed(MaybeAgent : ?agent) : void =
        LapTimer.ResetForAll()
        StartGate.Enable()

Gotchas

1. Clamp works on float, not int — convert explicitly

Verse does not auto-convert between int and float. If you have an int value you want to clamp, cast it first with float[MyInt], clamp, then cast back with int[Result] (which truncates toward zero).

# WRONG — type mismatch, won't compile:
# var Safe : int = Clamp(MyIntValue, 0, 20)

# CORRECT:
var Safe : int = int[Clamp(float[MyIntValue], 0.0, 20.0)]

2. Argument order doesn't matter — but be explicit anyway

Clamp(Val, A, B) returns the median of all three, so Clamp(5.0, 10.0, 2.0) still returns 5.0 (the median). This robustness is intentional, but relying on it makes code hard to read. Always pass A as your intended lower bound and B as your upper bound.

3. NaN is treated as greater than +Inf

If Val is NaN (e.g., from a division by zero), Clamp returns B (the upper bound). This is a safe fallback, but you should still guard the inputs that could produce NaN — especially division results.

4. ?agent events must be unwrapped before use

Every event on trigger_device and timer_device sends ?agent (an optional agent). You must unwrap it before passing to agent-specific device methods:

# WRONG — ?agent is not agent:
# RaceTimer.Complete(MaybeAgent)  # compile error

# CORRECT:
if (A := MaybeAgent?):
    RaceTimer.Complete(A)

5. SetMaxTriggerCount clamps to [0, 20] internally — but document your intent

The device will silently clamp any value you pass. If you pass 50, you get 20. Calling Clamp yourself before the call makes the constraint visible to future readers and prevents confusion when GetMaxTriggerCount() returns a different number than you set.

6. message parameters need localised values

If you ever display remaining time through a device that takes a message parameter, you cannot pass a raw string. Declare a localised helper:

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

Then pass TimeLabel("Time: 12s"). There is no StringToMessage function in Verse.

Guides & scripts that use trigger_device

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

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