Overview
A lerp (linear interpolation) is the simplest way to blend between two numbers. Verse's built-in Lerp(From, To, Parameter) returns From*(1 - Parameter) + To*Parameter. When Parameter is 0.0 you get From; at 1.0 you get To; at 0.5 you get exactly the midpoint. Feed it a Parameter that crawls from 0 to 1 over time (using Sleep in a loop) and any value — a height, a light intensity, a countdown position — moves smoothly instead of teleporting.
That's the game problem it solves: raw device state changes are instant and jarring. Reach for Lerp whenever you want a gradual transition — a sunlit cove gate rising, a pirate-ship plank easing down, a dock beacon fading up. Lerp itself isn't a device; it's a math built-in you drive from a creative_device script, usually kicked off by a trigger_device and timed against a timer_device.
In this article the island moment is a sunny lagoon cove: a player steps on a pressure plate wired to a trigger, a countdown timer starts, and a stone gate smoothly lerps up out of the water to reveal a hidden cove.
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. |
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. |
vector3
3-dimensional vector with
floatcomponents.
Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse).
vector3<native><public> := struct<concrete><computes><persistable><uht_comparable>:
Walkthrough
Here's the full sunny-cove scene. When the player triggers the plate, we start a timer_device, then run a coroutine that lerps a float height from the closed position up to the open position over 3 seconds. We also read that height into a vector3 so you can see how lerp feeds real position math. When the timer's SuccessEvent fires, we make sure the gate is fully open.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }
# The sunny lagoon cove gate controller.
cove_gate_device := class(creative_device):
# The pressure plate at the water's edge that starts everything.
@editable
StartTrigger : trigger_device = trigger_device{}
# A countdown timer shown to the player while the gate rises.
@editable
RiseTimer : timer_device = timer_device{}
# Closed water-line height (cm) and fully-open height (cm).
ClosedHeight : float = 0.0
OpenHeight : float = 500.0
OnBegin<override>()<suspends>:void =
# React when a player steps on the plate.
StartTrigger.TriggeredEvent.Subscribe(OnPlateStepped)
# React when the countdown finishes cleanly.
RiseTimer.SuccessEvent.Subscribe(OnTimerSuccess)
# Handler for the pressure plate. Runs the smooth rise.
OnPlateStepped(Agent : ?agent):void =
# Give the countdown a 3 second window and start it for everyone.
RiseTimer.SetMaxDuration(3.0)
RiseTimer.StartForAll()
# Kick off the async lerp animation.
spawn { RaiseGate() }
# Smoothly lerp the gate height from closed to open over 3 seconds.
RaiseGate()<suspends>:void =
Steps : int = 60 # 60 frames of animation
Duration : float = 3.0 # total seconds
var Step : int = 0
loop:
if (Step > Steps):
break
# Parameter walks 0.0 -> 1.0 across the steps.
Parameter : float = Step * 1.0 / (Steps * 1.0)
# The core call: blend closed height toward open height.
Height : float = Lerp(ClosedHeight, OpenHeight, Parameter)
# Feed the height into a position (X and Y stay put on the shore).
GatePosition : vector3 = vector3{ X := 1000.0, Y := 2000.0, Z := Height }
# (Here you'd move a prop to GatePosition; height is now animated.)
set Step = Step + 1
Sleep(Duration / (Steps * 1.0))
# When the countdown succeeds, snap the gate to its final open height.
OnTimerSuccess(Agent : ?agent):void =
FinalHeight : float = Lerp(ClosedHeight, OpenHeight, 1.0)
GatePosition : vector3 = vector3{ X := 1000.0, Y := 2000.0, Z := FinalHeight }
# Gate guaranteed fully open even if timing drifted a frame.
Line by line:
@editable StartTrigger : trigger_device— you MUST declare the placed device as an editable field so Verse can find it; a baretrigger_device.Trigger()fails with 'Unknown identifier'.OnBegin<override>()<suspends>:void =— the entry point. We subscribe our handlers here.StartTrigger.TriggeredEvent.Subscribe(OnPlateStepped)— wires the plate's realTriggeredEventto our method.RiseTimer.SetMaxDuration(3.0)thenRiseTimer.StartForAll()— realtimer_devicemethods that give the player a visible 3-second countdown.spawn { RaiseGate() }— launches the animation coroutine without blocking the handler.Parameter : float = Step * 1.0 / (Steps * 1.0)— Verse never auto-converts int↔float, so we multiply by1.0to make floats before dividing.Lerp(ClosedHeight, OpenHeight, Parameter)— the heart of it: at Step 0 the height is 0, at Step 60 it's 500, smoothly in between.Sleep(Duration / (Steps * 1.0))— waits ~0.05s per frame so the loop paces the animation across 3 seconds.OnTimerSuccessusesLerp(..., 1.0)to guarantee the exact open height when the countdown ends.
Common patterns
1. Fade a dock beacon's brightness with EaseIn timing. Instead of a straight Parameter, you can shape the curve. Here we still use Lerp for the value but let the parameter ramp accelerate for a gentle sunrise glow, driven off a trigger with a reset delay.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
beacon_glow_device := class(creative_device):
@editable
GlowTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends>:void =
# Let the beacon re-trigger every 5 seconds.
GlowTrigger.SetResetDelay(5.0)
GlowTrigger.TriggeredEvent.Subscribe(OnGlow)
OnGlow(Agent : ?agent):void =
spawn { FadeUp() }
FadeUp()<suspends>:void =
var T : float = 0.0
loop:
if (T > 1.0):
break
# EaseIn-style ramp: square the parameter for a slow start.
Eased : float = T * T
Brightness : float = Lerp(0.0, 100.0, Eased)
# (Apply Brightness to your light prop here.)
set T = T + 0.05
Sleep(0.05)
2. Pause and resume a lerp with a timer's controls. A pirate-ship plank lowers, but if the timer is paused the animation waits. This shows Pause/Resume and Complete on timer_device.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
plank_device := class(creative_device):
@editable
PlankTimer : timer_device = timer_device{}
@editable
LowerButton : trigger_device = trigger_device{}
OnBegin<override>()<suspends>:void =
LowerButton.TriggeredEvent.Subscribe(OnLower)
PlankTimer.SuccessEvent.Subscribe(OnDone)
OnLower(Agent : ?agent):void =
PlankTimer.SetActiveDuration(2.0, GetDefaultAgent(Agent))
PlankTimer.StartForAll()
GetDefaultAgent(Agent : ?agent)<decides><transacts>:agent =
Agent?
OnDone(Agent : ?agent):void =
# Plank hits the water: final lerp position at 100%.
FinalDrop : float = Lerp(300.0, 0.0, 1.0)
# (Move plank down to FinalDrop.)
PlankTimer.CompleteForAll()
3. Lerp toward a target only when urgency mode kicks in. Speed up a lagoon light's color shift when the timer enters StartUrgencyModeEvent.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
urgency_light_device := class(creative_device):
@editable
CountdownTimer : timer_device = timer_device{}
OnBegin<override>()<suspends>:void =
CountdownTimer.StartUrgencyModeEvent.Subscribe(OnUrgent)
CountdownTimer.SetMaxDuration(10.0)
CountdownTimer.StartForAll()
OnUrgent(Agent : ?agent):void =
spawn { FlashRed() }
FlashRed()<suspends>:void =
var T : float = 0.0
loop:
if (T > 1.0):
break
# Lerp light from calm (0) to alarm (255) fast.
RedValue : float = Lerp(0.0, 255.0, T)
# (Apply RedValue to the cove warning light.)
set T = T + 0.1
Sleep(0.02)
Gotchas
- No int↔float auto-conversion.
Lerptakes threefloats. If your loop counter is anint, convert with* 1.0before dividing or passing it in.Step / Stepson ints is integer division and will give you 0 for most of the loop. Lerpextrapolates past 1.0.Lerp(0.0, 10.0, 2.0)returns20.0, not10.0. If you loop yourParameterpast 1.0 the gate will overshoot. Clamp with abreakwhenT > 1.0(as shown) or guard your parameter.- Do the animation in a coroutine.
Sleeprequires a<suspends>context. Call your lerp loop viaspawn { ... }from an event handler so the handler returns immediately and the animation runs alongside the game. - Unwrap
?agentbefore use. Event handlers receive(Agent : ?agent). If you need the agent (e.g. forSetActiveDuration(Time, Agent:agent)), unwrap it withif (A := Agent?):or a<decides>helper — you can't pass a?agentwhere anagentis required. InterpolationTypes.Ease/EaseIn/...are curve shapes, notLerpreplacements. They producecubic_bezier_parametersfor the CreativeAnimation system. For simple value blends in your own loop, shape the parameter yourself (e.g.T*Tfor ease-in) and keep callingLerp.- Frame pacing vs. timer duration. Your
Sleep-based loop and thetimer_devicecountdown are independent. Use the timer'sSuccessEventto snap the final value (viaLerp(From, To, 1.0)) so tiny frame drift never leaves the gate half-open.