Reference Devices

gameplay_controls_device: Swapping a Player's Control Scheme on Cue

Ever wanted a player's controls to change the moment they hop into a turret, possess a drone, or enter a stealth section? The gameplay_controls_device lets you push a custom control scheme onto a player's control stack — and pop it back off when they're done. This article shows you how to drive it from Verse.

Updated
The code on this reference page is provided as-is and did not pass the latest compile check — treat the examples as a starting point and verify in your project.
Watch the Knotgameplay_controls_device in ~90 seconds.

Overview

The gameplay_controls_device represents a single gameplay control scheme you configure in UEFN (button mappings, sensitivity, and which inputs are allowed). What makes it powerful is that controls live on a per-player stack: when you push a scheme onto a player, it becomes the active control mode; when you pop it, the player falls back to whatever scheme was beneath it.

This solves a very common game-design problem: temporary control modes. A few concrete examples:

  • A player steps into a turret — push a "turret aim" scheme that disables jumping. Step out — pop it, normal movement returns.
  • A stealth section where sprint is removed and crouch is forced — push on entry, pop on exit.
  • A mini-game with a custom control layout that only applies while the player is inside the arena.

Because it's a stack, you can layer schemes and they unwind cleanly in reverse order. The device exposes six methods: Enable/Disable toggle whether the device participates at all, while AddTo/AddToAll push the scheme onto one player or everyone, and RemoveFrom/RemoveFromAll pop it back off.

Note: gameplay_controls_device is an abstract base. In UEFN you'll place a concrete control-scheme device (its specific control-mode variant) — but in Verse you reference it through this gameplay_controls_device type, and these are the methods you call.

API Reference

gameplay_controls_device

Used to update the gameplay controls scheme based on current control mode.

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

gameplay_controls_device<public> := class<abstract><epic_internal>(creative_device_base):

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.
AddTo AddTo<public>(Agent:agent):void Adds the gameplay control to the Player's gameplay controls stack and pushes it to be the active control.
AddToAll AddToAll<public>():void Adds the gameplay control to all Agents gameplay controls stack and pushes it to be the active control.
RemoveFrom RemoveFrom<public>(Agent:agent):void Removes the gameplay control from the Agent's gameplay controls stack and pops from being the active control replacing it with the next one in the stack.
RemoveFromAll RemoveFromAll<public>():void Removes the gameplay control from all Agents gameplay controls stack and pops from being the active control replacing it with the next one in the stack.

Walkthrough

Scenario: We have a turret pad. When a player steps onto a trigger volume (the turret seat), we enable a custom "turret controls" scheme and push it onto that player. When they step onto an exit trigger, we pop the scheme so their normal controls come back.

We'll use a trigger_device for the seat and another for the exit. The triggers' TriggeredEvent is a listenable(?agent), so we unwrap the agent before calling AddTo/RemoveFrom.

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

# A turret seat that swaps the player's control scheme while they're sitting.
turret_seat_device := class(creative_device):

    # The custom control scheme configured in UEFN (e.g. turret aim controls).
    @editable
    TurretControls:gameplay_controls_device = gameplay_controls_device{}

    # The trigger the player steps on to take the seat.
    @editable
    EnterSeatTrigger:trigger_device = trigger_device{}

    # The trigger the player steps on to leave the seat.
    @editable
    ExitSeatTrigger:trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        # Make sure the control device is active before we ever push it.
        TurretControls.Enable()
        # Wire up the seat enter / exit triggers.
        EnterSeatTrigger.TriggeredEvent.Subscribe(OnEnterSeat)
        ExitSeatTrigger.TriggeredEvent.Subscribe(OnExitSeat)

    # Called when a player steps onto the seat trigger.
    OnEnterSeat(Agent:?agent):void =
        if (Player := Agent?):
            # Push the turret scheme onto this player's control stack.
            # It becomes their active control mode immediately.
            TurretControls.AddTo(Player)

    # Called when a player steps onto the exit trigger.
    OnExitSeat(Agent:?agent):void =
        if (Player := Agent?):
            # Pop the turret scheme; the player's previous controls return.
            TurretControls.RemoveFrom(Player)

Line by line:

  • @editable TurretControls:gameplay_controls_device — declares a field you bind, in the UEFN Details panel, to the control-scheme device you placed. A bare device variable can't be called; the @editable field is what makes TurretControls.AddTo(...) legal.
  • OnBegin<override>()<suspends>:void = — the entry point that runs when the game starts.
  • TurretControls.Enable() — turns the device on so it's eligible to be pushed. If it's disabled, pushing it has no effect.
  • EnterSeatTrigger.TriggeredEvent.Subscribe(OnEnterSeat) — registers our handler method. The handler signature must accept (Agent:?agent) because TriggeredEvent is a listenable(?agent).
  • if (Player := Agent?): — the agent arrives wrapped in an option; this unwraps it. If there's no agent we skip safely.
  • TurretControls.AddTo(Player) — pushes the scheme onto this specific player. Other players are unaffected.
  • TurretControls.RemoveFrom(Player) — pops it back off, restoring the player's prior control mode.

Common patterns

Push a control scheme on EVERY player when an event starts

Use AddToAll() to apply the scheme to the whole lobby at once — great for a phase of a round where everyone shares special controls (e.g. a vehicle-only segment).

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

phase_controls_device := class(creative_device):

    @editable
    SpecialControls:gameplay_controls_device = gameplay_controls_device{}

    # A button that starts the special phase for everyone.
    @editable
    StartPhaseButton:button_device = button_device{}

    OnBegin<override>()<suspends>:void =
        SpecialControls.Enable()
        StartPhaseButton.InteractedWithEvent.Subscribe(OnStartPhase)

    OnStartPhase(Agent:agent):void =
        # Push the scheme onto every player's control stack at once.
        SpecialControls.AddToAll()

Pop the scheme off everyone when the phase ends

RemoveFromAll() unwinds the stack for all players, returning each to their previous control mode.

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

end_phase_controls_device := class(creative_device):

    @editable
    SpecialControls:gameplay_controls_device = gameplay_controls_device{}

    # A trigger that fires when the special phase ends.
    @editable
    EndPhaseTrigger:trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        EndPhaseTrigger.TriggeredEvent.Subscribe(OnEndPhase)

    OnEndPhase(Agent:?agent):void =
        # Remove the scheme from all players, restoring normal controls.
        SpecialControls.RemoveFromAll()

Disable the device to block all pushes

If you want to globally turn a control scheme off (so no further AddTo calls take effect), call Disable(). Re-enable later with Enable().

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

controls_toggle_device := class(creative_device):

    @editable
    StealthControls:gameplay_controls_device = gameplay_controls_device{}

    # Toggles the stealth control scheme on / off globally.
    @editable
    ToggleButton:button_device = button_device{}

    var Active:logic = false

    OnBegin<override>()<suspends>:void =
        ToggleButton.InteractedWithEvent.Subscribe(OnToggle)

    OnToggle(Agent:agent):void =
        if (Active?):
            StealthControls.Disable()
            set Active = false
        else:
            StealthControls.Enable()
            set Active = true

Gotchas

  • It's a stack, not a switch. Every AddTo pushes a new active scheme; every RemoveFrom pops one. If you push twice and pop once, the scheme is still active. Keep your pushes and pops balanced per player, or you'll get "stuck" control modes.
  • Enable before you push. If the device is Disable()d, calling AddTo/AddToAll won't make the scheme active. Call Enable() once in OnBegin (or whenever you want pushes to start working).
  • AddTo is per-player; AddToAll is everyone. Don't loop over players calling AddTo when you mean AddToAll — and don't call AddToAll when you only want one player to get the new controls.
  • Unwrap the option agent. trigger_device.TriggeredEvent hands you (Agent:?agent). You must do if (Player := Agent?): before passing it to AddTo / RemoveFrom, which expect a plain agent.
  • The type is abstract. gameplay_controls_device is abstract — you can't construct a meaningful one in code; you bind the @editable field to a concrete control-scheme device placed in your level. The = gameplay_controls_device{} default is just a placeholder until you assign it in UEFN.
  • Removing when nothing was pushed. Calling RemoveFrom/RemoveFromAll when the scheme isn't on a player's stack simply does nothing harmful, but it also won't "reset" controls — there has to be a matching prior push.

Build your own lesson with gameplay_controls_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 →