0
点赞
收藏
分享

微信扫一扫

微信小游戏开发入门 3/5

Sprite类目前没有实现动画支持,所以有一个新类Animation,继承自Sprite,额外提供了动画功能。所谓动画功能,实际上就是根据每一帧调用,更换图片,动画本质就是一连串图片的播放。

Animation.js

代码如下,

export default class Animation extends Sprite {

constructor(imgSrc, width, height) {

super(imgSrc, width, height)

// 当前动画是否播放中
this.isPlaying = false
// 动画是否需要循环播放
this.loop = false
// 每一帧的时间间隔
this.interval = 1000 / 60
// 帧定时器,这里采用
this[__.timer] = null
// 当前播放的帧
this.index = -1
// 总帧数
this.count = 0
// 帧图片集合
this.imgList = []

/**
* 推入到全局动画池里面
* 便于全局绘图的时候遍历和绘制当前动画帧
*/
databus.animations.push(this)
}

initFrames函数

在创建enemy对象时会被调用,这个游戏里面动画效果只有敌机被击落时才发生。

/**
* 初始化帧动画的所有帧
* 为了简单,只支持一个帧动画
*/
initFrames(imgList) {

imgList.forEach((imgSrc) => {
const img = new Image()
img.src = imgSrc
this.imgList.push(img)
})
this.count = imgList.length
}

playAnimation函数

负责调用触发动画的准备工作,比如让该对象显示的静态敌机图片无需渲染,后续渲染都是渲染爆炸效果的图片,而爆炸图片的变化又有这里启动了一个定时器来控制。

// 播放预定的帧动画
playAnimation(index = 0, loop = false) {

// 动画播放的时候精灵图不再展示,播放帧动画的具体帧
this.visible = false



this.isPlaying = true

this.loop = loop



this.index = index



if (this.interval > 0 && this.count) {

this[__.timer] = setInterval(

this.frameLoop.bind(this),
this.interval
)
}
}

aniRender函数

渲染动画的方法为aniRender函数,还记得渲染精灵的图片方法么?是Sprite.drawToCanvas。因为visiable=false,所以精灵渲染就会消失,只剩动画渲染。

// 将播放中的帧绘制到canvas上
aniRender(ctx) {

ctx.drawImage(
this.imgList[this.index],
this.x,
this.y,
this.width * 1.2,

this.height * 1.2

)
}


frameLoop函数

动画效果里面提到一个定时器,setInterval调用的是frameLoop方法,功能简单,就是改变动画图片的索引值index,当达到最后一张时,如果设置的是循环动画播放则重置为第一张,否则终止定时器。

// 帧遍历
frameLoop() {
this.index++
if (this.index > this.count - 1) {
if (this.loop) {
this.index = 0
} else {
this.index--
this.stop()
}
}
}

在这个项目中都是非循环动画,所以可以正常工作,但是如果是循环动画,定时器会在对象回收到pool之后,还​一直调用,因为并没有其他调用的地方来判断何时停止定时器,所以完善的话,应该在回收时注意清理。

我觉得实际上这个函数应该由update函数调用,因为update函数是有全局的定时器控制的,时间间隔也一样,不用再额外创建定时器了。

Enemy.js

enemy继承了Animation。飞机需要速度,所以需要设置速度,飞机的移动时基于速度来计算坐标位置。初始化动画爆炸图片,接下来所有功能基本就靠Animation来实现了,无需自己实现。

这也是这个游戏中唯一用到动画功能的地方,其他功能都只是移动。因为基本功能基本都靠Animation和Sprite实现,所以,在具体敌人飞机上只要调用相关功能即可。

import Animation from '../base/animation'

import DataBus from '../databus'



const ENEMY_IMG_SRC = 'images/enemy.png'

const ENEMY_WIDTH = 60

const ENEMY_HEIGHT = 60



const __ = {

speed: Symbol('speed')

}


const databus = new DataBus()



function rnd(start, end) {

return Math.floor(Math.random() * (end - start) + start)

}


export default class Enemy extends Animation {

constructor() {

super(ENEMY_IMG_SRC, ENEMY_WIDTH, ENEMY_HEIGHT)



this.initExplosionAnimation()
}


init(speed) {

this.x = rnd(0, window.innerWidth - ENEMY_WIDTH)

this.y = -this.height



this[__.speed] = speed



this.visible = true

}


// 预定义爆炸的帧动画
initExplosionAnimation() {

const frames = []



const EXPLO_IMG_PREFIX = 'images/explosion'

const EXPLO_FRAME_COUNT = 19



for (let i = 0; i < EXPLO_FRAME_COUNT; i++) {

frames.push(`${EXPLO_IMG_PREFIX + (i + 1)}.png`)

}


this.initFrames(frames)
}


// 每一帧更新子弹位置
update() {

this.y += this[__.speed]



// 对象回收
if (this.y > window.innerHeight + this.height) databus.removeEnemey(this)

}
}
举报

相关推荐

0 条评论