Dungeon Heist
Throw a coin and watch one guard drop its patrol mid-step, commit to the noise, walk over, and look around. The interruption sample, with the heist rules left in C#.
On this page
A dark Kenney dungeon, three relics on pedestals, five guards on routes, and a pocket full of coins. One of those guards, Guard_2, decides everything it does from a graph file you can open. Throw a coin behind it while it is walking and you get the moment this whole sample exists for: it abandons the walk on the same update the noise lands.

Play it in one minute
Under a minute
- Scene
Assets/_Project/Scenes/DungeonHeist.unity- Select
- Guards > Guard_2 in the Hierarchy
- Controls
- Keyboard and mouse: WASD or the arrow keys to move, hold Left Ctrl to sneak, hold Left Shift to sprint, Q to throw a coin, click in the Game view to capture the mouse for camera look and Esc to release it. Gamepad: left stick to move, right stick to look, east button to sneak, south button to sprint, north button or right shoulder to throw a coin. Relics are taken by walking into them; there is no take key.
- Goal
- Steal all three relics without being caught. Walk within about a metre and a half of a relic and it is yours.
The first thirty seconds
Section titled “The first thirty seconds”Select Guard_2 in the Hierarchy before you press Play and the editor attaches to it as the scene starts. It walks Route_NorthEast, a four point loop, standing still for four seconds at each point. The HUD reads relics 0 of 3 and alarm calm.
Now spend a coin. Q throws one on a low arc up to eight metres ahead of you, and it makes its noise where it lands, not where you stood. Any guard within the landing radius of seven metres hears it, and if that guard is Guard_2 you can watch the decision happen in the editor while it happens in the game.
Getting caught needs a guard in an active chase that closes to about a metre. The heist then resets around you: you reappear at the entrance, the relics you already stole stay stolen, every guard goes back to patrol on its own route, the alarm drops to calm, and the gates open again. Take the third relic and you win on the spot.


Paths here follow the standalone project layout. Importing the packaged sample puts the same folders under Assets/BitQuirky/BehaviorTreesAndStateMachines/Samples/.
What this game teaches
Section titled “What this game teaches”- Priority through one Selector with eight branches, walked top to bottom on every pass, so “stop being caught” beats “chase” beats “go look at that noise” beats “carry on patrolling”.
- Lower Priority interruption on the conditions that watch for a threat, which is what lets a coin or a clear sighting take the agent away from a walk that is already underway. The two Self Conditionals do the opposite job: they drop their own branch once its reason stops holding.
- Signal capture with an
outparameter. A member condition callsTryGetPendingSignal(out Stimulus signal)and captures the out value into a graph variable, so the guard commits to one coin and finishes the trip instead of chasing whatever is newest. - Coroutine actions: six of this guard’s leaves are
IEnumeratormethods that hold Running while they walk, dwell, or look, and that get cancelled cleanly when a higher branch wins. - A two-way member binding you can edit while the game runs.
patrolSpeedreads and writes aFloatVariable, and this guard owns a private clone of it. - A focus target held as a managed reference, so one movement node steers at the player during a chase and at the landed coin during an investigation, without knowing which is which.
What the graph deliberately leaves in C#: vision cones and line of sight, the hearing rule, stimulus ranking, suspicion state, the search timer, NavMesh movement, the route data, alarm escalation, the gate lockdown, the watcher statues, relic pickup, the win condition, the catch reset arithmetic, the player controller, the coin’s flight. Note what that list implies about the graph file: it holds no distances and no durations. Every number the guard uses belongs to the game.
Graph versus game code
Section titled “Graph versus game code”| The graph decides | Your game code does |
|---|---|
Guard2.bqbehavior on Guard_2, composed and enabled by GuardBehaviorActor |
Guards is the authority for suspicion state, stimulus ranking, the search clock, and whether a route return is owed. The graph asks it; it answers. |
| Which of the eight branches owns this update, and when to abandon the one that was running | GuardVisionSensor with LineOfSightRaycaster decides sight from an 8 metre cone at 55 degrees either side, and Hearing decides audibility from the noise radius times this guard’s hearing multiplier. |
| When to walk, when to hold still and face something, when to sweep the area | GuardBehaviorMovementService and NavMeshGuardMotor move the body. GuardBehaviorWaitService owns the 0.8 second dwell, the 2 second look, and the waypoint pause. |
Which noise to commit to, stored in the pending_signal variable |
GuardBehaviorSignalBinder latches every noise this guard can hear and hands over the newest one. StimulusArbiter decides whether it outranks what the guard is already reacting to. |
| Nothing at all | Guard_1, Guard_3, Guard_4, and Guard_5 are Guard.prefab instances running the hand-written GuardAgent, kept as the comparison. The alarm director, bell, gates, statues, relics, HUD, entrance, player, and coin thrower are all ordinary components. |
Guard2.bqbehavior is the scene’s only graph file, and Guard_2 carries the scene’s only Behavior Agent. That agent is saved switched off: the actor composes it, hands it three reference slots (guards, gameMaster, this guard’s own patrol-speed clone), initializes it, then enables the tick. Selecting the object in Play Mode is therefore unambiguous, because there is exactly one agent to attach to.
These are not the game
Section titled “These are not the game”MemberConformance.bqbehavior sits in the same folder and the scene never touches it. It is a five node Sequence that exercises one of each member form once: a method whose returned bool lands in the blackboard, a message with no result, a member condition comparing an enum property against Lockdown, a member event, and a variable read that carries its own change notification. It runs on MemberConformance.prefab under an EditMode proof, so read it as a worked example of the binding forms and expect no gameplay from it.
Follow one decision
Section titled “Follow one decision”Here is the whole coin journey, from your keypress to a guard standing over a coin looking around.
-
Open
Assets/_Project/Behavior/Guard2.bqbehaviorin Window > Behavior & State > Behavior Tree Editor and leave the toolbar on Static. The root is a Repeat on Forever over the Selector called priority selector, and its Children list is the guard’s whole policy in order: shared catch reset, catch, chase, capture stimulus, the grouped suspicion branch, search, return to route, patrol. -
Select newest eligible signal pending in the capture stimulus branch. The rail shows a method binding on the guard’s own signal binder,
TryGetPendingSignal, with itsoutparameter captured as a separated output into thepending_signalgraph variable, and a comparison that turns the returnedboolinto the node’s result.

Stimulus struct, report Success so the branch can continue.-
Read the two nodes after it. store one stimulus payload atomically hands
guard_idand the capturedpending_signaltoGuards.TryCaptureStimulus, which is where ranking happens: a coin cannot displace a confirmed sighting of you. consume the captured signal then clears the latch so the same noise is never captured twice. -
Press Play, select Guard_2, and switch the toolbar to Live. While it patrols, the running leaf is move to the current waypoint or wait out the waypoint pause or its remainder, and the branches above sit idle.
-
Throw a coin within seven metres of the guard. On the update the noise lands, newest eligible signal pending becomes true, the Lower Priority interruption takes the patrol coroutine away, and the suspicion branch takes the tick.
-
Watch the investigation run in order: enter suspicious, then hold and face the active stimulus for 0.8 seconds, then a validity check, enter investigate, move to the stimulus, look around for 2 seconds, and clear the completed investigation atomically. Its sibling branch, expire stale investigation, is the one that wins instead when the noise goes stale during the dwell.

Guard_2 while the heist plays. The canvas colors say which branch owns the update: the suspicion and investigation sequence holds the tick, and the dwell at its head is the node actually running.The last link in the chain is the focus target. The actor projects whatever object the Guards asset’s decision named into the liveFocus variable: you during a chase, the landed coin during an investigation. One member node reads Position off whatever is in there and hands it to the movement service, so pursue the live focus position steers at the right thing without a branch per target type.
Change something and watch
Section titled “Change something and watch”

Distances and durations are game data, which is why none of them is a graph edit. Hearing lives on Guard_2 as the Hearing Multiplier in the Perception block of its Guard Behavior Input Binder, multiplied against the coin’s Landing Noise Radius on GameLogic/Player/Coins.asset and scaled again by the active alarm profile. The dwell, the look, and the stimulus expiry sit together in the Timing block of GameLogic/Guard/Guards.asset. Raise Investigate Look Around Seconds from 2 to 6 and the look around node holds Running for six seconds in Live without a single change to the graph file.
Use this in your own game
Section titled “Use this in your own game”Copy as-is: the shape. One repeating priority Selector; safety branches plus recovery branches above ambient work; a capture branch that stores a changing fact once; thin coroutine leaves that each do one atomic thing. GuardBehaviorActor is worth reading before you write your own host: it composes the agent, injects every collaborator explicitly, fills the reference slots, initializes the agent, then enables it. Nothing in it decides behavior.
Rebind: the member targets and the reference slots. Your authority is not called Guards, so point the capture node at your own Try method, point the state calls at your own methods, and give the graph your own struct as the captured payload. The interruption opt-in travels with the shape: put the branch that should win above the branch that should lose, and set Abort to Lower priority on the Selector that owns both. That one setting covers every condition inside that Selector’s branches. This guard’s file predates the control and still marks each watching condition individually, which the runtime honors either way, so read the per-condition rows as history and not as a pattern to copy.
Needs new C#: a motor that can be told to stop. Every leaf here is interruptible because the movement and wait services handle cancellation, so a preempted walk really stops. If your character moves by tween, by grid step, or by animation event, write that cancellation before you wire the graph, or your guard will keep gliding to where the coin used to be. A second graph-driven guard also needs its own route, its own speed clone, and the same three slots filled; sharing one clone between two guards hands them one speed.
When it goes wrong
Section titled “When it goes wrong”When it goes wrong
| Symptom | Check | Fix |
|---|---|---|
| A different guard investigates your coin. | Which object is selected, and where did the coin land? Four of the five guards run hand-written C#. | Keep Guard_2 selected and land the coin inside its hearing range. Editing the graph file changes that one guard. |
| The editor shows Static when you expected Live. | Guard_2 saves its Behavior Agent disabled and the actor enables it during composition. | Attach in Play Mode after the scene has initialized. Before Play there is no running instance to attach to. |
| You edited a graph file and the scene ignored it. | Confirm you opened Guard2.bqbehavior and not MemberConformance.bqbehavior. | MemberConformance is a binding fixture with an EditMode proof behind it. The scene points at Guard2 only. |
| The capture condition never succeeds. | Read whether a noise actually reached this guard, then whether Guards rejected it as lower ranked than the current stimulus. | Land the coin closer, or break the sighting that outranks it. Sneaking emits no noise at all, so a sneaking player is silent by design. |
| A member node reports its target as unresolved. | Look for the generated member provider component beside the agent on Guard_2, then for the three filled reference slots (guards, gameMaster, patrolVariable). | Reimport the graph file so the provider regenerates, and confirm the actor filled all three slots. A provider baked for another graph binds nothing. |
| liveFocus is empty. | Which branch is running? Patrol has no focus target, and neither does search or return. | Read an empty focus as normal outside chase and investigate. It matters only while a focus-consuming node should be running. |
| A coroutine leaf stays Running after a higher branch wins. | Inspect the running instance in Live rather than the saved graph, and read whether the service handles its cancellation. | Cancel the underlying work in your own service. The graph drops the branch; only your code can stop the motor. |
| Pressing E does nothing at a relic. | The Interact action exists in the input actions asset and no game code listens for it. | Walk into the relic instead. Pickup is proximity based at a metre and a half, and the glow brightens as you close in. |
What next
Section titled “What next”- Interrupt: the exact rules behind the takeover you just watched, including what gets reset.
- Guard and investigate: this capture-then-act pattern on its own, ready to lift.
- Horde Survival: the same ideas at two thousand agents, on Entities.
- Waiting and cancellation: how a coroutine leaf holds Running and what cancelling one means for your code.
- Live editing: which values a running agent will let you change, and which it will not.
Credits and reuse
Section titled “Credits and reuse”Every model and texture in the vault is by Kenney, who gives away thousands of game assets for free:
- Modular Dungeon Kit: the rooms and corridors, the gates, and the shared colormap.
- Mini Dungeon: the chests you loot, the coin you throw, plus the barrels and columns you hide behind.
- Graveyard Kit: the keeper figure that stands in as the watcher statue.
- Animated Characters: the thief and the guards, the
characterMediumrig with its fantasy skins. That rig ships as the Animated Characters Bundle inside All-in-1; the series page lists the free packs it was split into.
We took ours from Kenney Game Assets All-in-1, the paid bundle of everything Kenney has released; the links above go to the same packs on kenney.nl, where each one is a free download.
All four are CC0, which asks nothing of you and allows commercial use; we always credit Kenney when we use their assets, and support them by buying the packs. We hope you will too! Each kit sits under Assets/ThirdParty/Kenney/ with its own License.txt. Keep those files beside the art if you move it into your own project. The asset’s code and graph files carry their own license, so check both before shipping a derivative.