Reference Verse

creature_spawner_device: Ambush on the Sunny Cove

The cove looks peaceful — golden sand, cel-shaded palm trees, a wooden dock stretching into a turquoise lagoon. But the moment a player steps on the dock plate, the creature_spawner_device springs to life and floods the shore with enemies. This article shows you how to drive that ambush entirely from Verse using the spawner's real methods and events.

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.

Overview

The npc_spawner_device is a UEFN-only device (Early Access) that spawns AI-driven characters at runtime. Unlike the Guard Spawner or Character device — which are limited to Fortnite skins and simple placement — the NPC Spawner supports custom NPC Character Definitions, imported meshes, wildlife, patrol paths, and full Verse scripting.

When to reach for it:

  • You want enemies or friendly NPCs that spawn in response to game events (a player enters a zone, a timer fires, a puzzle is solved).
  • You need to know exactly how many NPCs are alive at any moment.
  • You want to respawn a wave of pirates after the last one is eliminated.
  • You need to teleport an NPC to a specific world position at runtime with SpawnAt.

The device lives in All > Fortnite > Devices > !Early Access > NPC Spawner inside the UEFN content browser.

API Reference

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

Walkthrough

Scenario: A sun-drenched cove with a wooden dock. When the round starts, two pirate guards spawn on the dock. When all guards are eliminated, the device resets and a fresh wave spawns — endless waves until the player wins via another condition. A cel-shaded island vibe, bright noon light, turquoise water.

This single device class wires together Enable, EliminatedEvent, GetAgents, Reset, and Spawn.

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

# Attach this creative_device to your island.
# Wire PirateSpawner to an npc_spawner_device placed on the dock.
# The NPC Spawner's "Max Alive Count" should be set to 2 in its device properties.
dock_wave_manager<public> := class(creative_device):

    # The NPC Spawner placed on the dock in the UEFN editor.
    @editable
    PirateSpawner : npc_spawner_device = npc_spawner_device{}

    # How many pirates make up one wave.
    WaveSize : int = 2

    # Tracks how many pirates from the current wave have been eliminated.
    var EliminatedCount : int = 0

    # Called once when the round begins.
    OnBegin<override>()<suspends> : void =
        # Enable the spawner so it is active.
        PirateSpawner.Enable()

        # Spawn the first wave manually so we control the exact moment.
        SpawnWave()

        # Subscribe to EliminatedEvent so we know when each pirate dies.
        # EliminatedEvent sends a device_ai_interaction_result.
        PirateSpawner.EliminatedEvent.Subscribe(OnPirateEliminated)

    # Spawns WaveSize pirates by calling Spawn() once per pirate.
    # Spawn() is a void, non-suspending call — it queues a spawn request.
    SpawnWave() : void =
        set EliminatedCount = 0
        var I : int = 0
        loop:
            if (I >= WaveSize):
                break
            PirateSpawner.Spawn()
            set I = I + 1

    # Handler called each time a pirate is eliminated.
    # device_ai_interaction_result carries Target (the NPC) and Source (the killer).
    OnPirateEliminated(Result : device_ai_interaction_result) : void =
        set EliminatedCount = EliminatedCount + 1
        if (EliminatedCount >= WaveSize):
            # All pirates down — reset the spawn counter and send a new wave.
            PirateSpawner.Reset()
            SpawnWave()

Line-by-line breakdown:

Line What it does
@editable PirateSpawner Exposes the device slot in the UEFN Details panel so you can drag-and-drop the placed NPC Spawner device.
PirateSpawner.Enable() Activates the spawner. Without this, the device won't respond to Spawn() calls if it was placed in a disabled state.
SpawnWave() Calls Spawn() in a loop. Each call asks the device to instantiate one NPC up to its configured Max Alive Count.
PirateSpawner.EliminatedEvent.Subscribe(OnPirateEliminated) Registers our handler. Every time any NPC from this spawner is eliminated, OnPirateEliminated fires.
PirateSpawner.Reset() Resets the internal spawn counter so the device can spawn a new full wave even though it already hit its max.

Common patterns

Pattern 1 — SpawnAt: Drop an NPC at a specific clifftop position

SpawnAt is <suspends> and returns ?agent — the NPC that was spawned, or false if the max count was reached. Use it when you need the NPC to appear at an exact world location rather than the device's placed position.

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

# Spawns a single NPC at a hard-coded clifftop position and stores the agent.
clifftop_spawn_example<public> := class(creative_device):

    @editable
    LookoutSpawner : npc_spawner_device = npc_spawner_device{}

    # The agent reference we get back from SpawnAt.
    var SpawnedGuard : ?agent = false

    OnBegin<override>()<suspends> : void =
        LookoutSpawner.Enable()

        # World position of the clifftop in centimetres (set these to match your island).
        ClifftopPosition : vector3 = vector3{X := 1200.0, Y := -400.0, Z := 850.0}
        ClifftopRotation : rotation = MakeRotationFromYawPitchRollDegrees(180.0, 0.0, 0.0)

        # SpawnAt is <suspends> — it waits until the NPC asset is loaded and the
        # character is fully initialized before returning.
        ResultAgent := LookoutSpawner.SpawnAt(ClifftopPosition, ClifftopRotation)

        if (A := ResultAgent?):
            # SpawnAt succeeded — store the agent for later use.
            set SpawnedGuard = option{A}
        else:
            # SpawnAt returned false — max spawn count was already reached.
            # Handle gracefully: maybe log to a HUD or skip.
            set SpawnedGuard = false

Pattern 2 — GetAgents: Count living NPCs to gate a door

GetAgents() returns all agents currently alive from this spawner. Combine it with a trigger to check whether any pirates are still standing before unlocking a treasure vault door.

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

# A trigger_device on the vault door checks whether all NPCs are dead.
# If they are, it enables the mutator_zone or prop_mover that opens the door.
vault_guard_check<public> := class(creative_device):

    @editable
    CoveSpawner : npc_spawner_device = npc_spawner_device{}

    # A trigger the player interacts with to attempt to open the vault.
    @editable
    VaultTrigger : trigger_device = trigger_device{}

    # A button_device or prop_mover that represents the vault door opening.
    @editable
    VaultDoor : mutator_zone_device = mutator_zone_device{}

    OnBegin<override>()<suspends> : void =
        CoveSpawner.Enable()
        CoveSpawner.Spawn()
        VaultTrigger.TriggeredEvent.Subscribe(OnVaultAttempt)

    # Fires when the player interacts with the vault trigger.
    OnVaultAttempt(Agent : ?agent) : void =
        # GetAgents() returns all currently-alive NPCs from this spawner.
        LivingGuards := CoveSpawner.GetAgents()
        if (LivingGuards.Length = 0):
            # No guards left — open the vault.
            VaultDoor.Enable()

Pattern 3 — EliminatedEvent result fields: reward the player who got the kill

EliminatedEvent sends a device_ai_interaction_result. The result carries both the eliminated NPC (Target) and the agent responsible (Source). Use Source to grant a reward to the player who landed the kill.

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

# Grants score to whoever eliminates a cove pirate.
cove_kill_reward<public> := class(creative_device):

    @editable
    PirateSpawner : npc_spawner_device = npc_spawner_device{}

    # An item_granter_device pre-loaded with a reward item in the editor.
    @editable
    RewardGranter : item_granter_device = item_granter_device{}

    OnBegin<override>()<suspends> : void =
        PirateSpawner.Enable()
        PirateSpawner.Spawn()
        PirateSpawner.EliminatedEvent.Subscribe(OnKill)

    # device_ai_interaction_result has a Source field — the agent who did the eliminating.
    OnKill(Result : device_ai_interaction_result) : void =
        # Source is ?agent — unwrap it before use.
        if (Killer := Result.Source?):
            # Grant a reward item to the player who made the kill.
            RewardGranter.GrantItem(Killer)

Gotchas

1. Early Access — things can break between UEFN versions

The NPC Spawner is flagged Early Access. Epic may change API signatures or device behavior between UEFN updates. Pin your project's UEFN version and test after every engine update.

2. SpawnAt is <suspends> — you must call it from an async context

SpawnAt loads the NPC asset asynchronously. You can only call it from a <suspends> function (like OnBegin) or from a spawn{} expression. Calling it from a plain synchronous method will cause a compile error.

# WRONG — plain method, not suspends
DoSpawn() : void =
    LookoutSpawner.SpawnAt(pos, rot)  # compile error: SpawnAt requires <suspends> context

# RIGHT — use spawn{} to fire-and-forget from a non-suspending context
DoSpawn() : void =
    spawn{ SpawnAtAsync() }

SpawnAtAsync()<suspends> : void =
    LookoutSpawner.SpawnAt(pos, rot)

3. Spawn() respects Max Alive Count — call Reset() first for new waves

If your NPC Spawner's Max Alive Count is 2 and you've already spawned 2 NPCs, further calls to Spawn() silently do nothing. You must call Reset() to clear the internal counter before spawning a new wave. This is the most common "my NPCs won't spawn" bug.

4. GetAgents() returns only currently alive agents

GetAgents() is a snapshot of living NPCs right now. It does not include NPCs that have been eliminated. Don't use its length as a cumulative kill counter — use EliminatedEvent subscriptions for that.

5. EliminatedEvent Source is ?agent, not agent

The Source field on device_ai_interaction_result is an optional agent — it can be false if the NPC died from fall damage or an environmental hazard with no player responsible. Always unwrap it with if (Killer := Result.Source?): before calling any agent-taking API.

6. The device must be Enable()d before Spawn() works

A freshly placed NPC Spawner defaults to enabled in the editor, but if you have Enabled at Game Start turned off in device properties, you must call Enable() in Verse before any Spawn() or SpawnAt() call, or nothing will happen.

Guides & scripts that use creature_spawner_device

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

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