GameObjectManager类的startupGameObjectManager函数的代码如下:
/** 初始化这个对象 @return A reference to the initialised object */ this.startupGameObjectManager = function() { // 设置引用this对象的全局指针 g_GameObjectManager = this; // 取得画布元素及其2D上下文的引用 this.canvas = document.getElementById('canvas'); this.context2D = this.canvas.getContext('2d'); this.backBuffer = document.createElement('canvas'); this.backBuffer.width = this.canvas.width; this.backBuffer.height = this.canvas.height; this.backBufferContext2D = this.backBuffer.getContext('2d'); // 创建一个新的ApplicationManager this.applicationManager = new ApplicationManager().startupApplicationManager(); // 使用setInterval来调用draw函数 setInterval(function(){g_GameObjectManager.draw();}, SECONDS_BETWEEN_FRAMES); return this; }前面已经说过,我们会把每个类的初始化工作放在startupClassName函数中来做。因此,GameObjectManager类将由startupGameObjectManager函数进行初始化。
而引用这个GameObjectManager实例的全局变量g_GameObjectManager经过重新赋值,指向了这个新实例。
// 设置引用this对象的全局指针 g_GameObjectManager = this;
对画布元素及其绘图上下文的引用也同样保存起来:
// 取得画布元素及其2D上下文的引用 this.canvas = document.getElementById('canvas'); this.context2D = this.canvas.getContext('2d');在前面的例子中,所有绘图操作都是直接在画布元素上完成的。这种风格的渲染一般称为单缓冲渲染。在此,我们要使用一种叫做双缓冲渲染的技术:任意游戏对象的所有绘制操作,都将在一个内存中的附加画布元素(后台缓冲)上完成,完成后再通过一次操作把它复制到网页上的画布元素(前台缓冲)。
双缓冲技术()通常用于减少画面抖动。我自己在测试的时候从没发现直接向画布元素上绘制有抖动现象,但我在网上的确听别人念叨过,使用单缓冲渲染会导致某些浏览器在渲染时发生抖动。
不管怎么说,双缓冲还是能够避免最终用户看到每个游戏对象在绘制过程中最后一帧的组合过程。在通过JavaScript执行某些复杂绘制操作时(例如透明度、反锯齿及可编程纹理),这种情况是完全可能发生的。
使用附加缓冲技术占用的内存非常少,多执行一次图像复制操作(把后台缓冲绘制到前台缓冲)导致的性能损失也可以忽略不计,可以说实现双缓冲系统没有什么缺点。
如果将在HTML页面中定义的画布元素作为前台缓冲,那就需要再创建一个画布来充当后台缓冲。为此,我们使用了document.createElement函数在内存里创建了一个画布元素,把它用作后台缓冲。
this.backBuffer = document.createElement('canvas'); this.backBuffer.width = this.canvas.width; this.backBuffer.height = this.canvas.height; this.backBufferContext2D = this.backBuffer.getContext('2d');
接下来,我们创建了ApplicationManager类的一个新实例,并调用startupApplicationManager来初始化它。这个ApplicationManager类将在下一篇文章中介绍。
// 创建一个新的ApplicationManager this.applicationManager = new ApplicationManager().startupApplicationManager();
最后,使用setInterval函数重复调用draw函数,这个函数是渲染循环的核心所在。
// 使用setInterval来调用draw函数 setInterval(function(){g_GameObjectManager.draw();}, SECONDS_BETWEEN_FRAMES);下面来看一看draw函数。
/** 渲染循环 */ this.draw = function () { // 计算从上一帧到现在的时间 var thisFrame = new Date().getTime(); var dt = (thisFrame - this.lastFrame)/1000; this.lastFrame = thisFrame; // 清理绘制上下文 this.backBufferContext2D.clearRect(0, 0, this.backBuffer.width, this.backBuffer.height); this.context2D.clearRect(0, 0, this.canvas.width, this.canvas.height); // 首先更新所有游戏对象 for (x in this.gameObjects) { if (this.gameObjects[x].update) { this.gameObjects[x].update(dt, this.backBufferContext2D, this.xScroll, this.yScroll); } } // 然后绘制所有游戏对象 for (x in this.gameObjects) { if (this.gameObjects[x].draw) { this.gameObjects[x].draw(dt, this.backBufferContext2D, this.xScroll, this.yScroll); } } // 将后台缓冲复制到当前显示的画布 this.context2D.drawImage(this.backBuffer, 0, 0); };