拖尾探索
标签(空格分隔): 特效
拖尾效果
个人理解是一个物体在移动的时候,在它的移动路径中经过的位置会出现残影、尘埃等等特效的一种效果。如汽车移动时排放的尾气就是一种拖尾效果,可以用拖尾加粒子系统组合来完成。
unity 里面的拖尾
unity 中实现拖尾非常简单,给 gameobject 添加一个 TrialRenderer 就完事了,丰富的 API 和参数几乎可以解决拖尾的所有问题。因为 unity 没有开源,我们尝试从他的接口去猜测他的实现原理。
重要 API
Time:
- 拖尾存在的时间。
Min Vertex Distance:
- 最小顶点距离,这个距离决定了一个物体至少需要移动多少的距离,才会显示出拖尾。注意这个值越小,意味着拖尾的分段渲染更多,效果越平滑,同时性能方面也会有更多的消耗。所以应该尽可能地在不影响需求效果的情况下使用最大的值。
Width:
- 拖尾的宽度,在 unity 中,width 属性可以设成一条曲线,作为对应比例的长度的尾巴的宽度。
TrialRenderer 还有很多很好用的 API 但是最关键的应该就是这几个了,从上面几个 API 看来,unity 的做法是,按照这个 Min Vertex Distance
来将尾巴划分成很多段,每一段有对应的顶点数据逐段渲染的。在游戏蛮牛中也又看到使用 MeshRenderer 来实现武器拖尾效果的方法,原理就是在每帧记录当前帧的武器位置、朝向信息、并根据效果持续时间来记录消失时间点。详情。
总结下来,unity 上对拖尾的支持可以说是比较完备的,而且容易使用,当然没有源码的情况下,它屏蔽的细节也相当的多不方便去探索背后的实现原理。
2D 引擎的拖尾
cocos
2D 的拖尾效果在搜索引擎中最容易搜出来的就是 cocos 的拖尾,cocos 的拖尾和 unity 一样也是有一个专门的类 CCMotionStreak 以供使用。博主火云洞红孩儿也进行了相关的源码解读。里面的原理也是和上面说的类似,根据设定的最小段值,来记录物体移动的一系列顶点,再根据顶点来生成四边形,最后达到拖尾效果。
egret & laya
相比于 cocos ,egret 和 laya 这两个 h5 引擎是没有提供像 CCMotionStreak 这种专门的拖尾类型的,这时如果想实现拖尾效果,我们只能够通过自己来实现了。在经过一番搜索和思考之后,发现有几种形式的拖尾是在 h5 引擎上可行性比较高的。
- 使用一个在一定范围不断发射粒子的粒子系统,通过移动发射器的位置,让发射出去的粒子成为“拖尾”。
- 直接在物体移动过的位置后生成透明度不同的图片,造成残像的“拖尾”。参考
- 使用更加简单的“画线”法,来模拟拖尾。参考
laya 拖尾参考实现
这里是暂时只实现了第一种移动粒子发射器的方法,本来这种最简单的方法没什么说的必要,但是因为在 Laya 上粒子发射器的位置隐藏的有点深,所以就写出来分享一下。
在白鹭引擎里,粒子发射器的位置比较明确,有一个 emitterXY 的公开接口可以设置,不过在 Laya 的实现中,就没有这么明确了。在 Laya 的粒子系统中,Particle2D 是负责播放粒子效果的类型,里面有一个 EmitterBase 粒子发射器的类型引用 emitter
,和一个粒子参数数据类型 ParticleSetting 的引用。复杂就复杂在于,emitter
中并没有控制粒子发射位置的接口。
继续查源码会发现 emitter
发射粒子的函数 emit()
最后其实是调用 ParticleTemplate.addParticleArray(positon: Float32Array, velocity: Float32Array)
方法。毫无疑问,里面的第一个参数 position 就是粒子发射的位置。这里遇到的问题是我把 Emitter 里面传入作为 position 的变量 _posRange
当成了最终的位置,殊不知,其实在 ParticleTemplate 类型中,其实是有几重的继承关系的。其中,真正创建粒子的是 ParticleTempleWebGL 这一层的 addParticleArray
方法中调用 ParticleData.Create(settings:ParticleSetting, position:Float32Array, velocity:Float32Array, time:Number)
中的 position 才是最终的发射位置。而在 Emitter 中传入的 position 其实在 ParticleTemplateWebGL 的子类 ParticleTemplate2D 里面还会经过处理,查看 ParticleTemplate2D 的 addParticleArray
方法可以发现,里面加上的 ParticleTemplate2D 的字段 x
、y
才是真正的控制粒子发射位置,_posRange
不过是一个发射的误差半径。
理解到了这里,问题就很简单了,简单写一下代码
private sp:Particle2D = new Particle2D(settings);
onMouseMove():void{
(this.sp.emitter.particleTemplate as Laya.ParticleTemplate2D).x = Laya.stage.MouseX;
(this.sp.emitter.particleTemplate as Laya.ParticleTemplate2D).y = Laya.stage.MouseY;
this.sp.emit();
}
区别
unity 和 cocos 的拖尾能够做出的拖尾效果是像刀剑划过的光影那种拖尾,而不是单纯的有东西渐隐显示出移动轨迹的拖尾。是像这样的,而如果加入粒子材质的话,可以做出更加炫酷的效果
而针对上面的实现,只能实现像这样的效果
更好的实现
在参考了 cocos 源代码的拖尾实现后,自己在 Laya 上写了一个拖尾效果,代码是在公司里面写的不方便拿出来,又不想再抄一遍,就在这里把思路说一下。补充一下,原理上和 cocos 上是相似的,逐帧取样点,使用这些样点生成一段段的条带,至于颜色、透明度、宽度渐变这些都是通过插值做出来的。
思路:
- 逐帧取样路径点,检测该路径点和上一个点之间的距离是否足够远(太小不必生成)。
- 遍历路径点数组,不断取当前点和后一个点,计算这两个点对应生成的条带位置、角度、宽高,并存入一个 Data 数组中。
- 遍历 Data 数组,创建对应的矩阵,使用 drawtexture 把它画出来。
注意:
- 逐帧所取的路径点,有可能因为速度过快,而使两个点之间的距离比较远,这样计算出的条带可能会很长,以至于整个尾巴走样,这种情况可以创建多一个顶点数组
vertex[]
,将路径点遍历一遍,并划分成足够小的点再存进顶点数组vertex[]
,然后在第 2 步中使用vertex[]
代替计算即可。 - 生成条带的数学计算需要自己去完成,我的方法也很笨重。
- 结合 laya 的粒子系统和发光滤镜,可以做出非常不错的效果。
缺点:
- 在小距离缓慢移动的时候,矩形拼凑特别明显。
- 拐角时会有缝隙。
引用
unity - TrailRenderer
游戏蛮牛 - 武器拖尾的实现