Reference Devices compiles

Verse Naming Conventions: Writing Code Everyone Can Read

Naming things well is the first skill that separates readable Verse from a tangled mess. Verse has a clear, community-wide convention for identifiers, files, and modules — follow it and your code will feel at home alongside Epic's own APIs; ignore it and every collaborator (including future you) will slow down trying to decode what `x2`, `tmpDev`, or `myclass1` actually does.

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

Overview

Verse naming conventions are a set of agreed-upon rules for how you name classes, variables, functions, files, and modules. The Verse language itself does not enforce most of these rules at compile time — your code will compile whether you write mydevice or MyDevice — but the conventions exist because:

  • The entire Fortnite/UEFN standard library uses them, so matching them makes your code read like a first-party API.
  • UEFN's content browser surfaces class names directly; a well-named class like vault_door_controller is immediately discoverable.
  • Large islands with many Verse files become unmanageable without a consistent file-naming scheme.

When you reach for naming conventions you are solving a communication problem: how do you make the intent of every identifier obvious at a glance?

API Reference

(API surface could not be resolved for this device.)

Walkthrough

The scenario: a Vault Door puzzle room. A pressure plate unlocks the door, a countdown timer closes it again, and a score-granter rewards the player who made it through. We will build the Verse device that wires this together — and every identifier will follow Verse conventions so the code is immediately readable.

Naming rules in practice

Construct Convention Example
Class name snake_case vault_door_controller
@editable device field PascalCase PressurePlate
Local variable PascalCase TriggeringAgent
Method / function PascalCase OnPlateTriggered
Module-level constant PascalCase (all-caps words OK) MaxOpenSeconds
var mutable field PascalCase IsOpen
File name snake_case with role suffix vault_door_controller.verse
<localizes> helper PascalCase VaultMessage

Key insight: Classes and file names use snake_case; almost everything else (fields, variables, functions) uses PascalCase. This mirrors how Epic's own APIs look — creative_device is the class, OnBegin is the method.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }

# File: vault_door_controller.verse
# Naming conventions demonstrated:
#   - Class:          snake_case  (vault_door_controller)
#   - @editable fields: PascalCase (PressurePlate, DoorDevice, etc.)
#   - Methods:        PascalCase  (OnPlateTriggered, CloseDoor)
#   - Local vars:     PascalCase  (TriggeringAgent, OpenPosition)
#   - Constants:      PascalCase  (DoorOpenDurationSeconds)
#   - <localizes>:    PascalCase  (VaultOpenMessage)

# Module-level localized message helper — PascalCase function name
VaultOpenMessage<localizes>(S : string) : message = "{S}"

# Class name: snake_case, as all Verse class names are
vault_door_controller := class(creative_device):

    # @editable fields are PascalCase — they appear in the UEFN
    # details panel exactly as written, so clear names matter.
    @editable
    PressurePlate : trigger_device = trigger_device{}

    @editable
    DoorDevice : creative_device = creative_device{}

    # A module-level constant expressed as a field — PascalCase
    DoorOpenDurationSeconds : float = 5.0

    # Mutable state field — PascalCase var
    var IsOpen : logic = false

    # OnBegin: override from creative_device (PascalCase, matches Epic's API)
    OnBegin<override>()<suspends> : void =
        # Subscribe uses a PascalCase method name for the handler
        PressurePlate.TriggeredEvent.Subscribe(OnPlateTriggered)

    # Event handler — PascalCase, verb phrase describing what happened
    OnPlateTriggered(TriggeringAgent : ?agent) : void =
        # `logic` is fallible, so query it with `?` and use `else:` for the false case
        if (IsOpen?):
            # Already open, do nothing
        else:
            set IsOpen = true
            # Show the door device (it was hidden, acting as a closed vault)
            # Then spawn a timed close coroutine
            spawn { CloseDoorAfterDelay() }

    # Private helper — PascalCase verb phrase
    CloseDoorAfterDelay()<suspends> : void =
        # Wait for the configured duration
        Sleep(DoorOpenDurationSeconds)
        set IsOpen = false```

### Line-by-line explanation

**`VaultOpenMessage<localizes>`**  A module-level function whose name is `PascalCase`. The `<localizes>` specifier turns it into a `message` producer. Naming it with a noun phrase (`VaultOpenMessage`) makes its purpose obvious.

**`vault_door_controller`**  The class name is `snake_case`, matching every class in the Verse standard library (`creative_device`, `trigger_device`, `barrier_device`). UEFN's content browser will show this exact string.

**`PressurePlate`, `DoorDevice`** — `@editable` fields are `PascalCase`. Because UEFN surfaces these in the Details panel, a name like `PressurePlate` is self-documenting to a designer who has never read the code.

**`DoorOpenDurationSeconds`** — A constant field. The name encodes both the concept (`DoorOpenDuration`) and the unit (`Seconds`), a best practice when dealing with numeric values.

**`var IsOpen : logic`** — Mutable state is still `PascalCase`. The `var` keyword signals mutability; the name signals boolean state with a simple adjective phrase.

**`OnPlateTriggered`** — Event handler names conventionally start with `On` followed by a past-tense description of what happened. This mirrors Epic's own events (`OnBegin`, `OnEnd`).

**`TriggeringAgent`**  Local variable, `PascalCase`. The name answers *what is this?* without a comment.

**`CloseDoorAfterDelay`**  A helper method. Verb phrase, `PascalCase`, describes the action and the timing.

## Common patterns

### Pattern 1 — File and module organization with snake_case names

Large islands split logic across multiple files. The convention is `[system]_[role].verse`. Each file holds one focused class or set of helpers.

```verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# File: score_config.verse
# Role suffix "_config" signals this file holds tunable constants.
# The class name follows snake_case.
score_config := class(creative_device):

    # PascalCase constants — units in the name prevent bugs
    PointsPerElimination : int = 100
    PointsPerObjective   : int = 250
    BonusMultiplier      : float = 1.5

    # OnBegin does nothing here; this device is a data container.
    OnBegin<override>()<suspends> : void =
        # Config devices typically just hold values other devices read.
        Sleep(0.0)

Pattern 2 — PascalCase event handlers with clear verb phrases

Subscribing to multiple events in one device is common. Consistent On[Event] naming makes the subscription list read like a table of contents.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# File: arena_round_controller.verse
arena_round_controller := class(creative_device):

    @editable
    RoundStartButton : button_device = button_device{}

    @editable
    RoundEndTrigger : trigger_device = trigger_device{}

    # Mutable round counter — PascalCase var
    var CurrentRound : int = 0

    OnBegin<override>()<suspends> : void =
        # Each subscription line reads like a sentence:
        # "When the button is interacted with, call OnRoundStarted."
        RoundStartButton.InteractedWithEvent.Subscribe(OnRoundStarted)
        RoundEndTrigger.TriggeredEvent.Subscribe(OnRoundEnded)

    # Handler name: On + past-tense event description
    OnRoundStarted(InstigatingAgent : ?agent) : void =
        set CurrentRound += 1

    # Handler name follows the same On + past-tense pattern
    OnRoundEnded(InstigatingAgent : ?agent) : void =
        # Round cleanup logic would go here
        set CurrentRound = 0

Pattern 3 — Localized message helpers with PascalCase names

Whenever you display text to players you need a <localizes> function. Name it with a noun phrase that describes the message category.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# File: hud_message_controller.verse

# Module-level <localizes> helpers — PascalCase noun phrases
ObjectiveCompleteMessage<localizes>(ObjectiveName : string) : message = "Objective Complete: {ObjectiveName}"
TimerExpiredMessage<localizes>() : message = "Time's Up!"
WelcomeMessage<localizes>(PlayerName : string) : message = "Welcome, {PlayerName}!"

hud_message_controller := class(creative_device):

    @editable
    HUDMessageDevice : hud_message_device = hud_message_device{}

    @editable
    ObjectiveTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        ObjectiveTrigger.TriggeredEvent.Subscribe(OnObjectiveTriggered)

    OnObjectiveTriggered(TriggeringAgent : ?agent) : void =
        # Pass a localized message — NOT a raw string
        # The helper name ObjectiveCompleteMessage clearly signals its purpose
        HUDMessageDevice.SetText(ObjectiveCompleteMessage("Vault Opened"))
        if (A := TriggeringAgent?):
            HUDMessageDevice.Show(A)

Gotchas

1. Classes are snake_case, everything else is PascalCase — don't mix them up

The single most common mistake is writing a class in PascalCase (VaultDoorController) or a field in snake_case (pressure_plate). While the compiler accepts both, your code will look alien next to Epic's APIs. Adopt the rule immediately: if it's a class or file name, use snake_case; if it's anything else, use PascalCase.

2. @editable field names appear verbatim in the UEFN Details panel

When a designer opens your device in UEFN, they see your @editable field names exactly as written. Pd or trig1 tells them nothing. PressurePlate or RoundEndTrigger is self-documenting. Treat every @editable name as public API.

3. File names should encode both system and role

A file called controller.verse is useless when you have ten of them. The convention [system]_[role].verse — e.g., vault_door_controller.verse, score_config.verse, player_data.verse — makes the content browser and file tree scannable at a glance. The roles are: _controller (creative_device), _config (constants/tunables), _data (state structures), _module (shared helpers).

4. Localized text helpers must be PascalCase functions, not raw strings

You cannot pass a raw string where a message is expected. You must declare a <localizes> function. Name it clearly — TimerExpiredMessage is better than Msg2 — because these helpers accumulate quickly in a large project and you will need to find them by name.

5. Unit suffixes prevent numeric bugs

Verse does not have a unit type system. A field named Duration could be seconds, milliseconds, or frames. A field named DurationSeconds is unambiguous. Apply this to any numeric field where the unit is not obvious from context: SpeedCentimetersPerSecond, DamagePoints, CooldownSeconds.

6. var signals mutability — use it sparingly and name accordingly

Mutable state (var) is the source of most concurrency bugs. When you do need it, name the field so its mutability is obvious from context: IsOpen, CurrentRound, RemainingLives. Avoid vague names like State or Flag for mutable fields — the name should tell you what changes and why.

Build your own lesson with naming_conventions

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 →