Overview
The guard_spawner_device is your one-stop shop for AI combatants. Placed in the editor, it spawns guard-type NPCs that already know how to patrol an area (or follow an AI Patrol Path Node), fill a suspicion meter when they see a target, go Alert, chase, and attack. You configure their weapons, team, accuracy, and spawn count in the device's User Options; you drive the timing and reactions from Verse.
Reach for it when you need:
- PvE enemies — waves of guards to fight through.
- Objective defense — guards protecting a vault, terminal, or capture point that turn hostile when players approach.
- Hireable allies — guards a player can recruit that leash to and protect them.
- Scripted encounters — spawn a guard at an exact position, then force it to hunt a chosen player.
The key rule to remember: changing device properties only affects guards spawned afterward, and the device has a spawn limit. Verse lets you subscribe to a rich set of perception events (SuspiciousEvent, AlertedEvent, TargetLostEvent, UnawareEvent) plus combat events (DamagedEvent, EliminatedEvent, EliminatingEvent) and hiring events (HiredEvent, DismissedEvent), so you can react to the full guard lifecycle.
API Reference
guard_spawner_device
Used to spawn guards that can patrol and attack other
agents. Changing properties will only affect newly spawned guards.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
guard_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 guard is spawned. Sends the agent guard who was spawned. |
AlertedEvent |
AlertedEvent<public>:listenable(device_ai_interaction_result) |
Signaled when a guard has identified an opponent. Source is the guard who is aware. Target is the agent who alerted the guard. |
TargetLostEvent |
TargetLostEvent<public>:listenable(device_ai_interaction_result) |
Signaled when a guard has lost track of a target. Source is the guard that lost track of a target. Target is the agent no longer targeted by the guard. |
SuspiciousEvent |
SuspiciousEvent<public>:listenable(agent) |
Signaled when a guard becomes suspicious. Sends the agent guard who is suspicious. |
UnawareEvent |
UnawareEvent<public>:listenable(agent) |
Signaled when a guard becomes unaware. Sends the agent guard who is unaware. |
DamagedEvent |
DamagedEvent<public>:listenable(device_ai_interaction_result) |
Signaled when guard is damaged. Source is the agent that damaged the guard. If the guard was damaged by a non-agent then false is returned. Target is the guard that was damaged. |
EliminatedEvent |
EliminatedEvent<public>:listenable(device_ai_interaction_result) |
Signaled when a guard is eliminated. Source is the agent that eliminated the guard. If the guard was eliminated by a non-agent then Source is 'false'. Target is the guard that was eliminated. |
EliminatingEvent |
EliminatingEvent<public>:listenable(device_ai_interaction_result) |
Signaled when a guard eliminates an agent. Source is the guard that eliminated the agent. Target is the agent that was eliminated. |
HiredEvent |
HiredEvent<public>:listenable(device_ai_interaction_result) |
Signaled when a guard is hired by a player. Source is the agent who hired the guard. Target is the guard that was hired. |
DismissedEvent |
DismissedEvent<public>:listenable(device_ai_interaction_result) |
Signaled when a guard is dismissed by a player. Source is the agent who dismissed the guard. Target is the guard that was dismissed. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>():void |
Enables this device. Guards will start to spawn. |
Disable |
Disable<public>():void |
Disables this device. Guards will despawn if Despawn Guards When Disabled is set. |
Spawn |
Spawn<public>():void |
Tries to spawn a guard. |
Spawn |
Spawn<public>(Instigator:agent):void |
Tries to spawn a guard. If Auto Hire When Spawned is set to Triggering Player the guard will be hired by Instigator. |
Despawn |
Despawn<public>():void |
Despawns guards. |
Despawn |
Despawn<public>(Instigator:agent):void |
Despawns guards. Instigator will be considered as the eliminator of those guards. |
Hire |
Hire<public>(Instigator:agent):void |
Hires guards to Instigator's team. |
DismissAllHiredGuards |
DismissAllHiredGuards<public>():void |
Dismisses all hired guards. |
DismissAgentHiredGuards |
DismissAgentHiredGuards<public>(Instigator:agent):void |
Dismisses all hired guards that were recruited by Instigator. |
ForceAttackTarget |
ForceAttackTarget<public>(Target:agent, ?ForgetTime:float |
Forces guards to attack Target, bypassing perception checks. 'ForgetTime' ranges from 0.0 to 600.0 (in seconds, default is 600.0), it is the time after which the target will be ignored if not found. 'ForgetDistance' ranges from 0.0 to 100 |
SetGuardsHireable |
SetGuardsHireable<public>():void |
Allows guards to be hired. |
SetGuardsNotHireable |
SetGuardsNotHireable<public>():void |
Prevents guards from being hired. |
GetSpawnLimit |
GetSpawnLimit<public>()<transacts>:int |
Returns the spawn limit of the device. |
GetAgents |
GetAgents<public>()<transacts>:[]agent |
Get all agents created by this device. |
SetName |
SetName<public>(InText:message):void |
Sets the name of the guard in the hire the guard conversation and elimination feed. |
GetName |
GetName<public>():string |
Gets the name of the guard in the hire the guard conversation and elimination feed. |
SpawnAt |
SpawnAt<public>(Position:(/Verse.org/SpatialMath:)vector3, ?Rotation:?(/Verse.org/SpatialMath:)rotation |
Spawn a guard 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 tak |
Walkthrough
Let's build a vault-defense encounter. Two guards defend a vault. A pressure plate (a trigger_device) tells the spawner when the raiding player steps into the room, so we spawn the defenders and force them to attack that player. We track eliminations, and when the last defender falls we open the vault door (a barrier_device we Disable to let the player through).
using { /Fortnite.com/Devices }
using { /Fortnite.com }
using { /Verse.org/Simulation }
using { /Verse.org/Chat }
vault_defense := class(creative_device):
# The spawner that produces our defending guards.
@editable
GuardSpawner : guard_spawner_device = guard_spawner_device{}
# Pressure plate the raider steps on to trigger the fight.
@editable
IntrusionPlate : trigger_device = trigger_device{}
# The vault door barrier; we disable it once all guards are down.
@editable
VaultDoor : barrier_device = barrier_device{}
# How many guards are still alive.
var GuardsRemaining : int = 0
# Localized helper so we can name our guards (message, not string).
NameText<localizes>(S : string) : message = "{S}"
OnBegin<override>()<suspends> : void =
# Give the guards a name shown in the elimination feed.
GuardSpawner.SetName(NameText("Vault Sentinel"))
# React to the whole guard lifecycle.
GuardSpawner.SpawnedEvent.Subscribe(OnGuardSpawned)
GuardSpawner.AlertedEvent.Subscribe(OnGuardAlerted)
GuardSpawner.EliminatedEvent.Subscribe(OnGuardEliminated)
# The plate starts the encounter.
IntrusionPlate.TriggeredEvent.Subscribe(OnPlateStepped)
# Player stepped on the plate: wake the spawner and force the fight.
OnPlateStepped(Agent : ?agent) : void =
if (Raider := Agent?):
# Enable the device so it will accept spawns.
GuardSpawner.Enable()
# Spawn up to the device's configured count.
SpawnLimit := GuardSpawner.GetSpawnLimit()
var Count : int = 0
loop:
if (Count >= SpawnLimit):
break
GuardSpawner.Spawn()
set Count = Count + 1
# Everything the device has produced should hunt the raider.
for (Guard : GuardSpawner.GetAgents()):
GuardSpawner.ForceAttackTarget(Raider, ?ForgetTime := 600.0)
# Count each guard as it appears.
OnGuardSpawned(Guard : agent) : void =
set GuardsRemaining = GuardsRemaining + 1
# A guard spotted someone — nothing extra needed, but log-worthy hook.
OnGuardAlerted(Result : device_ai_interaction_result) : void =
# Source is the guard, Target is who alerted them (optional agent).
if (Target := Result.Target?):
# We could highlight the raider here; left as a hook.
GuardSpawner.ForceAttackTarget(Target, ?ForgetTime := 600.0)
# A guard died. When the last one falls, open the vault.
OnGuardEliminated(Result : device_ai_interaction_result) : void =
set GuardsRemaining = GuardsRemaining - 1
if (GuardsRemaining <= 0):
# Reward: drop the barrier so the player can loot the vault.
VaultDoor.Disable()
Line by line:
- The
@editablefields let you drop the real placed devices into the panel. Without declaring the spawner as a field you could not call any of its methods. NameText<localizes>builds amessage—SetNamerequires localized text, so we can never pass a raw string.- In
OnBeginwe subscribe our handler methods to the device's events.SpawnedEventhands us a rawagent; the interaction events hand us adevice_ai_interaction_resultwithSourceandTarget. OnPlateSteppedunwraps the optionalagentwithif (Raider := Agent?). WeEnable()the spawner, then callSpawn()in a loop up toGetSpawnLimit()so we never exceed the device's cap.GetAgents()returns every agent this device created; we loop andForceAttackTargeteach toward the raider, bypassing perception so the fight starts immediately.?ForgetTime := 600.0keeps them locked on for the full 10 minutes.OnGuardEliminateddecrements the counter and, when it reaches zero,Disable()s the barrier — the vault opens.
Common patterns
Hireable ally guards
Let a player recruit a guard from a button, then hire the spawner's guards onto their team. React to the HiredEvent.
using { /Fortnite.com/Devices }
using { /Fortnite.com }
using { /Verse.org/Simulation }
ally_recruiter := class(creative_device):
@editable
AllySpawner : guard_spawner_device = guard_spawner_device{}
@editable
HireButton : button_device = button_device{}
OnBegin<override>()<suspends> : void =
# Allow these guards to be recruited.
AllySpawner.SetGuardsHireable()
AllySpawner.HiredEvent.Subscribe(OnHired)
HireButton.InteractedWithEvent.Subscribe(OnHirePressed)
OnHirePressed(Agent : agent) : void =
# Hire whatever this spawner has produced onto the player's team.
AllySpawner.Hire(Agent)
OnHired(Result : device_ai_interaction_result) : void =
# Source is the player who hired; Target is the recruited guard.
if (Player := Result.Source?):
# Guard now leashes to and protects the player.
AllySpawner.SetGuardsNotHireable()
Suspicion / detection feedback
Use the perception events to build a stealth loop — light up an alarm when a guard gets suspicious, clear it when they lose the target.
using { /Fortnite.com/Devices }
using { /Fortnite.com }
using { /Verse.org/Simulation }
alarm_system := class(creative_device):
@editable
PatrolSpawner : guard_spawner_device = guard_spawner_device{}
# A light or FX driver enabled while guards are alerted.
@editable
AlarmLight : device_ability_kit = device_ability_kit{}
OnBegin<override>()<suspends> : void =
PatrolSpawner.Enable()
PatrolSpawner.SuspiciousEvent.Subscribe(OnSuspicious)
PatrolSpawner.TargetLostEvent.Subscribe(OnTargetLost)
PatrolSpawner.UnawareEvent.Subscribe(OnUnaware)
OnSuspicious(Guard : agent) : void =
# A guard sees something. Escalate by forcing a spawn of reinforcements.
PatrolSpawner.Spawn()
OnTargetLost(Result : device_ai_interaction_result) : void =
# Guard lost track — dismiss any player-hired guards to reset the scene.
PatrolSpawner.DismissAllHiredGuards()
OnUnaware(Guard : agent) : void =
# Guard fully calmed down; no action needed here.
PatrolSpawner.Disable()
Scripted spawn at an exact spot
SpawnAt is <suspends> and returns the spawned agent (or fails). Use it for cinematic ambushes at precise coordinates.
using { /Fortnite.com/Devices }
using { /Fortnite.com }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
ambush_trap := class(creative_device):
@editable
AmbushSpawner : guard_spawner_device = guard_spawner_device{}
@editable
Tripwire : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
AmbushSpawner.Enable()
Tripwire.TriggeredEvent.Subscribe(OnTripped)
OnTripped(Agent : ?agent) : void =
if (Victim := Agent?):
spawn { SpringAmbush(Victim) }
SpringAmbush(Victim : agent)<suspends> : void =
# Spawn a guard right beside the victim's ambush point.
SpawnPos : vector3 = vector3{ X := 500.0, Y := 0.0, Z := 100.0 }
if (Guard := AmbushSpawner.SpawnAt(SpawnPos)):
# Lock the fresh guard onto the victim immediately.
AmbushSpawner.ForceAttackTarget(Victim, ?ForgetTime := 120.0)
Gotchas
- Properties are apply-on-spawn. Weapon, team, accuracy, and name changes only affect guards spawned after you set them.
SetNamebefore youSpawn, not after. SetNameneeds amessage, not a string. There is noStringToMessage. Declare aNameText<localizes>(S:string):message = "{S}"helper and passNameText("...").- Respect the spawn limit.
Spawn()silently does nothing once the device hitsGetSpawnLimit(). Loop againstGetSpawnLimit()(as in the walkthrough) or the device just stops producing guards. If you need a fresh batch, the device also offersResetto reset the spawn count. - Interaction-event fields are optional. In a
device_ai_interaction_result,SourceandTargetare?agent— always unwrap withif (X := Result.Source?). When a guard is damaged/eliminated by non-agent damage (fall, environment),Sourceisfalse. SpawnAtis<suspends>and can fail. Call it from inside aspawn{}block or another<suspends>context, and wrap it inif (Guard := ...SpawnAt(Pos)):because it fails when the device is at max count.Enable/Disableand despawn interact.Disable()only despawns guards if the device's Despawn Guards When Disabled option is set — otherwise existing guards stay. UseDespawn()explicitly if you want them gone.- Patrol paths are set in the editor, not Verse. Match the spawner's Spawn on Patrol Path Group to the AI Patrol Path Node's Patrol Path Group number so fresh guards start patrolling.