前言
说到下雨效果,是游戏中常见的一种天气表现,各种教程中,常见用粒子来模拟雨水,效果虽好,但属实是不敢用在对性能要求比较高的移动端设备上,而2D手游上最常见的,是用uv动画来做雨水效果,这也是本文的重点,但怎样做的逼真,怎样做成编辑器模拟各种雨势,就是细节功夫了。
原理
我们这里只讨论2D的雨效,如上所说,雨水效果是用uv动画实现的,但简单的uv动画又缺乏层次感,毕竟雨点会有近大远小的感觉,嗯,那就不妨多来几种uv动画叠加显示。雨势怎么区分,想想现实的雨水,雨滴掉落速度,倾斜角度,天色暗淡程度等,是不是都可以增加雨势强弱的感觉。那雷雨的闪电怎么模拟,因为是俯视角,闪电的感觉全来自场景的快速闪白效果,我们用曲线编辑器模拟一下。最后有个事别忘了,天上下雨,地面就要有水花,不然就像地上的物体没有影子一样缺乏真实感。
一、雨水shader(实现雨水的uv动画)
uv动画的shader是很简单的,毕竟下雨只是雨水的图按uv的y方向向下滚动的效果,我截了部分雨图如下:
为了做无限滚动的效果,雨图要做成四方连续的。上图为了看清雨点,背景有了颜色,实际上是透明的,也就是Alpha是0,最后别忘了Wrap Mode是Repeat,不然也就无法滚动了。
shader很简单,主要是uv的操作,我们拿内置的Sprite的shader稍微改改就行了,顶点着色器的核心代码就一句:
v_uv -= vec2(0, _FallSpeed) * cc_time.x;
就是每帧在uv的y值上减去_FallSpeed,这个_FallSpeed就是uv滚动的速度,也就是雨点掉落的快慢。
片段着色器更简单,
vec4 col = texture(cc_spriteTexture, v_uv);
col = col * _col.a;
混合模式那里,源和目标都设置成one
blendSrc: one
blendDst: one
二、制作Sprite
我们把上述shader,赋予材质,就准备就绪了。然后创建个Sprite,把材质付给他。Type要改成SLICED,运行看看,你应该就能看到一个简单的下雨的Sprite了。
哇塞,是不是这样就可以交差了。。。如果你不在乎策划,美术暗地里骂你的话。
三、层次空间感
为了避免被骂,我们还得再细化一些,想想3D空间的雨滴,是近大远小的,并且由于视野的不同,会有近快远慢,近疏远密的感觉,那2D效果怎么去模拟空间感呢。
说的那么复杂,其实特别简单,我们还是在uv上做手脚,在同一块幕布上,uv值越大,平铺的Sprite就越多,每个Sprite图就越小,所以我们只要给uv一个相乘的系数就可以控制图的大小了。顶点着色器里只需加一句:
v_uv = a_texCoord * _RepeatTimes;
这个_RepeatTimes就是我们想要的东西,值越大,平铺的数量就越多,视觉上图就越小越密。
OK,我们终于可以控制雨滴的大小疏密了,但我要的不是整体的变大变小,而是近大远小,也就是说有层次感,可是2D空间没有z轴,那我们不妨用几层Sprite来模拟一下。
这里我做了3层Sprite,分别模拟近处,不远处以及远处。用不同的RepeatTimes控制大小疏密,再用不同的_FallSpeed控制雨速(近快远慢),甚至在片段着色器中加了个颜色的强度加成,做到近处雨清晰,远处雨朦胧的感觉。
col.rgb = col.rgb * _Intensity;
看一下最终的shader
顶点着色器
片元着色器
近处雨的材质
中部和远处的材质用不同的值去尝试,以达到自己想要的效果。
然后将它们放在一起,充满全屏,这个效果比之前好看多了。
四、雨水编辑器
为了让策划美术们再开心些,我们不妨做个编辑器,把这些参数放一起,省的他们一个一个去编辑。编辑器把近,中,远的雨水参数放在一起编辑,然后分别赋予自己的材质属性,代码很简单,就不展开了,看一下编辑器。
这里面的Distance就对应材质里的_RepeatTimes,用Distance做名称比较好理解。
我创建了一个父物体,把近中远三层Sprite作为子,然后把编辑器代码拖到父物体上,分别控制子物体的材质。
五、雨水角度
再进一步,我们前面说过,要模拟雨势强弱,雨水角度是个不错的参数。就好像大雨伴随大风。那角度怎么做呢,说来也容易,给Sprite的Rotation一个值就行了。雨水角度是个公共值,三层Sprite分别设置这个公共值即可。
我们继续扩展一下上面的编辑器,把角度也加上,然后代码里读取这个角度,分别赋给三个Sprite的Rotation。
六、天色变化
既然是下雨,那就该阴天,并且阴的程度跟雨势相关,可是怎么展示阴天呢,我的俯视角看不到天空,那就让场景上的所有物体暗淡一些吧
我们这里先简单的修改场景所有Sprite的Color值,来模拟阴天的感觉,还是在编辑器里加一个天色的Color属性,然后场景所有Sprite的Color都读取这个天色值即可,代码简单,就不详细介绍了。
七、下雨时长
我们给下雨设定个时长,比如60秒,既然有时间,那就该有雨效开始和结束的渐变效果,比如开始阶段天色从晴逐渐到阴,雨势从小到大,结束阶段则相反。好在上面所有的属性都已具备,我们变化些参数就可以模拟这种渐变了。
首先在编辑器里加上雨水的总时长,再加上开始和结束的时长,这样把下雨分成三个阶段,开始阶段,中间阶段和结束阶段。
然后在编辑器代码的Update里判断当前时间,是在下雨的哪个阶段,如果是在开始或结束阶段,我们就要根据这个时间算个比值,比如开始阶段时长是10s,当前到了5s,那比值就是0.5。
雨效渐变影响两个参数,一个是天色,一个是雨的强度,这两个参数我们上面都已经有了,我们用上面得到的比值去做它们的插值,就可以得到当前的天色和雨的强度。
上面代码,_CurTime就是当前时间,_startToMaxTime是开始阶段时间,_stopToEndTime是结束阶段时间,所以 t 就是在计算插值,intensity是雨的强度,通过插值去计算当前强度。sunColor是天色,也是通过插值去计算。
上述属性的编辑器截图
如上,总时长60s,开始和结束分别10s,下面是天色和雨水角度
八、闪电
做到以上阶段,下雨的属性就基本都满足了,想做多大的雨就调多大的雨,只是暴雨没有闪电,效果不够惊艳。
闪电嘛,我这里是看不到的,没有天空,2D游戏时常是俯视角,这种情况下怎么做闪电呢。想想你住楼上,闪电时你看楼下是啥样,闪白就好了。
说到闪白,可能有人想到,那就接着改场景Sprite的颜色,你可以改改试试,Sprite的Color默认是白色,就是自己的原色,可是闪电啊,原色可不够,我要它白一点,再白一点,只要不闪瞎我的眼。这样的话,恐怕Sprite默认的材质就不行了。那就造个轮子吧,其实也简单,在Sprite的shader里做个颜色的加成即可。
如上代码,_LightStrength就是干这个的,默认是1,闪电时变大即可。天色我也通过_RainColor来传递了。RAIN_EFFECT是下雨的Keyword。
这里要吐个槽,Unity可以动态开关Keyword,但Cocos不提供,所以只能用换材质的方法。不下雨就用系统内置的材质,下雨就换这个新建的材质。
接受闪白的材质有了,控制闪白怎么搞呢,也就是怎么给_LightStrength传值呢,不绕弯子了,用Cocos自带的曲线编辑器RealCurve。
啊。。。我不得不再吐一次槽,Cocos的曲线编辑器也不好用啊,我这是3.8.3的版本了,曲线编辑器不能拖动,不能放大,我用的是Mac版本,不知道Windows有没有这个问题,最可惜的是x轴只能从0到1,不能直观的代表时间,这样是无法精准的设计闪电时长的,只能调着来。希望官方后续能把它做的完善些。
书归正文,上面截图就是调的一版曲线,x代表时间,y代表闪白加成值,可以看出每次闪电时间短暂,曲线峰值尖锐,波峰波谷瞬变,这样就会有快速忽闪的效果。
上面说了,Cocos的曲线x只能从0到1,所以要做个时间的转换,然后采样曲线,传给材质的_LightStrength参数。
闪电效果看开篇的gif图即可
编辑器的闪电编辑如图
做到这,有关雨的部分就做全了,但要想做的逼真,就要场景物体跟着造势,比如地面水花,留给下一篇介绍吧,本文已经太长了,多谢你耐心看完。
往期文章:
2D光影系统-光源一全局方向光
2D光影系统-光源二Sprite光
2D光影系统-阴影
2D描边-内描边
2D描边-外描边
2D基于屏幕的反射