Overview
Verse is a strongly typed language: an int is never silently promoted to a float, and a number is never automatically turned into a string. That strictness prevents whole classes of runtime bugs, but it means you must explicitly convert values whenever you cross a type boundary.
The three conversions you'll reach for most often are:
| From | To | How |
|---|---|---|
float |
string |
ToString(val:float):string |
int |
float |
val * 1.0 or cast via expression |
float |
int |
Int[val] (failable — use inside if) |
These come up constantly when you:
- Display a score or timer on a player's HUD.
- Feed a computed delay into
SetResetDelay(Time:float)on atrigger_device. - Build a
vector3from integer grid coordinates.
The sunny pirate-ship lagoon below is the perfect playground: cannon shots are counted as int, converted to float to drive trigger delays, and converted to string to show on the HUD.
API Reference
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):
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
Scenario — The Cannon-Fire Scoreboard
A player stands on the deck of a cel-shaded pirate ship anchored in a turquoise lagoon. Every time they step on a cannon plate (a trigger_device), a cannon fires. The number of shots taken is tracked as an int, converted to float to set an ever-growing reset delay (making each subsequent shot harder to spam), and converted to string to display on the player's HUD via a text_block widget.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/UI }
# Localisation helper — Verse requires `message` not raw string
CannonMessage<localizes>(S : string) : message = "{S}"
cannon_score_device := class(creative_device):
# ── Editable device references ──────────────────────────────
# Place a Trigger Device on the cannon plate in your island.
@editable
CannonTrigger : trigger_device = trigger_device{}
# A second trigger that fires a "reload complete" signal to
# linked devices (e.g. a sound cue or particle burst).
@editable
ReloadTrigger : trigger_device = trigger_device{}
# ── Runtime state ────────────────────────────────────────────
var ShotCount : int = 0
# ── Entry point ──────────────────────────────────────────────
OnBegin<override>()<suspends> : void =
# Start with a 1-shot limit so the player must wait between shots.
CannonTrigger.SetMaxTriggerCount(1)
CannonTrigger.SetResetDelay(2.0) # 2-second base cooldown
# Subscribe to the cannon plate
CannonTrigger.TriggeredEvent.Subscribe(OnCannonFired)
# ── Event handler ────────────────────────────────────────────
OnCannonFired(MaybeAgent : ?agent) : void =
# Increment shot counter (int)
set ShotCount += 1
# ── Conversion 1: int → float ────────────────────────────
# Verse will NOT auto-promote int to float.
# Multiply by 1.0 to produce a float literal.
ShotCountFloat : float = ShotCount * 1.0
# Use the float to make each successive shot take longer to reload.
# Formula: 2 seconds + 0.5 seconds per previous shot.
NewDelay : float = 2.0 + (ShotCountFloat * 0.5)
CannonTrigger.SetResetDelay(NewDelay)
# ── Conversion 2: float → string ─────────────────────────
# ToString() is the ONLY built-in that converts float → string.
DelayText : string = ToString(NewDelay)
# ── Conversion 3: int → string (via float path) ──────────
ShotText : string = ToString(ShotCountFloat)
# Build a HUD message for the agent who fired the cannon.
if (A := MaybeAgent?):
if (P := player[A]):
ShowHUD(P, ShotText, DelayText)
# Fire the reload trigger after the delay so linked devices
# (sound, particle) know the cannon is ready again.
ReloadTrigger.SetTransmitDelay(NewDelay)
ReloadTrigger.Trigger()
# ── HUD helper ───────────────────────────────────────────────
ShowHUD(P : player, Shots : string, Delay : string) : void =
HUDText : string = "Shots: {Shots} | Next reload: {Delay}s"
Msg : message = CannonMessage(HUDText)
TB := text_block{DefaultText := Msg}
if (UI := GetPlayerUI[P]):
UI.AddWidget(TB)
Line-by-line explanation
| Lines | What's happening |
|---|---|
CannonTrigger.SetMaxTriggerCount(1) |
Limits the trigger to 1 fire before it must reset — forces the delay to matter. |
CannonTrigger.SetResetDelay(2.0) |
Sets the initial 2-second cooldown. SetResetDelay takes a float — if you pass an int the compiler rejects it. |
CannonTrigger.TriggeredEvent.Subscribe(OnCannonFired) |
Wires the plate to our handler. The event sends ?agent (optional agent). |
ShotCountFloat : float = ShotCount * 1.0 |
int → float: multiplying by the float literal 1.0 widens the result to float. |
NewDelay : float = 2.0 + (ShotCountFloat * 0.5) |
Pure float arithmetic — no conversion needed once both sides are float. |
ToString(NewDelay) |
float → string: the only built-in for this direction. |
if (A := MaybeAgent?) |
Unwraps the ?agent option before use — required by the type system. |
if (P := player[A]) |
Casts agent to player — this is a failable cast, so it lives inside if. |
ReloadTrigger.SetTransmitDelay(NewDelay) |
Delays the signal sent to linked devices by the same float duration. |
ReloadTrigger.Trigger() |
Fires the reload trigger immediately (the transmit delay handles the timing). |
Common patterns
Pattern 1 — Reading back a float and converting to int for display
You stored a delay as float via GetResetDelay(). You want to show it as a whole-second countdown — so you need float → int.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
delay_reader_device := class(creative_device):
@editable
WatchedTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
# GetResetDelay returns float
CurrentDelayFloat : float = WatchedTrigger.GetResetDelay()
# float → int: Int[x] is FAILABLE — use inside if
if (WholeSeconds := Int[CurrentDelayFloat]):
# WholeSeconds is now a plain int
AsFloat2 : float = WholeSeconds * 1.0 # int → float again
AsString : string = ToString(AsFloat2) # float → string
Log("Reload is {AsString} whole seconds")
else:
Log("Could not convert delay to int")
Key point: Int[x] is a failable conversion — it truncates toward zero but fails if the value is out of the valid integer range. Always wrap it in if.
Pattern 2 — Configuring trigger limits from a computed float score
A leaderboard score (float) drives how many times a treasure-chest trigger can fire. SetMaxTriggerCount takes int, so you must convert.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
treasure_gate_device := class(creative_device):
@editable
ChestTrigger : trigger_device = trigger_device{}
# Simulated score arriving as float (e.g. from a stat system)
var PlayerScore : float = 7.5
OnBegin<override>()<suspends> : void =
# Divide score by 2.5 to get allowed chest opens
AllowedOpensFloat : float = PlayerScore / 2.5
# float → int (failable)
if (AllowedOpensInt := Int[AllowedOpensFloat]):
# Clamp to [0,20] — SetMaxTriggerCount clamps internally but
# being explicit is good practice.
Clamped : int = if (AllowedOpensInt > 20) then 20
else if (AllowedOpensInt < 0) then 0
else AllowedOpensInt
ChestTrigger.SetMaxTriggerCount(Clamped)
# Confirm: read it back and display
Confirmed : int = ChestTrigger.GetMaxTriggerCount()
ConfirmedFloat : float = Confirmed * 1.0
Log("Chest opens allowed: {ToString(ConfirmedFloat)}")
else:
# Fallback: unlimited
ChestTrigger.SetMaxTriggerCount(0)
Log("Score out of range — chest opens unlimited")
Pattern 3 — Building a vector3 from int grid coordinates
A pirate-ship grid uses integer tile coordinates. You need a vector3 (all float fields) to position a prop or pass to an API.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
grid_position_device := class(creative_device):
@editable
SignalTrigger : trigger_device = trigger_device{}
# Integer tile coordinates from a game-design grid
TileX : int = 3
TileY : int = 7
TileSize : float = 256.0 # centimetres per tile
OnBegin<override>()<suspends> : void =
SignalTrigger.TriggeredEvent.Subscribe(OnSignal)
OnSignal(MaybeAgent : ?agent) : void =
# int → float for each axis before building vector3
WorldX : float = TileX * 1.0 * TileSize
WorldY : float = TileY * 1.0 * TileSize
WorldZ : float = 0.0
TilePos : vector3 = vector3{X := WorldX, Y := WorldY, Z := WorldZ}
# Convert components back to string for a debug log
PosText : string = "({ToString(TilePos.X)}, {ToString(TilePos.Y)})"
Log("Cannon tile world pos: {PosText}")
# Trigger with no agent to signal linked devices
SignalTrigger.Trigger()
Gotchas
1. int and float are NEVER interchangeable
Verse will not silently widen int to float. Passing ShotCount (an int) directly to SetResetDelay() is a compile error. Always write ShotCount * 1.0 or declare the value as a float literal from the start.
2. Int[x] is failable — it MUST live inside if
# ❌ Compile error — Int[] can fail, you can't use it bare
MyInt : int = Int[SomeFloat]
# ✅ Correct
if (MyInt := Int[SomeFloat]):
# use MyInt here
3. ToString only accepts float, not int
There is no ToString(Val:int) overload. Convert to float first:
Count : int = 5
# ❌ ToString(Count) — no int overload
# ✅
CountStr : string = ToString(Count * 1.0)
4. message ≠ string — you cannot pass a raw string to a widget
text_block{DefaultText := "hello"} is a compile error. Declare a localised helper:
MyMsg<localizes>(S : string) : message = "{S}"
# Then:
text_block{DefaultText := MyMsg("hello")}
5. Unwrap ?agent before casting to player
The TriggeredEvent sends ?agent (an optional). You need two steps:
OnFired(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?): # unwrap option
if (P := player[A]): # failable cast agent → player
# use P
Skipping either step is a compile error.
6. SetMaxTriggerCount clamps to [0, 20]
Values above 20 are silently clamped. 0 means unlimited, not zero shots. Plan your int arithmetic accordingly.
7. GetTriggerCountRemaining returns 0 when the limit is unlimited
Don't use GetTriggerCountRemaining() = 0 as a "no shots left" check — it also returns 0 when the trigger is set to unlimited (MaxCount = 0). Check GetMaxTriggerCount() first.