池化:大量特效的复用管理
机关枪每秒播 10 次特效、团战满屏飘字——如果每次都新建组件再销毁,内存分配与 GC 压力会迅速累积。对象池化是标准解法。
为什么要池化
| 逐次新建 | 池化 |
|---|---|
| 每次分配组件 + 初始化模拟 | 从池里取,几乎零成本 |
| 销毁时要清理模拟数据 | 停止播放保留数据,下次复用 |
| 大量短时特效时帧率抖动 | 帧率平滑 |
Niagara 组件的 Activate / Deactivate 设计本身就考虑了复用,池化只是把"复用"自动化了。
池化实现要点
// 伪代码:简单的特效组件池
struct FVfxPool {
TMap<UNiagaraSystem*, TArray<UNiagaraComponent*>> FreeMap;
UNiagaraComponent* Acquire(UNiagaraSystem* NS) {
auto& Free = FreeMap.FindOrAdd(NS);
if (Free.Num() > 0) {
UNiagaraComponent* Comp = Free.Pop();
Comp->Activate(); // 重置并播放
return Comp;
}
return NewComponent(NS); // 池里没有,新建一个
}
void Release(UNiagaraComponent* Comp) {
Comp->Deactivate();
// 等完全停住再放回空闲队列(OnSystemFinished 委托)
FreeMap[Comp->GetAsset()].Add(Comp);
}
};
池化的三个细节坑
| 坑 | 现象 | 解法 |
|---|---|---|
| 没停透就复用 | 上一次的粒子残影闪现 | On System Finished 委托后再归还 |
| 池子无限增长 | 内存占用持续上升 | 每种 NS 设上限(如 8 个),超出的直接销毁 |
| 参数残留 | 上次的颜色 / 规模还留在复用的组件上 | 取出时先重置或重新设置全部参数 |
池化的适用范围
该池化:高频播发的短时特效(枪口火光、受击、脚步尘、飘字)。 不该池化:长时 / 环境特效(一圈火墙、持续雨),这些本来就常驻,池化无意义。 配合引擎原生能力:较新的引擎版本内置了系统实例池机制,部分场景可以不用手工池——先调研引擎原生能力再决定是否手工造轮子。
