Phaser动画篇
Phaser系列课程初步分成《基础篇上》、《基础篇下》、《动画篇》、《交互篇》、《物理引擎篇》、《图形绘制篇》、《场景篇》。
了解如何创建不同物体和认识物体的常用属性之后,我们在前面知识的基础上对物体添加动画。物体的动画分成两大类:Tween和Animation。
Tween补间动画
通过改变物体的属性创建的动画。1
var tween = game.add.tween(target);
重要属性
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
onStart | Phaser.Signal | 监听动画开始时 | |
onLoop | Phaser.Signal | 监听动画含括子动画循环时 | |
onRepeat | Phaser.Signal | 监听动画含括子动画重复时 | |
onComplete | Phaser.Signal | 监听动画含括子动画结束时 | |
repeatCounter | Number | 0 | 动画重复次数,-1代表无限循环 |
reverse | Boolean | false | 是否反向 |
timeScale | Number | 1 | 动画速率缩放倍数,0.5代表原来速率的一半,耗时加倍 |
totalDuration | Phaser.TweenData | 单程动画的时间 |
例如,监听动画开始时(如果设置了延时,会等延时完成再回调):1
2
3tween.onStart.add(function(){
console.log('动画开始时')
})
重要方法
from(properties, duration, ease, autoStart, delay, repeat, yoyo) 从某个状态去到当前状态
to(properties, duration, ease, autoStart, delay, repeat, yoyo) 从当前状态去到某个状态
属性 | 类型 | 是否必选 | 默认值 | 说明 |
---|---|---|---|---|
properties | object | 是 | 想要改变的动画属性 | |
duration | number | 否 | 1000 | 动画播放时长 |
ease | function或string | 否 | null | 缓动函数 |
autoStart | boolean | 否 | false | 是否自动播放 |
delay | number | 0 | 延时毫秒数 | |
repeat | number | 0 | 动画重复次数,-1无限循环 | |
yoyo | boolean | false | 是否反向 |
start() 播放动画
pause() 暂停动画
stop(complete) 结束动画
参数 | 类型 | 是否必选 | 默认值 | 说明 |
---|---|---|---|---|
complete | boolean | 否 | false | 设置true触发Tween.onComplete signal |
resume() 继续播动画
loop(value) 循环动画
参数 | 类型 | 是否必选 | 默认值 | 说明 |
---|---|---|---|---|
value | boolean | 否 | true | 是否循环 |
repeat(total, duration) 重复动画
参数 | 类型 | 是否必选 | 默认值 | 说明 |
---|---|---|---|---|
total | number | 是 | 动画重复次数 | |
duration | number | 否 | 0 | 动画每次重复之间的时间间隔 |
delay(duration) 动画延迟播放毫秒数
easing(easing) 动画缓动函数,如线性easing(Phaser.Easing.Linear.None)
chain(tweens) 动画链,该动画播完后播另外的动画tweens,多个用英文逗号隔开
Animation帧动画
该内容属于Phaser中相对比较复杂的部分,写法丰富多样,根据不用的使用场景有不一样的写法,为遇到的首个需要重点克服的内容。
我们用艾斯的行走和跳跃动作作为案例。素材下载
当我们的Sprite图每个动作的每个帧宽高都一样时,我们可以用最简单的方法来实现
例如,我要给艾斯添加一个行走的序列帧动画。
先在preload中先加载资源1
game.load.spritesheet('sprite_animation', 'img/sprite_animation.png', 94, 166);
然后在create中创建人物,并设置中心点为正中1
2hero = game.add.sprite(game.world.centerX, game.height - 166, 'sprite_animation', 0);
hero.anchor.set(0.5);
最后在create中添加动作,并播放1
2hero.animations.add('walk');
hero.play('walk', 10, true);
解析:物体帧动画相关的设置都在animations属性下,每个动作都先add个名字,然后执行动作的方法play,这两个重要方法的参数如下:
add(name, frames, frameRate, loop, useNumericIndex)
参数 | 类型 | 是否必选 | 默认值 | 说明 |
---|---|---|---|---|
name | string | 是 | 动作名 | |
frames | Array | 否 | null | 关键帧集合 |
frameRate | number | 否 | 60 | 帧率 |
loop | boolean | 否 | false | 是否循环 |
useNumericIndex | boolean | 否 | true | 关键帧使用数字索引还是字符窜 |
我们可以自定义序列帧frames,来决定一个动作由哪几个帧形成,帧也会按照数组顺序进行播放。
1 | hero.animations.add('walk', [0,1,2,3]); |
play(name, frameRate, loop, killOnComplete)
参数 | 类型 | 是否必选 | 默认值 | 说明 |
---|---|---|---|---|
name | string | 是 | 动作名 | |
frameRate | number | 否 | null | 帧播放速率 |
loop | boolean | 否 | false | 是否循环 |
killOnComplete | boolean | 否 | false | 仅在动作不循环的前提下,完成后是否销毁物体 |
当我们的Sprite图每个动作之间的帧宽度不一样,需要用到TexturePacker导出的json文件
例如Sprite图包含walk和jump两个动作,两个动作的帧宽高是不一样的,但合并在一张图中,我们在导图时需要对walk和jump两个动作进行分组,并勾选关键帧名字去掉后缀名,以方便读帧。
此时,我们已经无法继续使用game.load.spritesheet,因为帧的宽高发生了变化,传固定宽高肯定是有问题的,我们需要换成一下写法:
1 | game.load.atlasJSONHash('sprite_animation', 'img/sprite_animation2.png', 'img/sprite_animation2.json'); |
1 | hero = game.add.sprite(game.world.centerX, game.world.centerY+500, 'sprite_animation', 'walk/01'); |
以下依然不改的情况下,动作会把Sprite图所有帧都依次读取完1
2hero.animations.add('walk');
hero.play('walk', 10, true);
而我们要把walk和jump两个动作分开,可以这么做:
1 | hero.animations.add('walk', ['walk/01', 'walk/02', 'walk/03', 'walk/04', 'walk/05', 'walk/06', 'walk/07', 'walk/08']); |
但是,这样每个帧都列出来会非常不方便,Phaser.Animation提供了生成帧序列的简便方法。
generateFrameNames(prefix, start, stop, suffix, zeroPad)
参数 | 类型 | 是否必选 | 默认值 | 说明 |
---|---|---|---|---|
prefix | string | 是 | 关键帧名的英文前缀部分,如walk/01,prefix就是walk/ | |
start | number | 是 | 动作开始帧,如果关键帧是walk/01到walk/10,start就是1 | |
stop | number | 是 | 动作结束帧,如果关键帧是walk/01到walk/10,stop就是10 | |
suffix | string | 否 | ‘’ | 关键帧后缀,如果关键帧是walk/01.png到walk/10.png,suffix就是.png |
zeroPad | number | 视序列位数而定 | ‘’ | 关键帧序列的计数位数,如walk/01到walk/10,zeroPad就是2 |
那么,walk和jump的动作可以这么写:
1 | hero.animations.add('walk', Phaser.Animation.generateFrameNames('walk/', 1, 8, '', 2)); |
以动作为主体的代码写法
另外,我们通常也需要对动作进行一些设置或监听,我们可以把以物体为主体的代码换成以动作为主体的方式来写:
1 | var walk = hero.animations.add('walk', Phaser.Animation.generateFrameNames('walk/', 1, 8, '', 2)); |
重要属性
参数 | 类型 | 说明 |
---|---|---|
currentFrame | string | 获取动作当前帧名 |
frame | number | 获取或设置动作此刻的帧 |
delay | number | 动作延迟播放的毫秒数 |
isPlaying | boolean | 是否在播放 |
isFinished | boolean | 是否已完成 |
paused | boolean | 获取或设置动作暂停状态 |
reversed | boolean | 动作反向 |
speed | number | 动作速度 |
onStart | Phaser.Signal | 监听动作开始时 |
onLoop | Phaser.Signal | 监听动作循环时 |
onComplete | Phaser.Signal | 监听动作完成时 |
onUpdate | Phaser.Signal | 监听动作在更新帧时 |
重要方法
play(frameRate, loop, killOnComplete) 播放动作
stop(resetFrame, dispatchComplete) 停止动作
参数 | 类型 | 是否必选 | 默认值 | 说明 |
---|---|---|---|---|
resetFrame | boolean | 否 | false | 是否重制到第一帧 |
dispatchComplete | boolean | 否 | false | 是否传递一个动作完成的信号 |
complete() 强制传递动作完成信号
destroy() 销毁动作
restart() 重新播放动作
reverse() 方向播动画
onResume(fn) 监听动作从暂停恢复播放
Homework
在上一节课作业的基础(动作素材):
1、给box添加一个简单的tween动画;
2、使用TexturePacker合并艾斯的walk和jump动作的帧;
3、创建艾斯,给它添加walk动作和jump动作,并播放walk动作。