Reference Verse compiles

Editable Properties: Wire Your Island Together Without Touching Code

Every Verse device you drop on an island is a black box until you connect it to something. `@editable` is the attribute that punches a hole in that box — it lets you drag-and-drop device references, set numeric tuning values, and even expose whole custom classes straight from the UEFN Details panel, zero recompile required. In this article you'll wire up a sun-drenched clifftop cove scene where a player steps on a pressure plate, a cinematic sunrise sequence plays, and a hidden prop rises from t

Updated Examples verified on the live UEFN compiler
Watch the Knoteditable_properties in ~90 seconds.

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 referencestrigger_device, cinematic_sequence_device, creative_prop, gameplay_camera_fixed_point_device, and every other creative_device_base subclass.
  • Primitive tuning valuesint, 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.

Guides & scripts that use editable_properties

Step-by-step tutorials that put this object to work.

Build your own lesson with editable_properties

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →