集中调度:去掉对象 Tick 以后仍可能每帧全扫描
自动关联目录:集中调度:去掉对象 Tick 以后仍可能每帧全扫描
核心判断:把上万个
ActorTick合并成一个 Subsystem Tick,能减少 UE Tick 注册、虚调用和散乱生命周期成本;但如果 Subsystem 每帧仍遍历全部对象,算法复杂度还是,规模上限只是被推迟。
一、业务冲突:组件化易扩展,空 Tick 也会按实例付费
早期功能按 Actor/Component 自治很合理:拾取、动态线、粒子参数、绳索、样条、物理同步和角色状态都能在自己的 Tick 中更新,代码局部、开关直接、符合 UE 的对象模型。创作内容规模上来后,即使大多数实例本帧无变化,也要承担 TickFunction 注册、调度、前置依赖、虚调用和缓存跳转。
后续实践把空间流式、资源完成和脚本更新集中到 Subsystem/Manager,这是正确演进。但流式调度器的主路径仍按“常驻处理、距离扫描、原子组修正、差分应用、完成检测”遍历候选;焦点位置还必须每帧读取,因为角色移动不会经过通用属性脏标记。集中调度减少了框架开销,却没有自动减少业务工作集。
二、实际实现:从分散 Tick 到可观察的单入口
集中入口带来了对象 Tick 很难提供的能力:统一阶段顺序、一次读取观察点、复用距离结果、统一迟滞、统一 Spawn/Despawn 差分、按帧记录候选数和执行耗时。编辑模式可以直接休眠,DS 也能关闭纯客户端流式分支。
但每帧全表扫描的成本近似:
其中 是全部候选 Part, 是组合组数, 才是本帧真正改变表示的对象数。稳定场景里 很小,主要成本仍由 决定。
三、值得保留:集中化先解决正确性与诊断
- 阶段顺序明确,常驻、距离和原子组规则不会由对象 Tick 顺序偶然决定。
- 距离结果被原子组和 LOD 复用,避免同帧重复计算。
- 观察点、策略与拓扑有单一快照,便于复现某一帧的决策。
- 增量拓扑维护、Dirty Root 和全量回退都有归因指标。
- 对象进出经统一差分提交,可以集中处理异步 Actor 晚到和角色保护。
这些收益是真实的,不能因为仍有全扫描就退回分散 Tick。
四、结构性缺陷:Dirty Cache 不等于 Dirty Evaluation
当前增量机制主要减少“拓扑重建”,但玩家移动会改变所有对象相对观察点的距离,于是空间决策仍需重算。仅给对象加 Dirty Flag 没用:观察点动了,理论上所有对象都 Dirty。
简单降频也会破坏边界正确性。高速移动、传送、对象突然进入交互范围时,低频扫描可能让碰撞或可见性晚到。把扫描平均分到多帧则会让原子组合的一部分先更新,除非以组为提交单位。
五、目标设计:空间索引提供候选,事件提供强制唤醒
把对象按世界包围盒写入网格、BVH 或分区索引。观察点移动时只查询进入/离开两个边界壳层;拓扑变更只更新受影响叶子。常驻、脚本 Pin、传送目标和正在交互对象通过事件直接加入强制工作集。最终调度的是变化集与邻界集,而不是全体对象。
MovedRegion swept = Sweep(previousFocus, currentFocus, targetRadius);
CandidateSet work = spatialIndex.QueryBoundary(swept);
work.Union(dirtyObjects);
work.Union(pinnedObjects);
work.Union(activeGameplayObjects);
for (AtomicGroup group : CollapseToCommitUnits(work)) {
EvaluateAndCommit(group);
}全量扫描仍应保留为校验和恢复路径:低频对账索引与真实状态,发现漂移时记录原因并重建,而不是让全量路径继续充当正常帧逻辑。
六、验证:成本必须随局部变化量增长
测试至少包含静止观察点、高速穿越、传送、多观察点、原子组跨边界、拓扑批量修改和索引失效回退。指标看 Evaluated / Total、空间查询耗时、差分对象数、强制工作集、全量回退率和决策延迟。
目标复杂度不是口号式,而是正常帧趋近:
最终决策规则:集中 Tick 是建立调度权,空间索引和事件工作集才真正减少计算量。