Overview
The npc_spawner_device spawns NPCs built from a Character Definition asset — guards, monsters, dancing llamas, whatever you configured in the device's Details panel. Unlike the lighter creature/sentry spawners, it gives you full AI: patrol, navigation, and behavior.
Reach for it whenever you want living enemies or allies that the player fights, escorts, or sneaks past — a stronghold of guards, a wave of reinforcements, a single boss at the end of an arena.
In Verse you place the device in your level, then declare an @editable field of type npc_spawner_device and point it at that placed device in the Details panel. From there you can:
- Spawn an NPC at the device's configured spawn point.
- SpawnAt an exact world position (e.g. ambush corners).
- Enable / Disable the device to start/stop automatic spawning.
- DespawnAll to wipe the room, optionally crediting an eliminator.
- GetAgents to enumerate everyone this device currently owns.
- React to SpawnedEvent (someone appeared) and EliminatedEvent (someone died, and who killed them).
API Reference
npc_spawner_device
Used to spawn NPCs made with Character Definition asset.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
npc_spawner_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
SpawnedEvent |
SpawnedEvent<public>:listenable(agent) |
Signaled when a character is spawned. Sends the agent character who was spawned. |
EliminatedEvent |
EliminatedEvent<public>:listenable(device_ai_interaction_result) |
Signaled when a character is eliminated. Source is the agent that eliminated the character. If the character was eliminated by a non-agent then Source is 'false'. Target is the character that was eliminated. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>():void |
Enables this device. Characters will start to spawn. |
Disable |
Disable<public>():void |
Disables this device. Characters will despawn if Despawn AIs When Disabled is set. |
SetNPCCharacterDefinition |
SetNPCCharacterDefinition<public>(CharacterDefinition:npc_character_definition)<transacts><decides>:void |
Set the NPC character definition to use. Fails if the character type is different from one in the current definition. |
Spawn |
Spawn<public>():void |
Tries to spawn a character. |
DespawnAll |
DespawnAll<public>(Instigator:?agent):void |
Despawns all characters. If set, Instigator will be considered as the eliminator of those characters. |
SpawnAt |
SpawnAt<public>(Position:(/Verse.org/SpatialMath:)vector3, ?Rotation:?(/Verse.org/SpatialMath:)rotation |
Spawn a NPC at the given position. When Rotation is not provided, it will default to the Devices rotation. Returns the agent spawned or false if the device has reached its maximum spawn count. This function is <suspends>` because it takes |
GetAgents |
GetAgents<public>()<transacts>:[]agent |
Get all agents created by this device. |
Walkthrough
Scenario: A defense arena. When the round begins we enable a guard spawner and manually spawn the first wave. Every time a guard spawns we count it; every time one is eliminated we count it down, and when the last guard falls we despawn anything remaining and disable the spawner — the room is "cleared."
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
arena_controller := class(creative_device):
# Point this at the placed NPC Spawner in the Details panel.
@editable
GuardSpawner : npc_spawner_device = npc_spawner_device{}
# How many guards we still expect to spawn this wave.
var WaveSize : int = 4
# How many guards are currently alive.
var AliveCount : int = 0
OnBegin<override>()<suspends>:void =
# React when a guard appears or dies.
GuardSpawner.SpawnedEvent.Subscribe(OnGuardSpawned)
GuardSpawner.EliminatedEvent.Subscribe(OnGuardEliminated)
# Turn the device on so its own spawn settings can run...
GuardSpawner.Enable()
# ...and kick off the first wave manually.
for (I := 0..WaveSize - 1):
GuardSpawner.Spawn()
Sleep(0.5) # stagger so they don't all pop in the same frame
# Handler for SpawnedEvent — receives the agent that spawned.
OnGuardSpawned(Spawned : agent):void =
set AliveCount += 1
Print("Guard spawned. Alive now: {AliveCount}")
# Handler for EliminatedEvent — receives a device_ai_interaction_result.
OnGuardEliminated(Result : device_ai_interaction_result):void =
set AliveCount -= 1
Print("Guard down. Remaining: {AliveCount}")
# Who got the kill? Source is an ?agent, so unwrap it.
if (Killer := Result.Source?):
Print("Credited to a player.")
# When the room is empty, clear it out and shut the spawner off.
if (AliveCount <= 0):
GuardSpawner.DespawnAll(false)
GuardSpawner.Disable()
Print("Arena cleared!")
Line by line:
@editable GuardSpawner : npc_spawner_device— the field you bind to the placed device. Without this you cannot call any of its methods (a bareDevice.Spawn()fails with Unknown identifier).SpawnedEvent.Subscribe(OnGuardSpawned)—SpawnedEventislistenable(agent), so the handler takes a plainagent(already unwrapped, no?).EliminatedEvent.Subscribe(OnGuardEliminated)— this event islistenable(device_ai_interaction_result). The handler receives the struct, whoseSourceandTargetare both?agent.GuardSpawner.Enable()— turns the device on. Combined with its configured settings, this lets it spawn.for (...) { GuardSpawner.Spawn() ; Sleep(0.5) }—Spawn()tries to spawn one NPC each call.Sleeprequires the<suspends>context thatOnBeginprovides.- In
OnGuardEliminated,Result.Source?unwraps the optional eliminator —falseif a non-agent (like the environment) made the kill. DespawnAll(false)— removes every NPC the device owns. We passfalse(no instigator) for the optionalInstigator:?agentparameter.Disable()stops further spawning (and despawns AIs too if Despawn AIs When Disabled is checked on the device).
Common patterns
Ambush: SpawnAt exact world positions
SpawnAt places an NPC at a precise vector3 and returns the spawned agent (or false if the spawn cap is reached). It's <suspends> because the NPC takes time to load.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
ambush_device := class(creative_device):
@editable
Spawner : npc_spawner_device = npc_spawner_device{}
OnBegin<override>()<suspends>:void =
# Three ambush corners around the player's path.
Corners := array:
vector3{X := 1000.0, Y := 500.0, Z := 100.0}
vector3{X := -800.0, Y := 600.0, Z := 100.0}
vector3{X := 200.0, Y := -900.0, Z := 100.0}
for (Pos : Corners):
# SpawnAt returns ?agent — succeeds only if under the spawn cap.
if (Enemy := SpawnAtPos(Pos)):
Print("Ambusher placed.")
# Helper wrapping the suspending SpawnAt call.
SpawnAtPos(Pos : vector3)<suspends>:?agent =
return Spawner.SpawnAt(Pos)
Wipe the field when the boss dies
Use GetAgents to inspect what the device currently owns, then DespawnAll crediting the player who landed the killing blow.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
minion_clearer := class(creative_device):
@editable
MinionSpawner : npc_spawner_device = npc_spawner_device{}
@editable
BossSpawner : npc_spawner_device = npc_spawner_device{}
OnBegin<override>()<suspends>:void =
BossSpawner.EliminatedEvent.Subscribe(OnBossDown)
BossSpawner.Spawn()
OnBossDown(Result : device_ai_interaction_result):void =
# How many minions are still out there?
Minions := MinionSpawner.GetAgents()
Print("Boss dead. Clearing {Minions.Length} minions.")
# If a player got the boss, credit them with the cleanup too.
if (Slayer := Result.Source?):
MinionSpawner.DespawnAll(option{Slayer})
else:
MinionSpawner.DespawnAll(false)
Swap the NPC type before spawning
SetNPCCharacterDefinition is <decides> (it can fail) — it only succeeds if the new definition's character type matches the device's. Wrap it in if.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
variant_spawner := class(creative_device):
@editable
Spawner : npc_spawner_device = npc_spawner_device{}
# Assign an alternate NPC Character Definition asset here.
@editable
EliteDefinition : npc_character_definition = npc_character_definition{}
OnBegin<override>()<suspends>:void =
# Try to switch to the elite variant; only spawn if it took.
if (Spawner.SetNPCCharacterDefinition[EliteDefinition]):
Print("Swapped to elite NPCs.")
Spawner.Enable()
Spawner.Spawn()
else:
Print("Definition type mismatch — keeping current NPC.")
Gotchas
- You must have a placed device. The
npc_spawner_device{}default value is just a placeholder — bind the@editablefield to a real device in the Details panel, or no NPCs spawn. SpawnedEventvsEliminatedEventpayloads differ.SpawnedEventhands you a bareagent.EliminatedEventhands you adevice_ai_interaction_resultstruct — read.Source(the killer) and.Target(the victim), both?agent, and unwrap withif (A := Result.Source?):.SpawnAtis<suspends>and returns?agent. You can only call it from a suspending context (likeOnBeginor a<suspends>helper), and you must check the result — it returnsfalsewhen the device's spawn cap is hit.SetNPCCharacterDefinitioncan fail. It's<decides>, so call it with[ ]inside anif— it fails if the new definition is a different character type from the current one.DespawnAlltakes?agent, notagent. Passfalsefor no instigator, or wrap a real agent asoption{MyAgent}to credit the eliminations.Disable()only despawns if configured. Whether disabling removes existing NPCs depends on the device's Despawn AIs When Disabled setting — useDespawnAllif you want a guaranteed clear.- No
int↔floatauto-conversion. Spawn positions needfloatcomponents invector3{X := 1000.0, ...}—1000(an int) won't compile there.