Reference Verse

Casting & Subtypes in Verse: Runtime Type Checks on a Sunlit Dock

Your pirate island has three kinds of visitors — Scouts, Captains, and Admirals — and each deserves a different welcome message on the dock. Verse's subtype casting system lets you inspect the *runtime* type of an `agent` (or any `<castable>` object) and branch on it safely, without crashing when the cast fails. This article teaches the `<castable>` specifier, fallible bracket-cast `Type[Value]`, and how to wire that pattern into real trigger_device and hud_message_device calls.

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 Knottrigger_device in ~90 seconds.

Overview

A subtype cast is how you tell Verse "I know this generic object is really a specific type — let me use it as that type." It solves a very common UEFN problem: functions like FindCreativeObjectsWithTag() / GetCreativeObjectsWithTag() return []creative_object_interface, because a single tag might match many different kinds of actors. That interface has almost no useful methods on it. To call TurnOn(), SetMesh(), or Toggle(), you must first cast the generic value into the concrete class.

The casting syntax is TargetType[value]. The square brackets mean it is a failable (fallible) expression: if value is not actually of TargetType, the cast fails and the surrounding if/for branch is skipped. This is exactly what you want when a tag matches ten props but only three are lights — the cast quietly filters out everything that isn't a light.

Reach for subtype casting whenever you:

  • Grab a batch of tagged actors and need to drive them as a specific device.
  • Store a mixed list of objects and want to act only on the ones of a certain type.
  • Convert a base creative_object reference into a creative_prop so you can move or reskin it.

API Reference

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

Walkthrough

Scenario: A vault room full of lights. When a player steps on a pressure plate (a trigger_device), every light tagged alarm_light snaps on red-alert style — we find them all by tag, cast each generic actor to customizable_light_device, and call TurnOn(). Step off, and we TurnOff() them.

We'll add a power_light tag in UEFN to each Customizable Light we want controlled, then let Verse discover them at runtime — no need to wire up a hundred editable fields.

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

# Tag placed on every Customizable Light we want to control.
alarm_light := class(tag){}

vault_alarm := class(creative_device):

    # The pressure plate the player steps on.
    @editable
    AlarmPlate : trigger_device = trigger_device{}

    # Cache the lights we find so we only search once.
    var Lights : []customizable_light_device = array{}

    OnBegin<override>()<suspends>:void =
        # 1. Find every actor carrying the alarm_light tag.
        FoundActors := GetCreativeObjectsWithTag(alarm_light{})

        # 2. Cast each generic actor into the concrete light device.
        for (Actor : FoundActors):
            if (Light := customizable_light_device[Actor]):
                set Lights += array{Light}

        # 3. React to the plate being stepped on / off.
        AlarmPlate.TriggeredEvent.Subscribe(OnStepOn)

    # Player stepped on the plate -> raise the alarm.
    OnStepOn(Agent : ?agent):void =
        for (Light : Lights):
            Light.TurnOn()

Line by line:

  • alarm_light := class(tag){} declares a gameplay tag as a type. In UEFN you add this tag to each light's Gameplay Tags list.
  • @editable AlarmPlate : trigger_device is the placed plate. Any placed device you call must be an @editable field on a creative_device class — a bare SomeDevice.Method() won't compile.
  • var Lights : []customizable_light_device caches the results so we don't re-scan every step.
  • GetCreativeObjectsWithTag(alarm_light{}) returns []creative_object_interface — the generic list.
  • if (Light := customizable_light_device[Actor]) is the subtype cast. The square brackets make it failable: for actors that aren't lights, the cast fails and the if body is skipped, so only real lights land in Lights.
  • set Lights += array{Light} appends each successfully-cast light.
  • AlarmPlate.TriggeredEvent.Subscribe(OnStepOn) wires the plate's event to our handler (subscribe in OnBegin; the handler is a class-scope method).
  • In OnStepOn we loop the cached lights and call the real device method TurnOn().

Common patterns

Pattern 1 — Toggle tagged lights with a button. Same cast, but we call Toggle() so one button flips them all on/off.

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

room_light := class(tag){}

light_switch := class(creative_device):

    @editable
    SwitchButton : button_device = button_device{}

    var Lights : []customizable_light_device = array{}

    OnBegin<override>()<suspends>:void =
        for (Actor : GetCreativeObjectsWithTag(room_light{})):
            if (Light := customizable_light_device[Actor]):
                set Lights += array{Light}
        SwitchButton.InteractedWithEvent.Subscribe(OnPressed)

    OnPressed(Agent : agent):void =
        for (Light : Lights):
            Light.Toggle()

Pattern 2 — Cast a base object into creative_prop to reskin it. Tags can match spawned props too. Here we cast to creative_prop and call Hide() on each to make a set of decoy props vanish.

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

decoy := class(tag){}

decoy_remover := class(creative_device):

    @editable
    RemoveTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        RemoveTrigger.TriggeredEvent.Subscribe(OnTriggered)

    OnTriggered(Agent : ?agent):void =
        for (Actor : GetCreativeObjectsWithTag(decoy{})):
            if (Prop := creative_prop[Actor]):
                Prop.Hide()

Pattern 3 — Dim tagged lights on demand. After caching lights via cast, drive a different method — DimLight() — to fade the room.

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

mood_light := class(tag){}

mood_setter := class(creative_device):

    @editable
    DimButton : button_device = button_device{}

    var Lights : []customizable_light_device = array{}

    OnBegin<override>()<suspends>:void =
        for (Actor : GetCreativeObjectsWithTag(mood_light{})):
            if (Light := customizable_light_device[Actor]):
                set Lights += array{Light}
        DimButton.InteractedWithEvent.Subscribe(OnDim)

    OnDim(Agent : agent):void =
        for (Light : Lights):
            Light.DimLight()

Gotchas

  • The cast is failable, so it needs a failure context. customizable_light_device[Actor] must live inside if, for, or another failable position. Writing it as a plain statement won't compile.
  • Use {} on the tag when calling the function. GetCreativeObjectsWithTag(alarm_light{}) — you pass a tag value, so construct it with alarm_light{}, not the bare type name.
  • GetCreativeObjectsWithTag is deprecated in favor of FindCreativeObjectsWithTag, but it still works and returns the same []creative_object_interface. Either way the casting technique is identical.
  • Cast in OnBegin, cache the result. Re-scanning by tag on every trigger fire is wasteful. Cast once, store the concrete devices in a var []customizable_light_device, then just call methods on the cached list.
  • A wrong-type cast doesn't crash — it just fails. If one of your tagged actors is a barrier instead of a light, customizable_light_device[Actor] silently fails and skips it. That's a feature: it filters your mixed list for free, but it also means a mistyped tag can leave Lights empty with no error.
  • ?agent vs agent handlers. trigger_device.TriggeredEvent hands you a ?agent (optional) — unwrap with if (A := Agent?): before using the player. button_device.InteractedWithEvent hands a non-optional agent directly.
  • Int↔float never auto-converts. If you later mix casting with brightness math, remember Verse won't quietly promote an int to a float.

Guides & scripts that use trigger_device

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

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