Canvas 射击小游戏详解系列(六)

这篇将介绍页面动画跟项目设计实现。先会对动画做介绍,然后再总结项目中实现动画的设计。

动画

这里说的页面动画指的是js动画,使用requestAnimationFrame()实现。

实现动画效果的原理是把动画拆成一帧帧的画面,当达到每秒至少24帧时,可以给人造成流畅的视觉变化效果。也就算在实现动画效果时,要经历更新画面,清除画面,绘制画面三个部分。

代码1 每个循环的代码逻辑

1
2
3
4
5
6
7
var animate = function () {
/*
* 1.更新下一帧画面,比如移动
* 2.清除当前页面画面
* 3.绘制下一帧画面
*/
}

第一步是更新下一帧的画面,处理完一些画面元素的数据调整跟状态调整;第二步是清除当前页面画布,腾出画布空间;第三步绘制下一帧画面,刷新画布内容。

完成三步循环之后,还需要通过requestAnimationFrame()进入下一个循环周期。

另外,既然有动画就需要用暂停或者结束的情况,需要做状态的判断。判断状态转变成暂停或者结束的条件一般有监听事件,碰撞检测,位置范围,超时等等。一般在第一步前判断是否需要更新,调用requestAnimationFrame()前判断是否需要进入下一个循环周期。

最终,一个完整的动画设计就明了了。

代码2 完整的动画代码逻辑

1
2
3
4
5
6
7
8
9
var animate = function () {
/*
* 判断条件,选择暂停动画或继续动画
* 1.更新下一帧画面,比如移动
* 2.清除当前页面画面
* 3.绘制下一帧画面
* 判断条件,选择调用requestAnimationFrame(animate)或结束动画
*/
}

项目设计

项目中的动画主要是在GAME对象中。当玩家点击开始游戏后,会执行play()方法。这个方法会先设置游戏状态,然后重置关卡数据,创建跟初始化画布所有的元素,然后再调用animate()方法。

动画设计

判断

进入animate()方法后,先做判断。先判断暂停状态变量是否为真,是则直接在画布上渲染暂停的图标。但是不是真的停下动画,只是让每一帧的渲染都是一样而已。直到暂停状态变量修改成假,就可以给移动量赋值。

在项目中,判断前还需要对键盘对象做一次检测,确认玩家是否有出现键盘事件,再根据不同的键盘事件修改。

更新动画

完成判断,继续动画时,根据前面得到的移动量,传入到飞机,怪兽类的实例对象中修改坐标位置。调用碰撞测试的方法collision(),判断碰撞是否发生。

清除画布

参考上一篇,直接使用context.clearRect(0, 0, canvas.width, canvas.height)

绘制画面

完成清除之后,调用drawOject()方法。这个方法内部会遍历所有画布的对象,调用对象的绘制方法。

调用requestAnimationFrame(animate)

这里需要再加一个判断。因为在前面的更新动画中,只是判断了是否暂停,这里判断是否结束动画。前面绘制之后,可能结束的条件是怪兽已经被消灭完或怪兽已经到达底部,其他情况下继续动画,需要调用requestAnimationFrame(animate)

碰撞设计

碰撞设计主要是判断两个画布元素是否有重叠,其次是碰撞之后的状态跟动画效果的处理。这里主要讨论碰撞判断。

碰撞的判断到底为画布元素的边界是否有交叉。比如矩形的碰撞判断,为两个矩形的左边界与右边界,上边界与下边界的交叉判断。比如圆形,则是判断两个圆形圆心的距离是否小于两个圆形的半径之和。

如果是考虑矩形跟圆形,则需要判断矩形四个角跟圆形的位置关系及矩形中心到圆心的距离;

如果是考虑矩形缺角或圆形缺弧,则需要判断矩形各个角跟圆心及弧角的位置关系及矩形中心到圆心的距离。

如果是跟复杂的图形,则需要增加参考移动行为。比如项目中的怪兽跟子弹。不管怪兽的形状有多复杂,子弹的移动只有向上一个移动行为,所以主要的判断可以是子弹最上方的坐标是否进入怪兽边界内。

越复杂的碰撞判断,越要参考除了图形之外,其他的因素,比如移动行为,形状变化等等。

以上就是关于项目中动画部分的总结。

系列结语

整个项目其实整体的重点是动画的流程,怎么实现完整的动画每一帧的代码逻辑。然后拆分成两个问题,怎么渲染游戏元素,怎么设计游戏对象。

这个只是前端的一部分,还需要继续学习。加油!