# 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