-
Notifications
You must be signed in to change notification settings - Fork 865
看Hilo如何描绘HTML5互动世界——粒子特效
在上文中看Hilo如何描绘HTML5互动世界——补间动画中我们描述了特效的前半部分——缓动,后半段的撞击破碎效果可以使用今天提到的粒子效果来实现。
粒子系统可以看成是多个粒子渲染单元的规则或不规则运动的集合,因此在组成上也就分为两个部分——粒子单元和运动集合。
单个粒子而言,和普通的游戏对象没有太大区别。但是当多个粒子组合在一起以后就会形成各种形状图案,运动后形成各种运动轨迹。粒子特效在需要氛围场景的情况下应用广泛。
Hilo 提供了ParticleSystem 能够实现规则粒子一般场景下的粒子特效,如火焰,烟花,尾气,云雾,爆炸等效果。
粒子的外观来源可以多样,可以是Graphic对象,可以是拼合图(Sprite)的某一区域,也可以是一张单独的位图(Bitmap)。
ParticleSystem 支持通过一张拼合图(Sprite)的形式来描述粒子形状,只需要指定拼合图的 image
字段(拼合图),和指定帧的区域信息。
var particleSystem = new Hilo.ParticleSystem({
x:0,
y:0,
emitNum:20,
emitTime:1,
particle:{
frame:[
[75, 236, 7, 11], //指定拼合图单图有效信息
[119, 223, 7, 17],
[90, 223, 22, 17]
],
image:img, //指定拼合图对象
life:22,
alphaV:-.01,
vxVar:300,
vyVar:300,
axVar:200,
ayVar:200,
scale:.5,
rotationVar:360,
rotationVVar:4,
pivotX:.5,
pivotY:.5
}
});
ParticleSystem 定义了两类概念——发射器(Emitter)和粒子(Particle)
- 发射器:发射器可以约束粒子的整体行为,如发射多少粒子,发射位置。特别指出,
gx
,gy
两个参数指定所有粒子的重力大小。另外,利用发射器可以将粒子效果——如尾气效果,布置到汽车上(通过指定坐标),这样就可以做到汽车喷气的效果。 - 粒子: 需要特别指出的是
*Var
类型的变量,如vxVar
,vyVar
,这类在已知变量a(对应的vx
,vy
)的后缀加上 Var 是为了限定a 变化区间。 例如,vx = 100; vxVar = 80
那么vx 的区间为[100 - 80, 100 +80 ]
实际上,Hilo提供的ParticleSystem解决了一类规则运动问题。这些粒子的特点是:
- 在某方向上有速度和加速度
- 粒子和粒子之间的运动非常相近
- 有一定的生命周期
- 粒子在运动过程中除了速度和方向上改变,还有旋转、缩放或透明度变化
- 相似的初始位置
- 以上特征在一定范围内随机
使用ParticleSystem可以快速创建出火焰,烟花,尾气,云雾,爆竹,雨滴,降雪等效果,如下图:
但是在还有一类不规则效果,粒子之间的运动有一定的差异性,需要对粒子的运动设计单独的计算方式。
在编写特殊运动效果之前,我们先构建一个粒子单元,即一个可视对象View。我们选择一个Bitmap来做这件事情。
粒子位图即一个位图对象,和ParticleSystem相比,可被渲染的对象更丰富一些——可以是单张Bitmap,纯色背景,或者是一个Graphic 对象。
var particle = new Hilo.Bitmap({
image:texture,
rect: [0,0,w,h], //指定位图选取图片的位置区域
x:x,
y:y,
onUpdate:function(){
//更新位置,旋转,透明度信息
}
});
下面是根据星星图案创建的单个位图对象:
创建好粒子位图后,我们就需要对这个粒子位图做运动计算。
以@墨川 实现的猫头散开运动为例,单个粒子——每个三角形面片p(Bitmap)在执行动画时有速度,旋转和alpha 值变化,粒子的update函数如下:
onUpdate = function(){
if(this.isStart){
this.x += this.vx;
this.y += this.vy;
this.rotation += Math.random() * 10;
this.alpha -= Math.random() * 0.1;
this.scaleX -= Math.random() * 0.01;
this.scaleY -= Math.random() * 0.01;
...
}
}
单个粒子的运动考虑清楚以后,我们再借助粒子编辑器(下文会提到)获得所有粒子的集合particles = []
。
然后,我们确定猫头的中心位置,根据每个粒子距离中心的位置来确定每个粒子延迟的缓动值:
var distance = Math.sqrt((p.x - centerX)*(p.x - centerX) + (p.y - centerY)*(p.y - centerY))
var delay = ( distance)* 5; //延迟时间和距离有关
这样同心圆上相同半径圆弧上的粒子就会有有相似的运动轨迹。
顺便,记录下每个粒子最初状态,在做猫头合拢动效的时候使用:
p.target = {
x:p.x,
y:p.y,
rotation:p.rotation,
delay:delay
};
猫头合拢的动画就是缓动回之前的状态:
p.close = function(){
p.isStart = false;
Hilo.Tween.to(p, {
x:p.target.x, //缓动到开合前的状态
y:p.target.y,
rotation:p.target.rotation,
alpha:1,
scaleX:1,
scaleY:1
},{
ease:Hilo.Ease.Bounce.EaseInOut,
duration:1000,
delay:p.target.delay
})
};
还有一类粒子系统实现了比较好的生物群落仿真行为,如 Boids,Boids粒子运动算法是克雷格·雷诺兹于1986年提出的。它们用于模拟各种畜群,虫群,鱼群,鸟群(Flocking)等生物群落行为,Boids内的粒子可以对其它物体的存在和自身起反应,具体算法描述参见 Boids 算法实现,如果有兴趣改成Hilo的版本,欢迎到 Hilo Github 提PR。
上图是模拟Boids的一种实现。
编辑器是一个非常重要的辅助互动&游戏开发的思路。整个互动&游戏开发开发流程中核心部分就是数据、驱动和渲染,其中,数据和渲染又是核心中的核心。数据,往小的方面来说就是在一段简单代码前需要构思怎样的数据结构;往大的方面来说,就是构建所有游戏对象的基础信息(怎样的位置、形状、运动等等)如关卡数据,地图数据,骨骼数据,帧动画数据,UI数据,粒子数据等。 拿到这些数据我们可以使用Hilo来渲染,可以使用3D来渲染,只要数据格式匹配,是可以使用任何引擎来做渲染的。通过编写编辑器我们可以快速拿到需要的数据格式。
Hilo会以工具的形式提供一些通用的编辑器。
按照上文提到的ParticleSystem 的粒子特点,这类粒子有相似的初始位置,XY方向上的速度和加速度,根据这些特点我们可以针对性的写一个运动编辑器,Hilo提供了这样的粒子运动编辑器:点击这里使用编辑器
编辑器效果UI如下:
- 区域1:粒子效果展示
- 区域2:粒子参数,对任何一个参数值都有对应的调整按钮
- 区域3:导出的粒子数据格式,这些数据可直接运用到 ParticleSystem 的构造函数中
- 区域4:预置的一些粒子效果
规则粒子的运动参数基本是固定的,因此其对应的运动编辑器会比较容易写。
通常不规则粒子运动受算法制约,适用性没有规则运动广泛,因此除非有特殊需要,才会编写编辑器。但是,一些场景下,不规则运动的粒子最初会有一些图案和特殊的粒子形状,例如天猫年会需要展示的猫头。这种场景下需要我们把组成猫头形状的粒子特征提取出来。
拿到视觉稿分析一下,基础图形的形状只有四个(a,b,c,d):
那么我们需要编辑器给我们产出两类信息——位置(相对矩形区域)和形状:
{
"colIndex": 1,
"rowIndex": 0,
"color": "rgb(240, 131, 39)",
"alpha": "1",
"type": "d"
}
拿到这些基本信息我们就可以构建单元粒子的外观、形状和位置。无独有偶,在之前的文章《天猫抢豪车竞速互动技术回放》讲到竞速赛道的地图编辑器也是相似的道理。
总结起来,如下图所示:
在需要提供渲染数据支持时,我们编写对应的编辑器;在需要提供运动数据支持时,我们编写对应的编辑器;在需要动作数据时,我们编写对应的编辑器。
由于粒子系统要操作大量的元素,因此在性能上会遇到一些问题。不过粒子效果多数为氛围效果,交互性不是非常强,所以只要保证粒子在渲染的时候符合预期就好了。
粒子非常多的情况下,如果逐个粒子渲染是非常耗性能的。下面的这个动画是我们使用dom实现的一个方案——改变dom的transform值来实现的效果。
PS:以上是在相同的帧率下 进行的录屏操作
可以看出,相比较猫头散开的动画帧率下降很多。
所以, 尽可能把所有渲染进行batch,Hilo提供Webgl的渲染模式,可以直接把这样的渲染效果集约到一次Draw call中进行。
使用上,只需要在创建stage
的时候将渲染类型指定为"webgl"
:
var stage = new Hilo.Stage({
width:canvasWidth,
height:canvasHeight,
container:containerID,
renderType:"webgl"
});