Reference Devices compiles

class_selector_ui_device: Let Players Pick Their Loadout Class

Hero shooters, RPG lobbies, and team modes all start the same way: "Pick your class." The class_selector_ui_device pops a menu where a player chooses a class, and its Verse events let your code react the moment they do. Here's how to show it, enable/disable it, and respond to selections.

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

Overview

The class_selector_ui_device presents a player with a menu of your island's configured Classes and lets them pick one. Think of a pre-game lobby where each player chooses Medic, Scout, or Tank before a round starts — that's exactly the job this device does.

You reach for it whenever a player needs to choose a class (as opposed to being force-assigned one). Three things make it useful from Verse:

  • You can Show the UI to a specific agent on demand (e.g. when they step on a plate or press a button).
  • You can Enable/Disable the device to gate when class-switching is allowed (open during lobby, locked once the round starts).
  • You get events the instant a player opens the UI, selects a class, changes class (with the class index), or closes the UI — so you can grant gear, start a timer, or update a scoreboard.

The device is configured in UEFN (which classes appear, their order, the visuals). Your Verse code drives when it shows and what happens after a choice.

API Reference

class_selector_ui_device

Used to allow players to select their Class from a Class Selector UI.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).

class_selector_ui_device<public> := class<concrete><final>(creative_device_base):

Events (subscribe a handler to react):

Event Signature Description
ClassSelectedEvent ClassSelectedEvent<public>:listenable(agent) Signaled when an agent selects a class. Sends the agent that selected a class.
ClassChangedEvent ClassChangedEvent<public>:listenable(tuple(agent, int)) Signaled when an agent changes a class. Sends the agent that selected a class and the int class that the player has changed to.
UIClosedEvent UIClosedEvent<public>:listenable(agent) Signaled when an agent closes the UI. Sends the agent that closed the UI.
UIOpenedEvent UIOpenedEvent<public>:listenable(agent) Signaled when the UI is opened. Sends the agent that is responsible for opening the UI.

Methods (call these to make the device act):

Method Signature Description
Enable Enable<public>():void Enables this device.
Disable Disable<public>():void Disables this device.
Show Show<public>(Agent:agent):void Show the Class Selector UI to Agent.

Walkthrough

Scenario: A lobby room. When a player walks onto a trigger pad, we pop the class selector for them. When they pick a class, we confirm it on the HUD and remember which class index they chose; if they close the menu without committing, we just nudge them. Once a player has selected, we don't need to keep the device enabled for them, but we leave it enabled so others can still choose.

class_selector_lobby := class(creative_device):

    @editable
    ClassUI : class_selector_ui_device = class_selector_ui_device{}

    @editable
    EntryPad : trigger_device = trigger_device{}

    @editable
    StatusHud : hud_message_device = hud_message_device{}

    # Localized text helper — message params need a localized value, not a raw string.
    InfoText<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        # Make sure the selector is on so Show() and events work.
        ClassUI.Enable()

        # When a player steps on the pad, pop the class menu for THAT player.
        EntryPad.TriggeredEvent.Subscribe(OnPadStepped)

        # React to the four lifecycle events of the UI.
        ClassUI.UIOpenedEvent.Subscribe(OnUIOpened)
        ClassUI.ClassSelectedEvent.Subscribe(OnClassSelected)
        ClassUI.ClassChangedEvent.Subscribe(OnClassChanged)
        ClassUI.UIClosedEvent.Subscribe(OnUIClosed)

    # TriggeredEvent hands us a ?agent — unwrap before using.
    OnPadStepped(Agent : ?agent) : void =
        if (Player := Agent?):
            ClassUI.Show(Player)

    # Fired when the menu appears for a player.
    OnUIOpened(Player : agent) : void =
        StatusHud.Show(Player)

    # Fired when a player commits a class selection.
    OnClassSelected(Player : agent) : void =
        StatusHud.Show(Player)

    # Fired when a player switches class — gives us the class index too.
    OnClassChanged(Result : tuple(agent, int)) : void =
        Player := Result(0)
        ClassIndex := Result(1)
        Print("Player changed to class index {ClassIndex}")
        StatusHud.Show(Player)

    # Fired when the player dismisses the menu.
    OnUIClosed(Player : agent) : void =
        Print("UI closed by a player")

Line by line:

  • @editable ClassUI : class_selector_ui_device = class_selector_ui_device{} — declares the field you bind to the placed device in UEFN. You must have this field; calling a bare class_selector_ui_device.Show() fails with Unknown identifier.
  • EntryPad, StatusHud — the trigger pad and HUD message device that drive and confirm the flow.
  • InfoText<localizes> — a localized-text helper. Any message parameter (like HUD text in UEFN) needs a localized value; there is no StringToMessage.
  • ClassUI.Enable() in OnBegin — turns the device on. A disabled device won't show or fire events.
  • EntryPad.TriggeredEvent.Subscribe(OnPadStepped) — wires the pad to our handler. TriggeredEvent is a listenable(?agent).
  • Each ClassUI.<Event>.Subscribe(...) line hooks one of the device's four events to a method.
  • OnPadSteppedTriggeredEvent gives a ?agent; we unwrap with if (Player := Agent?) and then ClassUI.Show(Player) to pop the menu for just that player.
  • OnClassChanged(Result : tuple(agent, int))ClassChangedEvent sends a tuple, so we read the agent with Result(0) and the class index with Result(1).

Common patterns

Lock class switching once the round starts (Disable). Open the lobby during warmup, then disable the selector so no one can swap mid-match.

round_class_lock := class(creative_device):

    @editable
    ClassUI : class_selector_ui_device = class_selector_ui_device{}

    @editable
    StartButton : button_device = button_device{}

    OnBegin<override>()<suspends> : void =
        # Warmup: players may freely pick.
        ClassUI.Enable()
        # When the host presses the start button, lock in classes.
        StartButton.InteractedWithEvent.Subscribe(OnRoundStart)

    OnRoundStart(Agent : agent) : void =
        # No more class swapping after this point.
        ClassUI.Disable()
        Print("Classes locked — round begins!")

Grant gear when a class is committed (ClassSelectedEvent). Hand the player a weapon the moment they confirm a class.

class_loadout_granter := class(creative_device):

    @editable
    ClassUI : class_selector_ui_device = class_selector_ui_device{}

    @editable
    WeaponGranter : item_granter_device = item_granter_device{}

    OnBegin<override>()<suspends> : void =
        ClassUI.Enable()
        ClassUI.ClassSelectedEvent.Subscribe(OnSelected)

    OnSelected(Player : agent) : void =
        # Reward the player for choosing by handing them their kit.
        WeaponGranter.GrantItem(Player)

Track which class each player runs (ClassChangedEvent index). The tuple's int is the class index — perfect for counting team composition.

class_tracker := class(creative_device):

    @editable
    ClassUI : class_selector_ui_device = class_selector_ui_device{}

    OnBegin<override>()<suspends> : void =
        ClassUI.Enable()
        ClassUI.ClassChangedEvent.Subscribe(OnChanged)

    OnChanged(Result : tuple(agent, int)) : void =
        ClassIndex := Result(1)
        if (ClassIndex = 0):
            Print("A player joined the Tank class")
        else if (ClassIndex = 1):
            Print("A player joined the Medic class")
        else:
            Print("A player picked class {ClassIndex}")

Gotchas

  • You must declare an @editable field and bind it in UEFN. A bare class_selector_ui_device.Show() won't compile — the device only exists through your field.
  • Show needs a concrete agent, not a ?agent. Events that hand you a ?agent (like a trigger's TriggeredEvent) must be unwrapped: if (Player := Agent?): ClassUI.Show(Player).
  • ClassChangedEvent is a tuple(agent, int), not a plain agent. Read the agent with Result(0) and the class index with Result(1). ClassSelectedEvent, UIOpenedEvent, and UIClosedEvent each hand a plain agent.
  • A disabled device is silent. Show() won't display and no events fire while the device is disabled. Call Enable() (typically in OnBegin) before relying on it.
  • message parameters need localized values. For HUD text or any message field, declare a <localizes> helper like InfoText<localizes>(S:string):message = "{S}" — there is no StringToMessage.
  • Verse doesn't auto-convert int↔float. The class index from ClassChangedEvent is an int; if you need it as a float for some math, convert explicitly.
  • Class definitions live in UEFN, not Verse. Which classes appear, their order, and the index numbers are set on the device in the editor — your code reacts to indices it doesn't itself define.

Device Settings & Options

The class_selector_ui_device User Options panel in the UEFN editor — every setting you can tune.

Class Selector Ui Device settings and options panel in the UEFN editor — Enabled, Label, Custom Singular Label, Custom Plural Label, Show Popup UI, Show in Map key menu
Class Selector Ui Device — User Options in the UEFN editor: Enabled, Label, Custom Singular Label, Custom Plural Label, Show Popup UI, Show in Map key menu
⚙️ Settings on this device (6)

Setting names read from the panel above — may be partial; the screenshots are the source of truth.

Enabled
Label
Custom Singular Label
Custom Plural Label
Show Popup UI
Show in Map key menu

Guides & scripts that use class_selector_ui_device

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

Build your own lesson with class_selector_ui_device

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 →