Overview
When you write a creative_device in Verse, the class lives in code — but your level designers (and your future self at 2 AM) need to point that code at the specific devices sitting on the island without editing source. @editable is the bridge.
Place @editable above any field and UEFN exposes it in the Details panel as a slot you can fill by clicking or dragging:
- Device references —
trigger_device,cinematic_sequence_device,creative_prop,gameplay_camera_fixed_point_device, and every othercreative_device_basesubclass. - Primitive tuning values —
int,float,logic(bool),string. - Custom
<concrete>classes — nest your own data classes and expose them as structured groups in the panel. - Arrays of any of the above — great for lists of props, wave configs, obstacle sets.
When to reach for it:
- You want the same Verse script to drive multiple rooms or levels with different devices.
- A designer needs to tune a float (e.g. cinematic delay) without touching Verse.
- You're building a reusable "module" class (like a gameboard or a wave definition) that bundles several device references together.
API Reference
(API surface could not be resolved for this device.)
Walkthrough
Scene: A cel-shaded clifftop cove at sunrise. A glowing pressure plate sits on the dock. When a player steps on it, a cinematic sunrise sequence fires, a hidden sea-rock prop rises into view, and the fixed-point camera swoops to frame the cove. All four device references live in editable fields — the designer wires them up in the Details panel.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
# A self-contained data class that groups everything needed for one
# "cove reveal" moment. Marked <concrete> so UEFN can show it in the
# Details panel as a nested editable group.
cove_reveal_config<public> := class<concrete>:
# The pressure plate the player steps on to start the reveal.
@editable
DockPlate : trigger_device = trigger_device{}
# The cinematic sunrise sequence to play.
@editable
SunriseSequence : cinematic_sequence_device = cinematic_sequence_device{}
# The sea-rock prop that is hidden at game start and rises into view.
@editable
SeaRockProp : creative_prop = creative_prop{}
# Fixed-point camera that frames the cove during the cinematic.
@editable
CoveCam : gameplay_camera_fixed_point_device = gameplay_camera_fixed_point_device{}
# How many seconds to wait after the plate fires before playing the
# cinematic. Designers can tweak this without touching Verse.
@editable
CinematicDelaySec : float = 1.5
# The main Verse device. Drop one of these on the island, then fill
# in the RevealConfig slot in the Details panel.
clifftop_cove_reveal_device := class(creative_device):
# Expose the entire config class as a single nested group.
@editable
RevealConfig : cove_reveal_config = cove_reveal_config{}
# Whether the reveal has already fired (prevents double-triggers).
var HasFired : logic = false
OnBegin<override>()<suspends> : void =
# Hide the sea-rock prop so the cove looks empty at game start.
RevealConfig.SeaRockProp.Hide()
# Subscribe to the dock plate. The handler runs every time the
# plate fires, but our HasFired guard makes it a one-shot.
RevealConfig.DockPlate.TriggeredEvent.Subscribe(OnDockPlateTriggered)
# Called whenever an agent (or code) triggers the dock plate.
# TriggeredEvent sends a ?agent — we unwrap it before using it.
OnDockPlateTriggered(MaybeAgent : ?agent) : void =
# One-shot guard: ignore subsequent triggers.
if (HasFired = false):
set HasFired = true
# Kick off the async reveal on a background fiber so the
# subscription handler returns immediately.
spawn { RunRevealSequence(MaybeAgent) }
# The actual reveal logic runs here, asynchronously.
RunRevealSequence(MaybeAgent : ?agent)<suspends> : void =
# Wait the designer-tunable delay before doing anything.
Sleep(RevealConfig.CinematicDelaySec)
# Show the sea-rock prop — it rises from the water.
RevealConfig.SeaRockProp.Show()
# Play the sunrise cinematic. If an agent triggered the plate,
# use the agent-scoped overload so per-player settings apply.
if (A := MaybeAgent?):
RevealConfig.SunriseSequence.Play(A)
else:
RevealConfig.SunriseSequence.Play()
# Wait for the cinematic to finish before cleaning up.
RevealConfig.SunriseSequence.StoppedEvent.Await()
Line-by-line highlights:
| What | Why |
|---|---|
cove_reveal_config<concrete> |
<concrete> is required for UEFN to instantiate the class in the Details panel. Without it the slot won't appear. |
@editable above each field |
Each decorated field becomes a named slot in the Details panel. Drag a placed device into the slot. |
Default values (trigger_device{}, creative_prop{}) |
Verse requires a default; these are empty stubs. The designer replaces them with real placed devices. |
CinematicDelaySec : float = 1.5 |
A plain float editable — shows as a number spinner in the panel. |
RevealConfig : cove_reveal_config = cove_reveal_config{} |
Nesting a <concrete> class inside another editable exposes it as a collapsible group. |
MaybeAgent : ?agent unwrap |
TriggeredEvent is listenable(?agent). Always unwrap with if (A := MaybeAgent?): before passing to APIs that need agent. |
spawn { RunRevealSequence(...) } |
Event handlers must return immediately; spawn launches the suspending logic on a new fiber. |
StoppedEvent.Await() |
Blocks the fiber until the cinematic signals it has stopped — clean sequencing without a fixed Sleep. |
Common patterns
Pattern 1 — Editable float tunes a prop physics launch
A designer sets LaunchStrength in the Details panel; Verse reads it and fires the prop upward when a trigger fires. Demonstrates reading an editable float and calling SetLinearVelocity on a creative_prop.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
cove_buoy_launcher_device := class(creative_device):
# The trigger the player activates on the dock.
@editable
LaunchTrigger : trigger_device = trigger_device{}
# The buoy prop to launch upward.
@editable
BuoyProp : creative_prop = creative_prop{}
# Upward speed in m/s. Designer-tunable without touching code.
@editable
LaunchStrength : float = 8.0
OnBegin<override>()<suspends> : void =
LaunchTrigger.TriggeredEvent.Subscribe(OnLaunch)
OnLaunch(MaybeAgent : ?agent) : void =
# Build a straight-up velocity vector using the editable strength.
BuoyProp.SetLinearVelocity(vector3{X := 0.0, Y := 0.0, Z := LaunchStrength})
Pattern 2 — Editable array of props for a multi-rock reveal
Expose an array of creative_prop references so the designer can add as many sea-rocks as they like without changing Verse. Demonstrates @editable on an array and calling Show() / Hide() in a loop.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
cove_multi_rock_device := class(creative_device):
# Fill this array in the Details panel with as many rock props as needed.
@editable
RockProps : []creative_prop = array{}
# The trigger that reveals all rocks at once.
@editable
RevealTrigger : trigger_device = trigger_device{}
# Start hidden; reveal on trigger.
OnBegin<override>()<suspends> : void =
for (Rock : RockProps):
Rock.Hide()
RevealTrigger.TriggeredEvent.Subscribe(OnReveal)
OnReveal(MaybeAgent : ?agent) : void =
for (Rock : RockProps):
Rock.Show()
Pattern 3 — Editable logic flag changes cinematic direction
A single logic editable lets the designer flip whether the sunrise cinematic plays forward or in reverse (a sunset). Demonstrates reading an editable logic value and branching between Play() and PlayReverse().
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
cove_time_of_day_device := class(creative_device):
@editable
DayNightTrigger : trigger_device = trigger_device{}
@editable
SkySequence : cinematic_sequence_device = cinematic_sequence_device{}
# true = sunrise (forward), false = sunset (reverse).
@editable
PlayForward : logic = true
OnBegin<override>()<suspends> : void =
DayNightTrigger.TriggeredEvent.Subscribe(OnToggle)
OnToggle(MaybeAgent : ?agent) : void =
if (PlayForward?):
SkySequence.Play()
else:
SkySequence.PlayReverse()
Gotchas
1. <concrete> is not optional for custom editable classes
If you write MyConfig := class: (no <concrete>), UEFN cannot instantiate it in the Details panel and the slot simply won't appear. Always add <concrete> to any class you intend to expose via @editable.
2. Default values are stubs, not live devices
trigger_device{} is an empty, unconnected stub. If you forget to fill the slot in the Details panel and your code calls RevealConfig.DockPlate.TriggeredEvent.Subscribe(...), it subscribes to a device that never fires — silent failure, no crash. Always double-check every editable slot is wired in the Details panel before testing.
3. @editable fields must be immutable at the class level
You cannot write @editable var Count : int = 0. Editable fields are set once by the editor and are read-only at runtime. If you need a mutable counter, declare a separate var field (no @editable) and copy the editable value into it in OnBegin.
4. ?agent must always be unwrapped
TriggeredEvent is typed listenable(?agent) — the agent is optional because the trigger can fire from code with no player. Always unwrap: if (A := MaybeAgent?): before passing A to any API that requires a concrete agent.
5. message vs string for editable text
If you expose a text field that will be displayed to players (e.g. a HUD label), the parameter must be typed message, not string. Declare a localizer helper: MyText<localizes>(S : string) : message = "{S}". There is no StringToMessage function.
6. Arrays of devices are supported — arrays of arrays are not
@editable MyProps : []creative_prop = array{} works perfectly. @editable MyGroups : [][]creative_prop = array{} does not; flatten your data or use a <concrete> wrapper class instead.
7. Suspending calls need spawn inside event handlers
Subscription callbacks must return immediately — they are not <suspends> contexts. Any call to Sleep, Await, or other suspending functions must be wrapped in spawn { MyAsyncFunction() } from inside the handler.