Overview
The experience_settings_device (often the first device you place when starting a fresh island) controls the high-level properties of your game mode: how many teams there are, how long a round lasts, what happens at the end of the match, respawn behavior, and similar top-level rules. In the UEFN Details panel you set these once and the device enforces them for the whole experience.
Reach for it whenever you're answering questions like "How long is a round?", "Is this Free-For-All or two teams?", or "What screen shows when the match ends?" — the game-mode skeleton, not moment-to-moment gameplay.
Here's the catch a lot of creators hit: in Verse, this device has no methods and no events. Its API surface is essentially just the device class itself:
experience_settings_device<public> := class<concrete><final>(creative_device_base)
That means you don't call Start() or subscribe to RoundEndedEvent on it — those don't exist. So how is it useful in Verse? You declare it as an @editable field so your script can hold a reference to the configured settings device, and you build your round logic in cooperating devices (timers, triggers, end-game devices) that you DO drive from Verse. The experience_settings_device is the anchor; the action happens around it.
This article shows that pattern: a runnable round-flow controller that references the experience settings and drives a real end-of-round handoff using devices that have working APIs.
API Reference
experience_settings_device
Used to customize high level properties of the game mode.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
experience_settings_device<public> := class<concrete><final>(creative_device_base):
Walkthrough
Let's build a round-start announcer. We place an experience_settings_device to define our game mode (we configured a 2-team mode in the Details panel), plus a trigger_device the host steps on to begin, and a hud_message_device to broadcast the call to action. Verse ties them together.
Because the experience_settings_device has no callable surface, our job in code is: keep a reference to it (so the design intent is documented and the reference travels with the script), and run the announce logic on the devices that do expose methods.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Round-flow controller anchored to the experience settings device.
round_controller := class(creative_device):
# The settings device defines our game mode. We hold the reference
# even though it exposes no methods — it documents intent and keeps
# the wiring in one place.
@editable
Settings : experience_settings_device = experience_settings_device{}
# The host steps on this plate to kick off the round.
@editable
StartPlate : trigger_device = trigger_device{}
# Broadcasts the "Round starting!" call-out.
@editable
Announcer : hud_message_device = hud_message_device{}
# Localized text helper — message params need a localized value.
MakeMsg<localizes>(S : string) : message = "{S}"
OnBegin<override>()<suspends> : void =
# Subscribe the plate's trigger to our handler.
StartPlate.TriggeredEvent.Subscribe(OnStartStepped)
Print("Round controller online. Waiting for host to start.")
# Event handlers are methods at class scope.
OnStartStepped(Agent : ?agent) : void =
if (Player := Agent?):
# Show the round-start message to everyone.
Announcer.SetText(MakeMsg("Round starting — good luck!"))
Announcer.Show()
Print("Round started by a player.")
Line by line:
- The three
@editablefields let you drag the placed devices onto the script in the Details panel.Settingsis the experience_settings_device — declaring it documents which game-mode rules this controller belongs to. MakeMsg<localizes>is the standard pattern for turning a string into amessage(there is noStringToMessage). We callMakeMsg("...")anywhere amessageis required.- In
OnBegin, weSubscribethe plate'sTriggeredEventtoOnStartStepped. Subscription happens here, but the handler is a separate method. OnStartSteppedreceives(Agent : ?agent). We unwrap it withif (Player := Agent?)before acting — alistenable(?agent)always hands you an optional.- Inside, we set and
Show()the HUD message. These are realhud_message_devicecalls that make something visible in-game. The experience_settings_device sits behind all of this as the configured game-mode context.
Common patterns
Pattern 1: End-game handoff to the configured mode
When a condition is met, signal an end_game_device to finish the match according to the win rules you set up alongside your experience settings. The experience_settings_device defines what winning means; the end_game_device triggers it.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
finisher := class(creative_device):
@editable
Settings : experience_settings_device = experience_settings_device{}
@editable
WinButton : button_device = button_device{}
@editable
Ender : end_game_device = end_game_device{}
OnBegin<override>()<suspends> : void =
WinButton.InteractedWithEvent.Subscribe(OnPressed)
OnPressed(Agent : agent) : void =
# Ends the round per the game-mode rules configured on settings.
Ender.Activate(Agent)
Pattern 2: Reference the settings device alongside a round timer
Use a timer_device to enforce a round length that matches your design. The settings device is the anchor; the timer drives the countdown and fires when time runs out.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
round_timer_runner := class(creative_device):
@editable
Settings : experience_settings_device = experience_settings_device{}
@editable
RoundTimer : timer_device = timer_device{}
OnBegin<override>()<suspends> : void =
# When the timer ends, the round is over.
RoundTimer.SuccessEvent.Subscribe(OnTimeUp)
RoundTimer.Start()
OnTimeUp(Agent : ?agent) : void =
Print("Round time elapsed for this game mode.")
Pattern 3: Gate a feature on whether the round has begun
Keep a simple var logic that your other devices read, set when the host starts. This is how you coordinate state around the settings-defined mode without needing any method on the settings device itself.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
state_gate := class(creative_device):
@editable
Settings : experience_settings_device = experience_settings_device{}
@editable
StartPlate : trigger_device = trigger_device{}
@editable
LootGranter : item_granter_device = item_granter_device{}
var RoundLive : logic = false
OnBegin<override>()<suspends> : void =
StartPlate.TriggeredEvent.Subscribe(OnStart)
OnStart(Agent : ?agent) : void =
set RoundLive = true
if (Player := Agent?):
# Only hand out loot once the round is live.
if (RoundLive?):
LootGranter.GrantItem(Player)
Gotchas
- It has no methods or events. You cannot call
Settings.Start(), subscribe to a round event on it, or read settings values via Verse. Searching its API for callable functions turns up nothing — that's by design. Drive your logic through timers, triggers, and end-game devices instead, and treat the settings device as the configured anchor. - You still must declare it as an
@editablefield if you want a Verse reference. A bareexperience_settings_device.Something()would fail with 'Unknown identifier' even if a method existed — and here, no methods exist at all. - Don't try to change game-mode rules at runtime from this device. Round length, team count, and win conditions are set in the Details panel before play. If you need dynamic changes, model them yourself in Verse state (see Pattern 3) and act on cooperating devices.
messageparams need localized values. Use a<localizes>helper likeMakeMsg<localizes>(S:string):message = "{S}"— there is noStringToMessage.- Optional agents from
listenable(?agent). Handlers likeTriggeredEvent's give you(Agent : ?agent); always unwrap withif (P := Agent?):before using the player. Note some events (likebutton_device.InteractedWithEvent) hand a plainagent— match the signature. - No int↔float auto-conversion. If you compute a round length, keep your numeric types consistent; Verse will not silently convert between
intandfloat.