Overview
While Unreal Editor for Fortnite (UEFN) provides a native Lives System device for basic configuration, complex game modes often require programmatic control over life tracking, custom respawn delays, and dynamic game-over states. Because the native device's API surface is limited for Verse, this guide teaches you how to build a robust, Custom Lives System using core Verse concurrency (Sleep), state tracking (map), and event subscriptions (trigger_device).
API Reference
API Reference
(API surface could not be resolved for this device.)
Walkthrough
Let's build a "Lava Floor" damage zone. When a player steps on the trigger, they lose a life. If they have lives remaining, a coroutine handles a respawn delay using Sleep. If they hit zero lives, it triggers a game-over state.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
custom_lives_device := class(creative_device):
@editable
DamageZone : trigger_device = trigger_device{}
var PlayerLives : map(agent, int) = map{}
const MaxLives : int = 3
const RespawnDelay : float = 3.0
OnBegin<override>()<suspends>:void =
DamageZone.TriggeredEvent.Subscribe(OnPlayerDamaged)
OnPlayerDamaged(Agent : ?agent):void =
if (Player := Agent?):
CurrentLives := GetLives(Player)
NewLives := CurrentLives - 1
if (NewLives <= 0):
Print("Game Over for player!")
else:
Print("Player lost a life. Remaining: {NewLives}")
set PlayerLives[Player] = NewLives
spawn HandleRespawn(Player)
HandleRespawn(Player : agent)<suspends>:void =
Print("Respawning in {RespawnDelay} seconds...")
Sleep(RespawnDelay)
Print("Player respawned!")
GetLives(Player : agent):int =
if (Lives := PlayerLives[Player]):
Lives
else:
MaxLives
Line-by-Line Breakdown:
@editable DamageZone: Exposes the trigger to the UEFN UI so you can place it in the level.PlayerLivesmap: Tracks the current lives for each specificagent(player).OnBegin: Subscribes to the trigger's event when the simulation starts.if (Player := Agent?): Safely unwraps the optional?agentprovided by the event.spawn HandleRespawn: Kicks off a background coroutine so the respawn delay doesn't block other players from triggering the zone.Sleep(RespawnDelay): Pauses the coroutine for the specified float duration.
Common patterns
Pattern 1: The Reset Pad
A safe zone that restores a player's lives to the maximum when they step on it.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
reset_lives_device := class(creative_device):
@editable
ResetPad : trigger_device = trigger_device{}
var PlayerLives : map(agent, int) = map{}
const MaxLives : int = 5
OnBegin<override>()<suspends>:void =
ResetPad.TriggeredEvent.Subscribe(OnReset)
OnReset(Agent : ?agent):void =
if (Player := Agent?):
set PlayerLives[Player] = MaxLives
Pattern 2: Sudden Death Countdown
Using Sleep to create a dramatic countdown before a global elimination or life-loss event.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
sudden_death_device := class(creative_device):
@editable
StartButton : button_device = button_device{}
OnBegin<override>()<suspends>:void =
StartButton.InteractedWithEvent.Subscribe(OnStartSuddenDeath)
OnStartSuddenDeath(Agent : ?agent):void =
if (Player := Agent?):
spawn SuddenDeathCountdown()
SuddenDeathCountdown()<suspends>:void =
Print("Sudden Death in 3...")
Sleep(1.0)
Print("2...")
Sleep(1.0)
Print("1...")
Sleep(1.0)
Print("Elimination!")
Gotchas
- Map Access Safety: In Verse, accessing a
mapkey that doesn't exist will cause a runtime failure. Always use anif (Val := MyMap[Key])block to safely unwrap the value, or provide a fallback as shown in theGetLiveshelper method. - Float vs Int in Sleep: The
Sleepfunction requires afloat. Passing an integer likeSleep(3)will cause a compile error. Always use a decimal point:Sleep(3.0). - Agent Unwrapping: Events like
TriggeredEventpass an optional agent (?agent). You must unwrap it usingif (Player := Agent?)before you can use the player reference in your logic. - Localized Text for UI: If you decide to pass your life-count strings to a UI device (like a
hud_message_device), you cannot pass a raw string. You must use a localized message type:MyText<localizes>(S:string):message = "{S}".