# 5. Worked Example: Guard Graph
Part of [[Unity Behavior]]. Prev: [[4. C Sharp Bindings and Event Channels]]. Next: [[6. Behavior Pitfalls]].
Reuse a capsule (or the enemy from [[Enemy AI State Machine]]) and, if you have it, sensing from [[Organic Enemy Sensing]]. The graph is the **policy**. C# writes **facts**. This was **not compiled** and **not play-tested**.
## 5.1 Blackboard (names you will type exactly)
| Name | Role |
|---|---|
| `CanSeePlayer` | bool, written by C# |
| `Target` | Transform or GameObject, written by C# when seen |
| `Speed` | float, designer-facing override |
| `PatrolAnchor` | Transform, per-instance after `Init` |
If your 1.0.17 blackboard uses a slightly different type for "a point to stand near," use that type and keep the name.
## 5.2 Graph shape (plain language)
Top to bottom, something you can read aloud:
1. If `CanSeePlayer`: chase / face `Target` (use a **pre-built** movement or look node from the 1.0.17 library if one fits; otherwise a **wizard-generated** custom Action that reads `Target` and moves with your existing NavMesh code).
2. Else: return to `PatrolAnchor` and wait (again, library node or a small custom Action).
Do **not** also run the old FSM mover on the same `NavMeshAgent`.
## 5.3 Sensing writes the blackboard
```csharp
using Unity.Behavior;
using UnityEngine;
/// <summary>
/// Pushes sensing facts into a Behavior graph. NOT compiled in Unity.
/// Hook this to your existing sense check ([[Organic Enemy Sensing]]), not a second cone.
/// </summary>
public sealed class GuardSensingToBlackboard : MonoBehaviour
{
[SerializeField] BehaviorGraphAgent agent;
[SerializeField] Transform target;
[SerializeField] float loseSightSeconds = 2f;
float _lostTimer;
void Update()
{
bool canSee = ComputeCanSee(); // your existing function
if (canSee)
{
_lostTimer = 0f;
agent.SetVariableValue("CanSeePlayer", true);
agent.SetVariableValue("Target", target);
}
else
{
_lostTimer += Time.deltaTime;
if (_lostTimer >= loseSightSeconds)
agent.SetVariableValue("CanSeePlayer", false);
}
}
bool ComputeCanSee()
{
// Replace with the existing sense result.
return false;
}
}
```
Call `SetVariableValue` **after** the agent has initialized if you want per-instance values (part 2). If this component runs in `Awake` before the agent, you may be writing overrides — fine for defaults, surprising for a Target that should be unique.
## 5.4 Optional: alert other systems
If a manager must hear "this guard is chasing," use the official event-channel pattern from part 4 (`SendEventMessage` on an asset channel). Do not poll every guard's blackboard from `Update` across the scene if a channel will do.
## 5.5 Playtest checklist
1. Play mode: the official visualization shows the chase branch when `CanSeePlayer` is true, and the patrol branch when it stays false.
2. Two prefab instances: different `PatrolAnchor` values after `Init()`, same graph asset.
3. Disabling `BehaviorGraphAgent` pauses the policy; enabling it resumes (official).
4. The old FSM is **not** also moving the character.
5. A typo in `"CanSeePlayer"` fails the hook — fix the string, do not add a second variable "just in case."
## 5.6 Exercise
Ship the graph on two guards with different `Speed` overrides. Record a Play-mode screenshot of the running node. If you write a custom Action, it must come from the official wizard (part 3), not a hand-rolled attribute.
## Sources
- [[Enemy AI State Machine]]
- https://docs.unity3d.com/Packages/
[email protected]/manual/bind-c.html
- https://docs.unity3d.com/Packages/
[email protected]/manual/behavior-agent.html