源码学习网 首页 编程学园 ue4教程 查看内容

Niagara粒子分享-落叶

2019-7-16 22:04| 发布者: opiye| 查看: 72| 评论: 0

摘要: 帮一位朋友分享几个UE4中的Niagara粒子系统的案例。引擎版本使用的是4.21.0,关于Niagara的基础知识这里不会提及。先来看效果,是一个比较简单的落叶粒子系统,在各个模块提前准备好的情况下用一晚上就可以做出来, ...
腾讯云服务器秒杀

帮一位朋友分享几个UE4中的Niagara粒子系统的案例。

引擎版本使用的是4.21.0,关于Niagara的基础知识这里不会提及。

先来看效果,是一个比较简单的落叶粒子系统,在各个模块提前准备好的情况下用一晚上就可以做出来,非常方便。

首先这个落叶有简单的碰撞。

和地面进行碰撞

然后是一些交互,可以将叶子给吹起来。

大概这么个意思

叶子碰到场景里的火焰会被点燃。

还可以被火球砸飞并点燃。

如果能看清的话


首先分析一下,粒子会发生这么几件事情。

1. 在天上自由落体

2. 在地上躺着

3. 被吹着

4. 被点燃

5. 被火球砸中弹开

这些事情并不完全冲突,先做出能够让粒子发生这么几种事情的模块,然后再处理它们之间的关系就好了。

一.生成落叶粒子

使用一个MeshRenderer,挂上叶片模型。

一个SphereLocation控制粒子初始位置

二.粒子自由落体

直接上一个重力、一个CurlNoise完事


三.碰撞相关

使用官方的碰撞检测模块

默认碰撞通道是StaticMesh

碰撞之后,我们不希望粒子还受到重力和Curl Noise Force的影响,所以用一个Collision Rest模块,让粒子速度和受力归零,并且关掉Solve Forces And Velocity模块的Apply Force。

Collision.Vaild只有在粒子碰到地板的一瞬间为True,而Particles.HasCollided一旦变成True,就得用别的模块去恢复为False。与其纠结官方给的模块,不如自己做一个Trigger来一次性管理,同时之后要接入新模块的话,控制起来也更加方便。

非常简单但是作用非常大的Trigger模块,天知道为什么官方不提供?

必备模块NMS_Trigger

这个案例中Trigger触发的条件为,当Collision.Collided为True的时候,触发,使Particles.NMS_Trigger.Trigger=True。一旦碰撞,就会触发。Trigger关上的条件是粒子速度大于10的时候,粒子就会认为自己即将从躺尸状态复活,将Trigger关上,以接受力的影响。这里的思路是我们希望粒子受力,所以改变粒子状态使粒子受力,而不是因为粒子受力离开地面,所以粒子状态改变。

用速度来判断是因为我不相信他的碰撞检测结果,会导致粒子抖来抖去最后穿过地板。用速度判断能排除这些干扰。同时,如果我们要唤醒粒子,只要直接施加速度就可以了。

四.粒子朝向调整

为了让粒子朝向速度方向,我们可以在Renderer那里设置速度对齐。然而有些时候我们不要对齐速度,在天上的时候要叶片尖端朝向速度,在地上要叶片朝上。另外,默认的速度对齐对齐的是X轴,万一有的模型正面是Y轴或者Z轴,这个默认的对齐就不好用了。

所以我们可以自己制作一个用来控制Mesh Particles 朝向的模块,这里我的叫做NMS_Velocity Mesh Alignment,但实际上不只是可以对齐速度。

NMS_Velocity Mesh Alignment

当Trigger没触发的时候(在天上),粒子的X轴对齐粒子的移动方向(Particles.MoveDir),当Trigger触发的时候(在地上),粒子的Z轴对齐碰撞点的法线(Particles.CollisionNormal)。这样粒子就能显示正确的朝向,落到地上的时候也能对齐地面躺好。并且这个朝向变化是逐渐变化的,通过Lerp控制,所以能看到落叶在碰到地面后缓缓躺下。

使用Particles.MoveDir而不是Particles.Velocity的原因是后面有的模块会直接修改粒子的位置,毕竟速度和力来描述运动并不是那么精准。Particles.MoveDir由一个简单的模块计算得出。这里我叫做NMS_Get Move Dir From Pos

NMS_Get Move Dir From Pos

而用Particles.CollisionNormal而不是Collision.Normal,是因为Other类Parameter每帧会重置,万一没碰上东西,这个Collision.Normal就会被重置。而我希望当没碰撞的时候我们记录的碰撞点的法线不会丢失,所以用Particles.CollisionNormal将Collision.Normal存起来。这个功能没必要专门写模块,用Set Variables就可以了。

Particles.CollisionNormal

五.将粒子吹起来

我这里用的是右键吹起粒子,并让粒子跟随鼠标移动。我的蓝图由于太过于混乱邪恶,就不上了。

思路是蓝图做射线检测,然后修改一个Niagara Parameter Collection里面的值。把旋转中心和是否进行旋转给存起来。然后多个Niagara 粒子系统就可以拿来用。

这里的Enable不是bool,而是float,因为这个值我还顺便拿去做状态转换时候的插值了

回到Niagara中来,实现让粒子旋转的思路一般有两种,一个是让向量绕轴旋转,一个是给粒子施加向心力和旋转用的切线方向的力。就这两种思路就可以组合出很多种模块来应对各种情况。

这里我选择让粒子直接绕轴旋转,并且通过一个向心力来控制旋转半径。这里的力会我直接在模块里计算成了粒子位置。用速度和力效果会更好,状态转换会更平滑。

NMS_RotateAroundPoint

这里的Particles.RandomFloat是在粒子Spawn的时候获得的一个随机值

如果在Update里取随机值,每一次取的值都会变化

这里的所有参数都受到Particeles.NMS_Trigger001.Trigger控制,是Trigger001不是Trigger。Trigger001是另一个Trigger模块,当粒子符合被吹起来的条件的时候触发。

当粒子的XY坐标到旋转中心的距离小于一定值的时候,并且这个NPC.NPC_WindRotateForce.Enable大于0.01的时候,说明粒子符合被吹起来的条件。当NPC.NPC_WindRotateForce.Enable小于0.01的时候,说明粒子不应该再被吹起来了。

这里否则对比大小的Dyanamic Input - Set Bool Float Comparision 是我修改过的,加了个Revert,原来的那个居然只有大于没有小于,挺麻烦的。

Trigger001

六.点燃粒子

首先让落叶材质能烧起来,材质用Dyanamic Parameter控制。

我这里X表示燃烧程度,Y表示燃烧边缘宽度,Z和W是噪波的随机偏移值,毕竟不能让每个叶子烧起来都一样。

那么问题来了,怎么让Niagara知道某一个粒子应该被烧了呢。

我的方法是,使用一套参数接收当前火焰燃烧的区域,然后如果粒子处于区域内,那么触发点燃的Trigger。如果场上存在多个火焰燃烧区域,那么就每一帧传其中一个,下一帧传另一个,直到所有在粒子系统附近的火焰被遍历完毕,重复以上过程。总有一帧,粒子会知道自己是不是在火里的。当出现了一些不是场景里固定存在的火焰时(比较玩家制造的火球爆炸),优先传这种动态火焰的参数给Niagara。如果火焰太多,就考虑多加几个同样的Trigger。

这里我用的Niagara Parameter Collection比较简单,用Position加一个Radius来表示一个球形燃烧区域。

传递火焰信息的蓝图如下。

已经尽力整理了....

回到Niagara。设置一个Trigger来让树叶燃烧。但这里要用的Trigger略有不同,这里还要一个计时器,当Trigger触发之后,计时器开始计时,逐渐让粒子燃烧程度增加。

NMS_TriggerTimer

这就是我的计时器Trigger。为什么要有个Special?名字就别在意了。

一旦某一帧里,这个粒子位于某个火焰里面,则Trigger触发,计时器开始计时,时间存在Particles.NMS_TriggerTimerSpecial.Timer里。

然后用来控制Dyanamic Parameter。

通过Timer来采样一条曲线

当落叶完全燃烧的时候,消灭这个粒子。这个Kill模块就是直接修改的DataInstance.Alive

把这个做成模块会挺方便的,用的比较多

七.火球交互

火球和燃烧相比就是多了个将落叶击飞而已。

把官方的Attract Force反过来给力就是这个模块了,传力的方法和火焰是一样的。

我就是复制官方的那个,改了下方向的

由于时间原因(懒),很多细节和步骤都没有写,以后有机会再做补充吧。




来源网址:https://zhuanlan.zhihu.com/p/73542406

鲜花

握手

雷人

路过

鸡蛋