@editable References: Wiring Level Objects Into Code
In Blueprints you tick Instance Editable, drag an Actor into the Details panel, and the code has its object. Verse does the same job with @editable, plus a few extra rules: default values are mandatory, how array slots work, whether you can reference your own classes, and the renaming landmine.
1. Object Reference Variable ↔ @editable Field
These two map word for word:
| Blueprint | Verse | Difference |
|---|---|---|
| Add a variable, type set to an Object Reference of some Actor class | TriggerButton:button_device |
Different spelling, same meaning: a field pointing at some object in the level |
| Tick Instance Editable (that eye) | @editable on its own line above the field |
A checkbox versus an attribute; both do nothing but "make it appear on the panel" |
| Defaults to None | = button_device{}, mandatory |
Verse refuses to have an "empty" state; omit the default and Compile goes red |
| Pick a level Actor from the Details panel dropdown | The same Details panel, dragged into the slot | Identical — this step has no code substitute |
The third row takes the most getting used to. In Blueprints an object reference can naturally be None, so you built the muscle memory of "Is Valid before you touch it." Verse closes that road — the field must be initialized to an empty archetype, and the type system has no notion of "this might be empty."
One thing to be clear about, though: that does not mean "not wiring the panel is fine." The empty archetype only lets the code compile; it is not the real object in the level. Forget the drag and touching it in game is a runtime error. Verse eliminates the "forgot the null check" class of mistake, not the "forgot to configure it" class — the latter is always a human's job, in Blueprints too.
2. Array Slots: Drag a Whole Batch at Once
In Blueprints you switch the variable type to Array of Actor Reference and the panel grows a list with a + button. Same idea in Verse:
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Receiving side: expose a public function as the mailbox
siren_device := class(creative_device):
OnNotified<public>():void =
Print("Broadcast received - siren on!")
# Sending side: drag a batch of sirens onto the panel, call the roll on broadcast
notifier_device := class(creative_device):
@editable
Listeners:[]siren_device = array{}
Broadcast<public>():void =
for (L : Listeners):
L.OnNotified()
In Blueprint terms: []siren_device is "an array of siren device references," defaulting to the empty array array{}; in the editor you drag every siren in the level into that list. Broadcast runs a ForEach over the list and calls the OnNotified each one exposes.
This is the most literal "one to many" approach, and it is a different road from the main lesson's event(t): an array is a roll call (the sender knows every listener, and the order is yours to control), an event is a broadcast (the publisher knows nobody). The trade-offs:
| Approach | Coupling | Best for |
|---|---|---|
A single @editable reference, calling its <public> functions |
High: A must know B | A fixed one-to-one pair of objects |
An @editable array, ForEach roll call |
Medium: the roster lives on the panel, order is controllable | A fixed set of listeners where order matters |
A shared event(t) with Signal / Subscribe |
Low: the publisher knows nobody | Cross-module decoupling, listeners that come and go |
The rule of thumb is the one you already use in Blueprints: reach for the event when decoupling is possible; the array slot only wins when you genuinely need "notify them in panel order" or "read that roster at runtime."
3. Referencing Your Own Classes
@editable is not limited to native devices — it can reference Verse classes you wrote. The Listeners:[]siren_device above is one example; the main lesson's gate_device referencing starter_device is another. It is the equivalent of setting a Blueprint variable's type to your own BP_Siren class.
Two extra rules are worth remembering:
- Members you want to reach from outside must be marked
<public>. The Blueprint counterpart is the Public / Private toggle on a node — Verse just defaults to the more conservative side. - To put an entire ordinary class (not a device) on the panel as configurable data, that class needs the
<concrete>marker, on the condition that every one of its fields has a default value — the same principle as an@editablefield needing a default: the panel may not contain an "undecided" cell.
4. Two Landmines Between Panel and Code
Landmine one: changed the code, panel didn't change. A newly added @editable field does not appear on its own — rebuild / push the Verse code to the editor first. This step is the equivalent of hitting Compile in Blueprints: skip it and the panel never refreshes.
Landmine two: renaming a field can drop configured references. Panel configuration is stored against the field name, so after a rename the old configuration may no longer match, and the symptom is "that slot on the panel suddenly went empty." Blueprints have a redirect mechanism to soften variable renames; Verse offers no equivalent guarantee, so after any rename go back to the editor and check every slot. Together, these two are the most common upstream causes of the classic "code is perfect, blows up in game" accident.
Why does this field fail to Compile? @editable on its own line, then Door:barrier_device
Sources
Compiled from Epic's official documentation and developer forums:
- Coding Device Interactions in Verse — official documentation ↗ (how @editable references a device)
- How do I add custom events in verse creative devices? — Epic developer forums ↗