深谈UE4粒子系统对象池的惊人大坑

1 篇文章 0 订阅
1 篇文章 0 订阅

抓手

标题起得很唬人,但一点不唬人。

作者在这里告诫大家,不要使用Cascade的对象池,会有bug,并且不起作用!

建议大家都转成使用Niagara,Niagara的对象池功能齐全,并且没什么bug。

后面会具体说明原因。

同时本文会告诉大家大家具体的使用方法,以及什么情况下需要使用对象池。

本文将基于UE4.26.2的源码进行分析研究。

UE4粒子对象池的大坑

Cascade和Niagara的对象池代码原理几乎一致,但是感觉是两个人写的。(而且是负责Niagara的同学复制Cascade的对象池代码来改改)

所以Niagara有一定的优化,Cascade有坑。

具体的坑,有五个

1、Cascade的对象池没有初始化多少个实例的功能,虽然Cascade的UI上看起来可以设置,但却没有实际功能!而Niagara是有该功能的。

原因是ParticleSystem类和NiagaraSystem类都继承了父类UFXSystemAsset,都有   

uint32 PoolPrimeSize = 0; 这个变量。但ParticleSystem却没有实现相应的功能。反而NiagaraSystem在Postload方法里调用了 PostLoadPrimePools方法去做这件事。

2、从Cascade的对象池里取出的实例会重新Register Component。而Niagara则默认不重复register。这样导致Cascade粒子从对象池取出来,照样会浪费CPU的消耗,而且消耗还不低。

在我的机子11代i7 CPU上,regiseter一次Component居然需要花费2ms左右,怎么可能不卡?

 而Niagara的优化:

 Niagara还注释了为了避免Register/Unregister Component的消耗:

3、即便从Cascade的对象池取出的实例,attach到某个component上仍然调用的是SetupAttachment方法,而不是AttachToCompoent方法。但SetupComponent是构造函数里调用才生效的,所以从Cascade对象池取出的无法正确attach到component上!

4、调用SpawnEmitterAtLocation方法去Spawn粒子,从Cascade的对象池取出的实例无法正确设置它的transform!因为它调用的是SetRelativeLocation_Direct和SetRelativeRotation_Direct,该方法只能在初始化的时候调用才能起效,因此Niagara改成了SetWorldLocationAndRotation,该方法会后续用tick update的办法去更新位置和rotation。

5、Niagara有个小坑,就是AnimNotify_PlayNiagaraEffect是不开启对象池的,如果不想改引擎代码,就只能重载该方法才能使用了。同理,Cascade的AnimNotify_PlayParticleEffect也是。

Cascade的对象池源码在:

UGameplayStatics.cpp和WorldPSCPool.cpp

Niagara的对象池源码在:

NiagaraFunctionLibrary.cpp和NiagaracomponentPool.cpp

如果细看的话,这两份代码不能说是毫无关系,只能说逻辑几乎一模一样。

如果想用Cascade的对象池,建议按照Niagara的做法把Cascade的bug给修了。具体查看上面的每个对比的点。

具体使用方法(仅介绍Niagara)

初始化对象池:

打开NiagaraSystem,选择System,然后在Selection面板的System Properties ,Performance里的Pool Prime Size,填入大于0的数字:

但Pool Prime Size大于Max Pool Size,它也只会按Max Pool Size的数量来初始化,所以可以适当调整Max Pool Size数量大于或等于Pool Prime Size数量。

调用代码去Spawn时:

UNiagaraFunctionLibrary::SpawnSystemAttached或SpawnSystemAtLocation方法,在参数里传入ENCPoolMethod::AutoRelease或者ManualRelease才能使用对象池。

  • AutoRelease:在lifetime到时间后自动放回池子里。
  • ManualRelease:意味着后续需要你自己手动调用ReleaseToPool方法才能把特效放回池子里。

蓝图里Spawn:

 Pooling Method选择AutoRelease或者ManualRelease。

为什么需要使用对象池

        因为UE的粒子在Spawn的时候,ActorComponent需要注册Component,这个时候就会对CPU产生比较大的消耗。

        而使用对象池的优化原理就是,先初始化一定数量的实例在池子里,然后需要的时候再去池子里取,然后在激活使用。

        什么时候需要使用对象池呢?就是同一种粒子特效需要高频播放的时候,或者同时大量播放的时候,那么对象池是对于CPU消耗来说,是个必备良药。代价只是多占了那么点内存嘛,初始化加载时间会多一点点嘛(只要数量不会太夸张,用到成千上万的数量)。

总结:

        对于一个这么大的引擎来说,有这样的bug的确是让人意料之外,但UE也希望大家都使用Niagara,Cascade也在被慢慢遗弃,截止到4.27,Cascade对象池的问题依然存在。而UE5则默认是关闭Cascade的。

        所以本文作者在这里也呼吁大家都直接转为使用Niagara,虽然4.26的Niagara确实还有一些不好用的bug,但如果熟悉了,也是能避免的。并且Niagara更自由了。如果完全不使用对象池,则继续使用Cascade粒子是没问题的。

参考:

Unreal引擎4.26.2源码。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值