前言
在游戏中,肯定会有很多动画,逐帧的或者代码实现的。我们今天讨论的呼吸动画就是代码实现的动画。
然而什么是呼吸动画呢?并不是呼吸的动画,而是指一个可视化物体(图片或者按钮)的整体在做呼吸的动作模拟,在开心消消乐和Candy Crush中就能看到类似的实现
气泡整体会做呼吸的动作,这种很舒服的动画增加了很高的用户体验!
原理解析
那么这种动画要怎样来实现呢?
当然,条条大路通罗马,实现的方式有很多种,但最终控制的其实就是对象的缩放属性而已,在egret中则是sacleX
和sacleY
了。
实现1
一般做法是通过两个Tween
来分别控制这两个属性,在1这个值上下徘徊,当scaleX
变大的时候,scaleY
变小,反之亦然,如此循环即可。
若要做得更逼真自然的话,两个Tween
之间加个延时即可,再加上一个egret.Ease.CubicInOut
缓动,基本能实现。
实现2
笔者在经历了众多游戏开发后,得出了一种更为“自然”的实现方法,就是借助三角函数的力量。
如果你已经把高中三角函数的知识全部还给你们体育老师了的话,那建议还是去复习一下吧!三角函数复习资料
通过对x的改变y会产生周期性的波动,这正是现实世界中的物体运动规律,通过这种函数,我们就能实现各种周期性的动画了!
1 /** 2 * Created by rockyl on 16/3/1. 3 * 4 * 呼吸图片类 5 */ 6 7 class BreathImage extends egret.Bitmap { 8 private _duration:number; 9 private _offset:number; 10 11 constructor(value:egret.BitmapData|egret.Texture, duration:number = 2000, offset:number = 0.05) { 12 super(value); 13 14 this._duration = duration; 15 this._offset = offset; 16 17 this.init(); 18 } 19 20 private init():void { 21 this.anchorOffsetX = this.width / 2; 22 this.anchorOffsetY = this.height / 2; 23 } 24 25 play():void{ 26 this.t = 0; 27 egret.Tween.get(this, {loop: true}).to({t: Math.PI * 2}, this._duration); 28 } 29 30 private _t:number; 31 get t():number{return this._t;} 32 set t(value:number){ 33 this._t = value; 34 this.scaleX = Math.sin(value) * this._offset + 1; 35 this.scaleY = Math.sin(value - Math.PI / 2) * this._offset + 1; 36 } 37 }
从代码中可以看出,笔者仍是使用了一个Tween
来实现动画的驱动(你也可以通过egret
的Ticker
来实现驱动),从0到2π之间循环播放。
再通过一个t属性的属性访问器来实现动画的实时“绘制”。
你可以通过锚点来控制缩放的中心位置。
核心就在set t()
中:
1 set t(value:number){ 2 this._t = value; 3 this.scaleX = Math.sin(value) * this._offset + 1; 4 this.scaleY = Math.sin(value - Math.PI / 2) * this._offset + 1; 5 }
Math.sin(value)在一个2π周期内会从0到1,然后到-1,最后回到0,offset
属性就是波动幅度,最后一个1作为常量,这样就实现在-offset
到offset
之间的波动。
如果你还是理解不了,可以画个图来帮助理解一下(其实和随机数的处理类似)。
总结
笔者个人还是推崇实现2的,也不知道是不是笔者首创,估计很多大神早就这么做了。
其实对于三角函数驱动的动画,呼吸只发挥了它的一点点功力,笔者已经实现了很多动画比如:跳跃、圆周轨迹、8字轨迹、N闭环轨迹、不规则轨迹……这里就不给代码了!
开发到一定的程度,你肯定会总结出一些经验,然而分享经验的同时,你也会有所收获!