Reference Devices compiles

channel_device: The Universal Signal Relay

The `channel_device` is Fortnite Creative's universal signal relay — it receives a signal, fires an event, and lets any number of other devices (or Verse scripts) react to it through a single connection point. Think of it as a switchboard: one call to `Transmit` ripples out to every subscriber of `ReceivedTransmitEvent`, making it perfect for chaining traps, unlocking doors, triggering cinematics, or synchronising multi-stage puzzles without tangling every device directly to every other device.

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

Overview

Every non-trivial Creative map eventually hits the same problem: you need one event to trigger many things, or you need a single named signal that multiple Verse devices can both fire and listen to. Wiring every source directly to every destination creates a spaghetti of connections that breaks the moment you add a new device.

channel_device solves this cleanly:

  • Any Verse code (or another device in the editor) calls Transmit(Agent) to broadcast a signal.
  • Every subscriber of ReceivedTransmitEvent reacts immediately — whether that's unlocking a vault door, spawning a VFX burst, or updating a score tracker.
  • Because the channel is a single named device you drop on the island, you can wire dozens of sources and dozens of listeners through it without touching the wiring of any individual device.

Reach for channel_device when:

  • You want a named, reusable signal bus (e.g. RoundEndChannel, BossDefeatedChannel).
  • Multiple independent Verse devices need to fire the same downstream logic.
  • You want editor-wired devices (buttons, triggers, etc.) to talk to Verse code through a clean handoff point.
  • You need to pass an optional agent instigator along with the signal.

API Reference

channel_device

Sends an Event for every signal it receives. A simple relay to connect multiple devices together. It can also broadcast an event to the playspace and listen for any other channel devices broadcasting to the playspace.

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

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

Events (subscribe a handler to react):

Event Signature Description
ReceivedTransmitEvent ReceivedTransmitEvent<public>:listenable(?agent) Signaled when either Transmit is called.

Methods (call these to make the device act):

Method Signature Description
Transmit Transmit<public>(Agent:?agent):void Calls ReceivedTransmitEvent, optionally with Agent as the instigator.

Walkthrough

Scenario: Vault Door That Opens When All Three Plates Are Stepped On

You have a vault room protected by three pressure plates. Only when all three are active should the vault door open. A channel_device acts as the "all clear" signal: your Verse manager listens to each plate, and when the final condition is met it calls Channel.Transmit(Agent). A second subscription on the same channel drives the door-open VFX spawner.

Island setup:

  • Three trigger_device instances (Plate1, Plate2, Plate3) — set to "Player steps on" mode.
  • One channel_device (VaultOpenChannel) — the shared signal bus.
  • One vfx_spawner_device (VaultVFX) — plays a burst when the vault opens.
  • One channel_device (VaultVFXChannel) — wired in the editor to enable VaultVFX (optional editor wiring; the Verse device also calls it directly).
vault_manager := class(creative_device):

    # The three pressure plates in the vault room
    @editable
    Plate1 : trigger_device = trigger_device{}
    @editable
    Plate2 : trigger_device = trigger_device{}
    @editable
    Plate3 : trigger_device = trigger_device{}

    # The channel that signals "vault is open" to all listeners
    @editable
    VaultOpenChannel : channel_device = channel_device{}

    # VFX spawner that plays the vault-open burst
    @editable
    VaultVFX : vfx_spawner_device = vfx_spawner_device{}

    # Track which plates are active
    var Plate1Active : logic = false
    var Plate2Active : logic = false
    var Plate3Active : logic = false

    OnBegin<override>()<suspends> : void =
        # Subscribe to each plate's triggered event
        Plate1.TriggeredEvent.Subscribe(OnPlate1Stepped)
        Plate2.TriggeredEvent.Subscribe(OnPlate2Stepped)
        Plate3.TriggeredEvent.Subscribe(OnPlate3Stepped)

        # Subscribe to the vault channel so we can trigger the VFX
        VaultOpenChannel.ReceivedTransmitEvent.Subscribe(OnVaultOpened)

    # Plate handlers — each marks its plate active, then checks all three
    OnPlate1Stepped(Agent : ?agent) : void =
        set Plate1Active = true
        CheckAllPlates(Agent)

    OnPlate2Stepped(Agent : ?agent) : void =
        set Plate2Active = true
        CheckAllPlates(Agent)

    OnPlate3Stepped(Agent : ?agent) : void =
        set Plate3Active = true
        CheckAllPlates(Agent)

    # Only transmit once all three plates are active
    CheckAllPlates(Agent : ?agent) : void =
        if (Plate1Active?, Plate2Active?, Plate3Active?):
            # Broadcast the vault-open signal with the triggering agent
            VaultOpenChannel.Transmit(Agent)

    # React to the vault-open signal — enable the VFX burst
    OnVaultOpened(Agent : ?agent) : void =
        VaultVFX.Enable()
        VaultVFX.Restart()
        # Optionally do something with the instigating agent
        if (A := Agent?):
            # A is a valid agent — could grant a reward, update a score, etc.
            VaultVFX.Enable()

Line-by-line breakdown:

Lines What's happening
@editable fields Expose all devices to the UEFN editor so you can drag-assign them
Plate1.TriggeredEvent.Subscribe(OnPlate1Stepped) Each plate's event feeds its own handler
OnPlate1Stepped(Agent : ?agent) The handler signature matches listenable(?agent) — the agent is optional
CheckAllPlates Central gate — only fires the channel when all three conditions are true
VaultOpenChannel.Transmit(Agent) Broadcasts the signal; every subscriber of ReceivedTransmitEvent fires
VaultOpenChannel.ReceivedTransmitEvent.Subscribe(OnVaultOpened) This device also listens to its own channel — valid and useful for decoupled logic
if (A := Agent?) Safe unwrap of the optional agent before use

Common patterns

Pattern 1 — Calling Transmit to fire a named game event

Use Transmit as a fire-and-forget broadcast. Here a button press signals a round-end channel that other systems (scoreboard, barrier, timer) can all subscribe to independently.

round_end_broadcaster := class(creative_device):

    @editable
    EndButton : button_device = button_device{}

    # Shared signal bus — other devices subscribe to this in the editor or via Verse
    @editable
    RoundEndChannel : channel_device = channel_device{}

    OnBegin<override>()<suspends> : void =
        EndButton.InteractedWithEvent.Subscribe(OnEndButtonPressed)

    OnEndButtonPressed(Agent : agent) : void =
        # Wrap the concrete agent in an optional before passing to Transmit
        RoundEndChannel.Transmit(option{Agent})

Key point: Transmit takes ?agent (an optional agent). When you have a concrete agent, wrap it with option{Agent} to match the signature. Pass false (the zero value for ?agent) when there is no meaningful instigator.


Pattern 2 — Subscribing to ReceivedTransmitEvent to react to an editor-wired signal

Here the channel is wired entirely in the editor (e.g. a Button device's "On Interact" output feeds the channel). The Verse device just listens and responds — no Transmit call needed from code.

boss_defeat_listener := class(creative_device):

    # This channel is wired in the editor: BossHealthBar "On Zero" -> BossDefeatedChannel
    @editable
    BossDefeatedChannel : channel_device = channel_device{}

    @editable
    RewardVFX : vfx_spawner_device = vfx_spawner_device{}

    OnBegin<override>()<suspends> : void =
        BossDefeatedChannel.ReceivedTransmitEvent.Subscribe(OnBossDefeated)

    OnBossDefeated(Agent : ?agent) : void =
        # Enable and burst the reward VFX for everyone
        RewardVFX.Enable()
        RewardVFX.Restart()
        # If we care who landed the killing blow, unwrap the agent
        if (Killer := Agent?):
            # Killer is now a valid agent — grant XP, show callout, etc.
            RewardVFX.Restart()

Key point: ReceivedTransmitEvent is a listenable(?agent), so the handler always receives ?agent. Always unwrap with if (A := Agent?): before treating it as a concrete agent.


Pattern 3 — Using a channel as a two-way handshake between two Verse devices

Two separate Verse devices share one channel_device. Device A transmits; Device B listens and then transmits back on a second channel; Device A listens to that. This creates a clean request/acknowledge pattern.

lock_controller := class(creative_device):

    @editable
    UnlockRequestChannel : channel_device = channel_device{}

    @editable
    UnlockGrantedChannel : channel_device = channel_device{}

    @editable
    DoorVFX : vfx_spawner_device = vfx_spawner_device{}

    OnBegin<override>()<suspends> : void =
        # Listen for an unlock request from any source
        UnlockRequestChannel.ReceivedTransmitEvent.Subscribe(OnUnlockRequested)
        # Listen for the grant acknowledgement
        UnlockGrantedChannel.ReceivedTransmitEvent.Subscribe(OnUnlockGranted)

    OnUnlockRequested(Agent : ?agent) : void =
        # Validate and then grant — here we always grant for simplicity
        UnlockGrantedChannel.Transmit(Agent)

    OnUnlockGranted(Agent : ?agent) : void =
        DoorVFX.Enable()
        DoorVFX.Restart()

Key point: A device can both Transmit on a channel and subscribe to ReceivedTransmitEvent on the same or a different channel. This makes channel_device a natural building block for event-driven state machines.

Gotchas

1. Transmit takes ?agent, not agent

The signature is Transmit(Agent : ?agent). If you have a concrete agent (e.g. from a button's InteractedWithEvent), you must wrap it: Channel.Transmit(option{Agent}). Passing a bare agent is a type error. To signal with no instigator, pass false — the zero/empty value for an optional in Verse.

2. Always unwrap ?agent before use

ReceivedTransmitEvent delivers ?agent. Accessing agent methods directly on an optional is a compile error. Always guard with:

if (A := Agent?):
    # A is agent here

Skipping this is the single most common runtime-logic bug with channel events.

3. @editable is mandatory for placed devices

You cannot reference a placed channel_device with a bare identifier. It must be declared as an @editable field inside your creative_device class and assigned in the UEFN editor. A bare MyChannel.Transmit(...) with no field declaration will fail with Unknown identifier.

4. Subscribe in OnBegin, not at field initialisation

Event subscriptions must happen at runtime inside OnBegin<override>()<suspends>. Subscribing at the class field level (outside a method) is not valid Verse. If your handler never fires, check that Subscribe is called inside OnBegin.

5. channel_device is <final> — you cannot subclass it

The class is marked <concrete><final>, meaning you cannot extend it. If you need custom behaviour, wrap it: hold a channel_device as a field in your own creative_device and delegate to it.

6. One channel, many listeners — order is not guaranteed

All subscribers to ReceivedTransmitEvent fire when Transmit is called, but Verse does not guarantee the order in which concurrent subscribers execute. Do not write logic that depends on subscriber A completing before subscriber B starts.

Device Settings & Options

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

Channel Device settings and options panel in the UEFN editor — Show Debug Message
Channel Device — User Options in the UEFN editor: Show Debug Message
⚙️ Settings on this device (1)

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

Show Debug Message

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