池化:大量特效的复用管理

机关枪每秒播 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 个),超出的直接销毁
参数残留 上次的颜色 / 规模还留在复用的组件上 取出时先重置或重新设置全部参数

池化的适用范围

该池化:高频播发的短时特效(枪口火光、受击、脚步尘、飘字)。 不该池化:长时 / 环境特效(一圈火墙、持续雨),这些本来就常驻,池化无意义。 配合引擎原生能力:较新的引擎版本内置了系统实例池机制,部分场景可以不用手工池——先调研引擎原生能力再决定是否手工造轮子。