在chrome浏览器的断网页面,按空格键或者向上键会出现一个小恐龙跑酷小游戏,这个2D小游戏在设计上精致小巧,在代码上也只有三千多行,思路清晰严谨,很有学习价值
demo
在非断网情况下,可以通过chrome://dino 进行访问,源代码在source面板中无法显示,可以前往这里下载。在这篇文章中异名会梳理2D游戏的制作思路,主要包括游戏的mainloop主循环和实例的update更新、帧图的动态绘制和切换、帧率的控制、游戏对象的运动控制、碰撞检测的实现等
游戏循环
循环是游戏的心跳,是一个定时回调,每隔一段时间去更新游戏的逻辑,比如处理用户的交互,更新游戏的状态,绘制动画等等
mainloop() {
this.clearCanvas() // 清除画布
// 处理逻辑....
window.requestAnimationFrame(this.mainloop.bind(this));
}
在rAF没出现之前,大家使用setTimeout和setInterval来触发视觉的变化,但是这两个api在时间的精准控制上有缺陷。因为「定时器属于异步任务,它必须等到同步任务执行完毕之后,以及异步队列里面的任务清空之后才轮到自己执行,它的实际执行时机一般都比设定的时间晚」,这就说明了它不能精准地按照一定的时间间隔去执行。还有一点就是「定时器的调用间隔和屏幕绘制频率不一致」,显示器的频率一般都默认是60Hz(1s绘制60次),每次绘制的时间差是16.7ms(1000/60≈16.7),因为定时器的调用间隔和屏幕频率不一致,所以下面这种情况就一定会出现
settimeout
红色叉叉那里就丢帧了,下面通过一个更清晰的例子来说明:
这也是为什么以前大家把setInterval的间隔设置为1000/60的原因,但是这本质上是硬件的差异,只要换个硬件,定时器的执行步调和屏幕的刷新步调不一致就一定会产生丢帧。这也就是rAF的最大优势,它是「由系统来决定回调函数的执行时机,系统每次绘制之前会主动调用 rAF 中的回调函数」,它能够确保回调函数是按照系统的绘制频率来调用,无论是60Hz还是50Hz,只要画面刷新就会调用回调函数,它就解决了步调统一以及回调频率可靠这两个问题。但是因为是系统主动调用,所以需要我们自己去做时间管理,raf的回调第一个参数是一个时间戳,但是在实践上一般我们自己计时
mainloop() {