欢迎光临散文网 会员登陆 & 注册

Niagara树叶交互

2023-03-12 21:30 作者:尘星_Star  | 我要投稿

    之前在A站看到一个树叶交互的游戏Demo,觉得很酷所以试着自己尝试做一下树叶交互。树叶的整个蓝图的基础框架是参考@SilenceMoon(感谢授权转载)里的,我结合自己的需求进行简化修改。希望大家多多提提意见。

    大致思路就是通过蓝图传递我们需要的属性到Niagara中,我想着尽量具有可拓展性高,一般需求只用调较少的参数。直接进入正题吧。

    首先叶子的模型是在Blender生成的,贴图直接用的是Bridge的,有现成的就不花时间自己做了。


    尽量保证叶子有一点弯曲,在引擎中看起来就会更加立体。

    

    粒子的模式肯定是GPU节省性能,我这里发射的是2000个粒子,给粒子初始的随机旋转以及一点点随机速度。

    

    碰撞的模式用的距离场,需要在项目设置中启动距离场。当时做的时候光线追踪的碰撞刚出我也试了下,切换到MeshRender有问题,不知道是Bug还是我哪里出问题了,我的显卡是2060应该支持的。这个问题先暂时跳过吧。简单的设置一下碰撞,Bounce给的0.01,不要让树叶像皮球一样弹起来。摩擦力这边默认设置,因为是在平面上模拟我就忽略了。注意把Rest里的Enable Rest State勾选项去掉,树叶需要一直接受物理模拟,我们不需要休眠。之后给树叶GravityForce,CurlNoiseForce作为最基本的物理模拟。值得一提的是,官方在最近的一次更新中,更新了AreaDynamicDrag。这个就是专门用来模拟树叶,纸张等受到的空气阻力。里面对四元数的运用和很多运算操作还有Debug的方法都让我学到很多,大家可以点进去看一下学习一下。GravityForce,AreaDynamicDrag大部分值都是默认的参数。做好树叶的基础材质给上,这样最基础的部分就完成了。

   

     然后需要解决叶子落到地面的朝向问题,我们需要粒子落在地面时它的朝向是朝上的。一开始我考虑了两种方法。一种是粒子的速度为0时,他的朝向向上;还有一种是基于距离场判断粒子距离附近表面的距离,当距离为0时粒子朝上。后来经过测试,我选择基于速度判断。因为基于距离场,当叶子靠近墙这种接近90度的物体表面时,粒子朝向会改变趋于一致,造成Bug。这里根据速度,当速度不为0时,保持物体原有的旋转,速度为0时让叶子的朝向向上。图中的FreezeInfluence暂时不用管,这是后面要提到的。在这里经过Max的判断,默认给出0,所以一般情况不会受到FreezeInfluence的影响。还有就是暂时没有考虑叶子的朝向随坡度的影响,这里我想其实可以参考官方示例中虫子哪里,用Find Nearest Distance Field Surface GPU中拿出Vector To Nearest Surface找到叶子距离最近表面的点的法线方向,给到叶子朝向再与速度绑定就可以了,这里我暂时还没有深入研究,给出一个大致的参考。最后模拟的结果,对于接近地面速度较大的叶子,模拟的结果还比较不错;对于缓缓落下接近地面速度较小时,还是有方向突然改变的顿挫感,这里我暂时还没有比较好的解决方法。不过由于数量多,现象也不是特别明显就暂时放着了。

    

    出于美术的考虑,给叶子大小,色相赋予随机值。那么叶子的准备工作就好了。

   需要在WorldSetting里,将GameModeOverride 换成自己新建的。

   

  

 

     控制系统用的UE5新出的EnhancedInput,这个网上有很多相关的教程,我就不赘述了。其中还加了很多判断来进行各种效果。比如当Attack为Flase才能对镜头进行上下左右的移动,否则当你攻击选择位置时鼠标移动,屏幕也会跟着晃动。

    由于不想让文章变得过于冗杂,有很多细节我没有提及,尽量多提及一些关键点。

    

    技能的整个架构参考SilenceMoon的GA,GE模板。这里的主要目标有两个,1.首先创建一个GameAbility的类(在创建之前先检查是否以及存在),并提升为CurrentAbility的变量。这样可以根据键盘的1、2、3数字键切换攻击的模式。我一共做了火、风、冰三种类型的攻击模式。2. 每帧获取鼠标的信息。

    

   

     在GAS_Master的蓝图Actor里,首先构建一个SpawnGE的函数,生成一个GameEffect的类。通过传递鼠标的信息包括位置,方向的基础上,我还定义一个Vector向量需要传递进GE里。

    

    在GA的主模块,主要设置一下鼠标UI的显示等。

    在GE中,这块地方是最重要的。初始化Niagara组件,并提升为变量。方便后面给不同的效果上不同的Niagara模块。

    

    

    然后通过NiagaraParameterCollection传递信息到Niagara中,传递的值我用了5个,其中包括2个Float、1个Int、2个Vector的值。根据命名很好理解,我想传递力的位置、方向、大小和所影响的半径。对于一般需求可以这么理解,但是对于比较特殊的效果,所传递的信息不一定是与命名一致的。至于Index是后面加上的,这个后面我会详细讲述的。

    首先就是火的部分了,这部分想试一下最基本的交互。火球产生,叶子被吸引;火球走一段时间后爆炸,周围的叶子会被炸开。

      在鼠标左键按下与松开分别记录此时鼠标的位置,并设置变量 。并使Niagara的起始位置比鼠标的位置高1米,然后把按下鼠标的位置也传递到GE里,并给一个新的Niagara系统。

     这一部分是设置UI,一些简单的运算以及防止Bug(鼠标按下以及松开的位置一样等)的出现。 

      定义一个新的变量FireSpawnTime这个时间是火球从生成到移动的时间,我设置为1S,并且传递到Niagara的User  Exposed 中

    通过传递进来的位置,松开鼠标的位置(即Actor的位置的这里只取X,Y值)来确定火球移动的方向。通过Lerp连接两个位置,至于Alpha值通过曲线来控制;而后通过TimeLine控制Alpha的移动。注意在Niagara中勾选LocalSpace,否则不会移动的。

    然后再添加一个执行接口的曲线,命名End,只需要保持横坐标为结束时间的点即可,纵坐标再此处没有意义。在结束后我再发射一个GE,设置好位置,作为最后的爆炸。

    

    最后设置力的相关属性,力的持续时间是通过Delay实现的,延迟多长时间后参数归0,意味着力的持续时间。Index的作用大家可以猜猜有什么用,这个是到后面出现问题后加上去的,我暂时先买个关子。

    这个Niagara系统没有什么有趣的东西,就跳过了。

    现在来实现树叶的部分。

      传递过来的参数在这里。

      之前提到过首先需要把叶子吸起来,我一开始还是尽量使用系统自带的模块去做,在Update模块里添加Point Attraction Force。

   将之前提到过的把力发生的位置,半径,大小传递过来,在蓝图那边调好参数。就实现了第一步的模拟。

    

    接下来是爆炸的时刻,这次想把周围的叶子全部炸开。设置好参数后,出现Bug了。系统不知道何时只对点吸引力作反应,何时对点排斥力做反应。所以这个时候,我引入Index的变量。这个变量作为效果索引,比如Index1只想要吸引力的效果、Index2只想要排斥力的效果、Index3既想要排斥力又想要漩涡力。

    我把吸收效果作为Index1,炸开效果作为Index2。接下来就需要在Niagara中判断了。怎么写这个逻辑了,说实话这个卡住我一段时间,我想要这个具有可拓展性,以后再想要什么一般效果直接再原基础上进行很小的修改,甚至不用修改。

    

    最后,我直接用最为暴力的判断加上逻辑或。首先判断传递进来的Index值,然后传递到对应力的逻辑或上,有就触发输出真。说实话,尽管之前我不怎么在乎性能上的考虑。但这个让我极为难受,叶子在大多数情况下都不会受到技能力的影响。却在每帧执行几个判断以及逻辑或,随着Index3、4出现上面也会更冗杂。之后,无意间发现一个思路。首先对Index进行一次判断(判断是否为0),若不为零,进行下一步。在力的逻辑上可以用数组进行查找,若Index在此数组内,则输出真。文章说这个比或的逻辑要更省。这里给出一个参考(希望大家多多提提意见),我的重心暂时还是在美术上,就不花时间深入了。这里暂时还是这样先用着,秉着能用就行。

    

    最后在需要的力的模块与力的强度相乘即可,这样彼此的效果就不会相互影响了。一开始我还加了一点漩涡力,来让叶子具有更多动感。但是时间太短,效果也不是特别好,而且官方的VortexForce没有半径我就没加。(虽然后面还是跑不掉)。

    

    爆炸产生的贴花参考了Lyra的工程,直接在Niagara中实现。

    材质里的UV需要通过WorldPositionBehindTranslucency这个节点与ParticlePosition相减再通过一些计算得到。透明度可以根据世界法线的一些运算,让贴花从水平到垂直方向上依次减淡,直至消失。

    

    火球的火焰效果是通过Embergen做的循环序列帧图完成的,真的很好用推荐一波。如果熟练的话十分钟就可以得到想要的序列帧图。

    接下来就是风的技能,这个最简单的设想就是两个龙卷风合成一个更大的龙卷风。途中经过的树叶会被吹起来。

    风的GA模块没有什么大的改变,鼠标松开时发射两个GE对应两个龙卷风。

    

    之前火的释放位置是鼠标松开的位置有点不符合玩家的预想,也不好操作。这次做了修改改成释放的位置为鼠标按下的位置。首先获得Actor所在的位置,与鼠标松开的位置是一致的,作为ReleasePosition,PressPosition通过Vector to GE传递进来。

    风的路线首先通过鼠标的按下与松开确定风的前进方向,前进的距离在通过刚才确定的方向归一化后相乘即可,为了方便美术调整用变量ExplosionDistance确定。至于风的偏转,用叉乘Cross来计算。叉乘最简单的几何意义就是得到两个不重合的直线所组成的平面的垂线。所以我想偏转的方向就是风前进的方向与Z轴组成平面的垂线,另一个GE龙卷风只需要改变一下叉乘的方向就行了。用Lerp来驱动起点和终点,在此基础上加上刚才所求的垂线的偏转量就行了。

    然后用TimeLine做一个Vector的曲线,用Vector的X轴代表时间,Y轴代表轨迹的偏移程度。

    最后输出的FXPosition提升为变量,由于这个值需要每帧改变。所以通过Tick来传递到Niagara中。这里由于这个风的特殊性,需要同时传递两个位置到Niagara中。一个龙卷风通过ForceDirection传递位置,另一个龙卷风通过ForcePosition传递。

   另一个GE没有什么大的改变,就是在结束后放一个更大的龙卷风GE,且最后一个GE龙卷风不产生力的交互。还需要注意的就是上面提到的传递的值不一样,Cross叉乘的方向不一样。

   回到Niagara树叶中。 

    这一部分比较复杂,连线也不好看,可读性不强,这时候用HLSL写可能还更易读一些。龙卷风的力我打算用线吸引力和漩涡力组合实现,由于有两个龙卷风所以要写两个。这些其实都是点开官方自带的LineAttractionForce和VortexForce简化而来的。

    首先判断龙卷风的Index值(我设置的为3),以确定是否产生龙卷风的力。然后通过传递过来的值设置线吸引力的两个点的位置以及影响的区域大小,最后加到PhysicsForce上。漩涡力也是差不多的思路。

    最后设置好值就行了。

    

    在风的Niagara中,设置了一个随机值来使得极少数的风的轨迹有着高亮的颜色以增加随机性与美术效果。

    

   

     这个是为了保证模型的初始朝向面向龙卷风的中心,加一点点偏转赋予随机性。其他的都是基础的东西了。

    最后就是冰的技能了,我想要的就是冰爆炸时树叶会结冰冻住的感觉。

冰的GA最为简单了,鼠标松开时生成一个GE。

    在GE里,还是通过NiagaraParameterCollection向Niagara传值。在延迟0.5S后,发生爆炸。结冰的效果主要通过Radius的值来实现。通过从0增长到我们所需要的爆炸半径在回归到0。注意还要确定GE的方向,这样好确定大冰花爆炸的朝向。

    爆炸的半径通过TimeLine来实现动态变化,半径确定为550,整个过程的持续时间是3S。

    回到树叶里面。

    首先将传递过来的ForcePosition的正上方220的方向作为爆炸的中心,然后计算Particle到爆炸中心的距离与刚刚提到的Radius作比较,将得到的值赋予新建的Particle属性FreezeInfluence。

    叶子结冰时,将其受到的GravityForce与CurlNoiseForce的值变为0即可。就好像叶子受到冰冻效果冻结在空中的感觉。将FreezeInfluence与强度值相乘即可。

    在这里会出现一个Bug,还记得之前当叶子的速度为0时,叶子的朝向会趋于一致向上。所以当叶子冰冻时,它的朝向也会趋于一致。

    这里需要将FreezeInfluence,1-x处理一下再与速度进行取最大值。这样的目的是使得叶子在结冰时这里输出的值任为1。不考虑结冰时输出的值为0,即只受速度影响。

    

    最后我想实现的效果是,在初始叶子升起的时候,中心的叶子所受的力大,边缘的叶子所受的力较小。注意这里也用Index值进行了判断。

    到此为止,叶子的处理已经完成了。

    在雪花的Nigara中

  

  

    雪花升起的运动就是简单设置起点(发射器的位置)与终点(比起点高点)然后通过Lerp实现的。最后为了美术效果用曲线调了下节奏。

    

    出于美术效果的考量,我想要小的粒子升起时,靠近中心的初速度大些,靠近边缘时初速度小些。通过算出粒子与发射器位置的距离,归一化后用Lerp节点设置粒子的最大速度与最小速度,然后再此的基础上再赋予一点随机速度,使其结构看起来不过于规整死板。

    

    然后再生成粒子的位置事件,用其他的发射器接受事件发射Ribbon与Sparks增加美术效果,前面风的轨迹也是用了相同的做法。

    

    

    爆炸的大冰晶是通过采样模型得到的。采样模型的三角形,给予一个很大的随机值保证模型的每个三角形都能被采样到。然后获取位置信息给到Particle.Position,UV信息通过DynamicMaterialParameter传递到材质中进行采样。贴图是通过FlamePainter软件的一个笔刷得到的。然后对Scale即大小进行曲线K值得到所需的节奏。

    对于性能的优化其实还有一些改进的地方的,比如可以在攻击时才进行每帧采样等等。也有很多细节由于篇幅原因无法写出来,不过重要的我都写出来了,这样就基本完成了。整个过程无论美术还是程序逻辑上都还有很多改进的,希望大家多多提提意见。

Niagara树叶交互的评论 (共 条)

分享到微博请遵守国家法律