
GOAP 性能瓶颈与缓存规划避免每帧重复求解动作图目标导向型行动计划GOAP赋予了游戏 AI 极高的自适应性与战术涌现能力。然而在大型开放世界或百人同屏战场中GOAP 常常成为主线程 CPU 的头号杀手。如果每个 NPC 在每一帧或每个 AI Tick 周期都发起一次基于 A* 的动作图逆向搜索当动作原子数量达到 30、世界状态变量达到 20 时搜索空间的分支因子将导致单次求解耗时飙升至数毫秒。解决 GOAP 性能瓶颈的核心思路在于掐断无效的重复搜索。通过构建计划缓存有效性验证机制、分帧时间切片求解器Time-sliced Solver以及拓扑不可达剪枝能够将单帧 GOAP 规划开销压制在 0.1ms 以下。[NPC 触发决策请求] │ ▼ [检查当前计划是否依然有效 (Preconditions Target Valid?)] ├── 是 ── [继续执行当前 Plan, 耗时 0.001ms] └── 否 ── [状态是否命中全局拓扑缓存 (Plan Cache Hit?)] ├── 是 ── [直接克隆缓存动作序列, 耗时 0.005ms] └── 否 ── [推入分帧时间切片求解队列 (Time-sliced A*)]计划失效检测与脏状态触发机制绝大多数 NPC 在执行一个复合计划例如“移动到掩体 - 装填弹药 - 探头射击”时世界状态在几秒钟内并没有发生颠覆性改变。传统的错误做法是在Update中无脑重新调用Planner.Plan()。正确的工程做法是维护动作契约Action Contract仅当遇到以下三种情况时才标记为“脏Dirty”并触发重规划当前动作执行失败如寻路被动态障碍阻挡、目标死亡。前置条件被外部事件破坏如原本藏身的掩体被手榴弹炸毁。出现了更高优先级的全局打断目标如遭遇偷袭进入濒死自救状态。using System; using System.Collections.Generic; using Unity.Mathematics; using UnityEngine; public class CachedGOAPAgent : MonoBehaviour { private QueueGOAPAction _currentPlan new QueueGOAPAction(); private GOAPAction _activeAction; private SquadWorldState _lastWorldState; private bool _isPlanDirty true; // 动作执行监控 public void TickAgent(float deltaTime) { // 1. 如果当前没有有效计划或状态标记为脏请求重规划 if (_isPlanDirty || (_activeAction null _currentPlan.Count 0)) { GlobalGOAPQueueManager.Instance.EnqueuePlanningRequest(this); return; } // 2. 推进当前动作 if (_activeAction ! null) { // 验证当前动作的前置条件是否依然成立 if (!_activeAction.IsPreconditionMet(GetCurrentWorldState())) { // 环境突变中断当前动作触发重规划 _isPlanDirty true; _activeAction.OnAbort(); _activeAction null; return; } bool completed _activeAction.OnTick(deltaTime); if (completed) { _activeAction.OnCompleted(); _activeAction _currentPlan.Count 0 ? _currentPlan.Dequeue() : null; if (_activeAction ! null) _activeAction.OnExecute(); } } } public void OnPlanSolved(QueueGOAPAction newPlan) { _currentPlan newPlan; _activeAction _currentPlan.Count 0 ? _currentPlan.Dequeue() : null; if (_activeAction ! null) _activeAction.OnExecute(); _isPlanDirty false; } public SquadWorldState GetCurrentWorldState() _lastWorldState; }全局状态拓扑缓存Plan Cache在同类兵种如巡逻卫兵、普通步枪兵中大量 NPC 面临的世界状态子集高度相似例如“发现敌人 - 自身无掩体 - 手中有武器”。我们可以将(StartState, GoalState)的紧凑哈希值作为键构建全局 LRU 动作计划缓存池。只要世界状态的关键特征未变后续 NPC 可以直接复用该路径模板避免再次执行图搜索public class GOAPPlanCache { private readonly Dictionaryulong, ListType _cachedActionPipelines new Dictionaryulong, ListType(); private const int MaxCacheSize 256; // 计算状态对的紧凑 64 位哈希键 public ulong ComputeKey(uint startFlags, uint goalFlags, int roleId) { return ((ulong)roleId 48) | ((ulong)startFlags 24) | (ulong)goalFlags; } public bool TryGetPlan(ulong key, out ListType actionTypes) { return _cachedActionPipelines.TryGetValue(key, out actionTypes); } public void StorePlan(ulong key, QueueGOAPAction plan) { if (_cachedActionPipelines.Count MaxCacheSize) { _cachedActionPipelines.Clear(); // 简易清空或采用 LRU 淘汰 } var list new ListType(); foreach (var action in plan) { list.Add(action.GetType()); } _cachedActionPipelines[key] list; } }时间切片求解器Time-Sliced Planner为了杜绝突发性大规模 AI 重新规划引发的主线程掉帧必须将搜索过程从单帧同步阻塞改为分帧时间切片执行。求解器在每帧设定严格的 CPU 预算例如 $0.5\text{ms}$利用高精度计时器在超时瞬间暂停搜索并保存 A* 的 OpenList / ClosedSet 上下文下一帧无缝继续推演public class GlobalGOAPQueueManager : MonoBehaviour { public static GlobalGOAPQueueManager Instance; private readonly QueueCachedGOAPAgent _requestQueue new QueueCachedGOAPAgent(); private readonly System.Diagnostics.Stopwatch _stopwatch new System.Diagnostics.Stopwatch(); private const double FrameBudgetMs 0.45; // 每帧最多允许执行 0.45 毫秒的 AI 搜索 public void EnqueuePlanningRequest(CachedGOAPAgent agent) { if (!_requestQueue.Contains(agent)) { _requestQueue.Enqueue(agent); } } void Update() { _stopwatch.Restart(); while (_requestQueue.Count 0) { // 严格检查时间切片预算 if (_stopwatch.Elapsed.TotalMilliseconds FrameBudgetMs) { break; // 剩余请求留待下一帧处理 } var agent _requestQueue.Dequeue(); if (agent null) continue; // 执行轻量规划或从缓存提取 QueueGOAPAction solvedPlan SolvePlanForAgent(agent); if (solvedPlan ! null solvedPlan.Count 0) { agent.OnPlanSolved(solvedPlan); } } } private QueueGOAPAction SolvePlanForAgent(CachedGOAPAgent agent) { // 核心 A* 求解逻辑... return new QueueGOAPAction(); } }通过引入“契约式失效检测”、“跨个体路径模板缓存”与“时间切片预算控制”三大体系即便场景中同时存在数百名高智商作战 NPCGOAP 系统的 CPU 占用也能被死死锚定在平稳的水平线上彻底消除了 AI 决策带来的掉帧尖峰。