Map Room Guards: AI Patrol Paths and NPC Routes

In this lesson you'll learn to

  • βœ“Student can lay patrol path nodes, assign an NPC guard, and start/stop/redirect patrols from Verse.

Student can lay patrol path nodes, assign an NPC guard, and start/stop/redirect patrols from Verse.

πŸ“– Reference & full walkthrough: Map Room Guards: AI Patrol Paths and NPC Routes

πŸ” Builds on: verse-npc-guard-patrol (compile-passed companion script, re-zoned here)

🧩 Your capstone piece: Town guards patrolling the plaza and market; guards pause when the hub round launches


Overview

The ai_patrol_path_device is the brain behind a guard's walking route. You place several of these node devices around your map, group them under a shared Patrol Path Group number, and guards spawned by a guard_spawner_device (with a matching Spawn on Patrol Path Group) will walk between them automatically.

Where it really earns its keep is at runtime. From Verse you can:

  • Assign(Patroller) β€” hand a specific guard to this patrol path on the fly (great for redirecting reinforcements).
  • GoToNextPatrolGroup(Patroller) β€” bump a guard onto its Next Patrol Path Group, e.g. switch a guard from a calm loop to an alert sweep.
  • Enable() / Disable() β€” turn a node on or off so guards skip or include it.
  • Subscribe to NodeReachedEvent, NextNodeUnreachableEvent, PatrolPathStartedEvent, and PatrolPathStoppedEvent to drive game logic β€” open a door when a guard reaches the gate node, raise an alarm if a guard gets stuck, etc.

Reach for this device whenever you want living, moving guards that feel like they're protecting something β€” a vault, a checkpoint, a treasure room β€” instead of standing still.

API Reference

ai_patrol_path_device

Used to create patrolling behavior for guards spawned with the guard_spawner_device.

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

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

Events (subscribe a handler to react):

Event Signature Description
NodeReachedEvent NodeReachedEvent<public>:listenable(agent) Signaled when a guard reaches this device.
NextNodeUnreachableEvent NextNodeUnreachableEvent<public>:listenable(agent) Signaled when a guard cannot reach the next ai_patrol_path_device.
PatrolPathStartedEvent PatrolPathStartedEvent<public>:listenable(agent) Signaled when a guard starts moving on the patrol path.
PatrolPathStoppedEvent PatrolPathStoppedEvent<public>:listenable(agent) Signaled when a guard stops moving on the patrol path.

Methods (call these to make the device act):

Method Signature Description
Assign Assign<public>(Patroller:agent):void Assign an AI to this patrol path.
Enable Enable<public>():void Enables this device.
Disable Disable<public>():void Disables this device.
GoToNextPatrolGroup GoToNextPatrolGroup<public>(Patroller:agent):void Commands patroller to follow the Next Patrol Path Group instead of the default Patrol Path Group.

Walkthrough

Scenario: A vault is guarded by a single sentry that loops between patrol nodes. When the guard reaches the gate node (this patrol device), we light up a prop and a HUD message. If the guard ever can't reach the next node (path blocked), we log an alert. And once the round starts, we explicitly assign the spawned guard to the path and command it to switch to its alert route.

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

vault_patrol_manager := class(creative_device):

    # The patrol node we react to (place it at the vault gate).
    @editable
    GatePatrolNode : ai_patrol_path_device = ai_patrol_path_device{}

    # The spawner that produces our guard.
    @editable
    GuardSpawner : guard_spawner_device = guard_spawner_device{}

    # A prop or device we light up when the guard arrives.
    @editable
    ArrivalLight : prop_mover_device = prop_mover_device{}

    # Localized message helper (message params need a localized value, not a raw string).
    StatusText<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        # React when the guard reaches THIS node.
        GatePatrolNode.NodeReachedEvent.Subscribe(OnNodeReached)
        # React when the next node can't be reached (blocked path).
        GatePatrolNode.NextNodeUnreachableEvent.Subscribe(OnNextUnreachable)
        # React when a guard starts / stops moving on the path.
        GatePatrolNode.PatrolPathStartedEvent.Subscribe(OnPatrolStarted)
        GatePatrolNode.PatrolPathStoppedEvent.Subscribe(OnPatrolStopped)

        # When the spawner produces a guard, wire it to our path.
        GuardSpawner.SpawnedEvent.Subscribe(OnGuardSpawned)

        # Make sure the node is active.
        GatePatrolNode.Enable()

    # Spawner hands us the new guard agent.
    OnGuardSpawned(Guard : agent) : void =
        # Assign this fresh guard to our patrol path.
        GatePatrolNode.Assign(Guard)
        # Immediately send it onto its ALERT route (the Next Patrol Path Group).
        GatePatrolNode.GoToNextPatrolGroup(Guard)

    OnNodeReached(Agent : agent) : void =
        # The guard hit the gate node β€” trigger the arrival light.
        ArrivalLight.Begin()

    OnNextUnreachable(Agent : agent) : void =
        # Path blocked: turn the node off so guards re-route past it.
        GatePatrolNode.Disable()

    OnPatrolStarted(Agent : agent) : void =
        ArrivalLight.End()

    OnPatrolStopped(Agent : agent) : void =
        # Guard stopped on the path; nothing dramatic, but we could escalate here.
        ArrivalLight.End()

Line by line:

  • GatePatrolNode, GuardSpawner, ArrivalLight are @editable fields β€” you must declare a placed device as a field of a creative_device class before you can call its methods. A bare Device.Method() fails with 'Unknown identifier'.
  • In OnBegin, every Subscribe(...) wires a class method as the handler. Patrol events are listenable(agent), so each handler receives a single Agent : agent.
  • OnGuardSpawned is called by the spawner's SpawnedEvent. We Assign the new guard to the node, then GoToNextPatrolGroup to push it onto the alternate (alert) route.
  • OnNodeReached fires the moment the guard reaches the gate node β€” we play the arrival light with Begin().
  • OnNextUnreachable handles a blocked path by Disable()-ing the node so the AI re-routes.
  • Enable() at the end of OnBegin guarantees the node is live when the round starts.

Common patterns

1. Redirect a specific player-hired guard onto a chase route with GoToNextPatrolGroup. When a player trips a trigger, switch the patrol to its alert group.

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

alert_switcher := class(creative_device):

    @editable
    PatrolNode : ai_patrol_path_device = ai_patrol_path_device{}

    @editable
    GuardSpawner : guard_spawner_device = guard_spawner_device{}

    @editable
    AlarmTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        AlarmTrigger.TriggeredEvent.Subscribe(OnAlarm)

    OnAlarm(MaybeAgent : ?agent) : void =
        # The trigger hands us an optional agent; we don't need it here.
        # Push every spawned guard onto the alert route.
        for (Guard : GuardSpawner.GetAgents()):
            PatrolNode.GoToNextPatrolGroup(Guard)

2. Toggle a checkpoint node on and off with Enable / Disable. A button opens or seals a patrol leg.

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

checkpoint_toggle := class(creative_device):

    @editable
    CheckpointNode : ai_patrol_path_device = ai_patrol_path_device{}

    @editable
    OpenButton : button_device = button_device{}

    @editable
    CloseButton : button_device = button_device{}

    OnBegin<override>()<suspends> : void =
        OpenButton.InteractedWithEvent.Subscribe(OnOpen)
        CloseButton.InteractedWithEvent.Subscribe(OnClose)

    OnOpen(Agent : agent) : void =
        CheckpointNode.Enable()

    OnClose(Agent : agent) : void =
        CheckpointNode.Disable()

3. Count nodes reached to escalate an alarm using NodeReachedEvent. After the guard completes its loop a set number of times, fire an alarm device.

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

patrol_counter := class(creative_device):

    @editable
    LoopNode : ai_patrol_path_device = ai_patrol_path_device{}

    @editable
    AlarmHud : hud_message_device = hud_message_device{}

    var ReachedCount : int = 0

    OnBegin<override>()<suspends> : void =
        LoopNode.NodeReachedEvent.Subscribe(OnReached)

    OnReached(Agent : agent) : void =
        set ReachedCount = ReachedCount + 1
        if (ReachedCount >= 3):
            AlarmHud.Show()
            set ReachedCount = 0

Gotchas

  • You can't call a device without a field. ai_patrol_path_device.Assign(...) straight up won't compile β€” declare an @editable field and call the method on that instance.
  • Patrol events deliver a non-optional agent. NodeReachedEvent and friends are listenable(agent), so your handler signature is (Agent : agent) β€” no ? to unwrap. Contrast with a trigger_device.TriggeredEvent, which hands you (MaybeAgent : ?agent) that you DO need to unwrap with if (A := MaybeAgent?):.
  • Group numbers must match. GoToNextPatrolGroup does nothing useful unless the device's Next Patrol Path Group and the destination nodes share that group number. Set these in the device's details panel β€” Verse only triggers the switch.
  • Assign needs a spawned AI agent. Pass an agent that actually came from the guard_spawner_device (use SpawnedEvent or GetAgents()); assigning a player agent won't make a guard patrol.
  • message params need a localized value. If you pass a guard's name or status into a HUD device, build it with a <localizes> helper like StatusText<localizes>(S:string):message = "{S}" β€” there is no StringToMessage.
  • Disable doesn't delete the guard. Disable() only takes the node out of the route; the guard keeps moving along the remaining active nodes. Re-Enable() to fold it back in.
πŸ”’

Keep going β€” free

You've read the intro. The rest of this lesson is free for members. Sign in to continue and track your progress.

Sign in free to continue this lesson
Pass the quiz above to chart this quest in your Journal.

Sources

/guides/ai-patrol-path-device
Source

Biloxi Studios original lesson

Guided course

Add this lesson to your free study plan.

🧭 The Keeper’s log

Quest complete? Chart your next heading from the 🧠 Center Village (Free) expedition.

β›΅ Take me there