行为调度:帧预算必须同时约束队列年龄与原子工作

自动关联目录:行为调度:帧预算必须同时约束队列年龄与原子工作

核心判断:集中调度、错峰和时间片能压平 DS 平均成本,但只限制“调度器循环用了多久”仍不够;一个不可抢占的行为步就能击穿帧预算,持续延期的低优先级任务也会变成玩法错误。

一、实际方案已经跨过“每个 NPC 自己 Tick”阶段

权威运行时使用 World 级调度器:按稳定哈希错开首次执行,相同运行时同帧只进入一次求值;任务带 generation,旧任务自动失效;到期请求可合并,并按稳定顺序执行。寻路类工作还有更小时间片,单个行为故障会注销自身并停止运动,而不是拖垮整个队列。

这些设计同时解决了 UE 大量 Actor/Component Tick 的分发成本、同步尖峰和生命周期悬空,且已有预算、延期、队列和故障指标,是应保留的工程基础。

二、剩余的两个预算盲区

第一,调度器只能在一次 Step 返回后检查时间。如果一个节点内部做全量搜索或同步原生调用,它仍可独占一帧。第二,生命周期任务为保证创建/销毁及时而绕开普通周期预算;高峰期它们仍可能形成无上限尖峰。

只看本帧执行时间还会掩盖饥饿:任务每帧都被推迟,帧时间漂亮,但 NPC 反应已经超出玩法容忍度。

三、目标调度:同时管理成本、截止期和降级

struct ScheduledWork {
    RuntimeId owner;
    Generation generation;
    TimePoint due;
    Duration estimatedCost;
    Duration maxAtomicCost;
    Duration maxAge;
    Lane lane; // lifecycle, combat, navigation, ambient
};

while (frameBudget.CanAdmit(work.estimatedCost)) {
    work = queues.SelectByDeadlineAndLaneReserve();
    StepResult r = work.RunOneYieldPoint();
    frameBudget.Charge(r.actualCost);
    if (!r.done) queues.Requeue(r.continuation);
}
ApplyOverloadPolicy(queues); // 降频、近似、暂停环境行为,不破坏战斗不变量

所有可扩展节点必须在受控 yield point 返回;无法切片的原生调用需要独立 maxAtomicCost 监控和准入上限。生命周期通道有保底份额也有硬上限,避免“高优先级”变成“无预算”。

四、UE 与玩法层面的降级边界

DS 不需要 AnimGraph 和渲染 Tick,但权威感知、攻击判定和状态转换不能因表现 LOD 被省略。环境巡逻可降频,远距离搜索可复用空间查询,战斗提交与销毁清理必须守住截止期。降级策略应由行为类别声明,而不是由调度器猜测。

五、验证与反思

压测同时记录 P95/P99 单步成本、最大原子工作、队列年龄、错过截止期数、每通道预算和降级次数。构造大量同帧创建、销毁、寻路完成和攻击到期,证明生命周期通道不会吞掉整帧,低优先级任务也不会无限饥饿。

经验是:帧预算回答“本帧最多做多少”,截止期回答“最迟何时必须做”,两者缺一不可。