Guard
The canonical F.E.A.R. demo. Three escalating goals, sensor fusion (sight + hearing + damage), goal commit floor preventing pursue thrash.
[ Example ]
Guard — patrol, investigate, pursue
The canonical GOAP demo since F.E.A.R. shipped in 2005. A guard patrols a route; on hearing/seeing something, escalates to Investigate; on confirming a threat, escalates to Pursue. Once in Pursue, the guard commits — momentary loss-of-sight doesn't drop back to Patrol. If your project is combat AI of any kind, this is the closest thing to a production blueprint.
The escalation ladder
Patrol → Investigate → Pursue
(idle) (suspicious) (engaged)
↑ ↑ |
| ↓ ↓
←———————————— cooldowns ——Each tier has higher priority than the one below. De-escalation isn't a hard transition — it's the higher-tier goal's score dropping below the lower-tier's after enough time / distance has elapsed. The goal commit floor (Family 5) makes de-escalation deliberate rather than instantaneous.
Anatomy
Schema — DA_Schema_Guard
LastSeenLocationVector- Triggers Replan
- true
LastHeardLocationVector- Triggers Replan
- true
LastDamageLocationVector- Triggers Replan
- true
TimeSinceContactScalar- Source
- Sensor (per tick increment)
PlayerVisibleBool (latched)- Source
- Sight cone
HeardSomethingBool (latched)- Source
- Audio threshold
WasDamagedBool- Set on
- Take-damage event
Goals — priorities ascending
DA_Goal_PatrolDefault- Priority
- 0.5
- Active
- Always (fallback)
DA_Goal_InvestigateMid-tier- Priority
- 1.0
- Scored by
- HeardSomething OR TimeSinceContact < 15
DA_Goal_PursueEngaged- Priority
- 2.0
- Scored by
- PlayerVisible OR WasDamaged
- Commit time
- 8 seconds
Actions
PatrolToWaypointIntentAction- Does
- Follows a spline of waypoints
MoveToInvestigatePointIntentAction- Target
- Last sensed location
EngagePlayerIntentAction- Does
- Fire weapon, take cover
SearchAreaIntentAction- When
- Brief search if Investigate exhausts
RadioBackupAdvanced- Does
- Coordinate other guards into Pursue
Sensor fusion lives in three sensor components attached to the
archetype: a sight cone, a hearing sphere, and a damage listener. Each
writes its own fact. The latched bools (PlayerVisible, HeardSomething)
are what the goals actually score against.
Why this example is here
Three production lessons:
- Sensor fusion via separate facts beats trying to compute a single "threat level" scalar. Each sensor is independent; the planner combines them via goal scoring.
- Time-since-contact as a derived scalar gives natural de-escalation behavior. After 15 seconds without re-confirmation, the Investigate goal's score drops below Patrol's and the guard transitions out — without hand-authoring "if 15 seconds elapsed, stop investigating."
- 8-second commit floor on Pursue matters. Without it, the guard
would flip back to Investigate every time the player ducks behind
cover (
PlayerVisible→false), even though it has every reason to keep pursuing.
Acceptance test
Player crouchesUnawareOutside the guard's sight cone. Guard patrols along its route.
Player makes noiseInvestigateGuard switches to Investigate, moves toward the heard location.
+15s no contactReturns to PatrolInvestigate de-escalates after timeout.
Player visiblePursue (immediate)Guard switches to Pursue the instant sight lands.
Cover for 4sStays in PursueCommit floor not elapsed — guard keeps moving toward LastSeenLocation.
Cover for 9s+De-escalatesGuard drops to Investigate, then Patrol after another 15s with no further contact.
At no point should the guard rapidly flicker between Pursue and Investigate when the player is intermittently visible.