0 Comments

使用JavaScript和Canvas写一个游戏框架(2)

发布于:2013-09-12  |   作者:广州网站建设  |   已聚集:人围观

GameObjectManager.js


  1.  
  2. /**  
  3.     管理游戏中所有对象的管理器  
  4.     @class  
  5. */  
  6. function GameObjectManager()  
  7. {  
  8.     /** 保存游戏中对象的数组  
  9.         @type Arary  
  10.     */  
  11.     this.gameObjects = new Array();  
  12.     /** 上一次帧被渲染的时间  
  13.         @type Date  
  14.     */  
  15.     this.lastFrame = new Date().getTime();  
  16.     /** x轴的全局滚动值  
  17.         @type Number  
  18.     */  
  19.     this.xScroll = 0;  
  20.     /** y轴的全局滚动值  
  21.         @type Number  
  22.     */  
  23.     this.yScroll = 0;  
  24.     /** 对ApplicationManager实例的引用  
  25.         @type ApplicationManager  
  26.     */  
  27.     this.applicationManager = null;  
  28.     /** 对画布元素的引用  
  29.         @type HTMLCanvasElement  
  30.     */  
  31.     this.canvas = null;  
  32.     /** 对画布元素2D上下文的引用  
  33.         @type CanvasRenderingContext2D  
  34.     */  
  35.     this.context2D = null;  
  36.     /** 对内存中用作后台缓冲区的画布的引用  
  37.         @type HTMLCanvasElement  
  38.     */  
  39.     this.backBuffer = null;  
  40.     /** 对后台缓冲画布的2D上下文的引用  
  41.         @type CanvasRenderingContext2D  
  42.     */  
  43.     this.backBufferContext2D = null;  
  44.  
  45.     /**  
  46.         初始化这个对象  
  47.         @return A reference to the initialised object  
  48.     */  
  49.     this.startupGameObjectManager = function()  
  50.     {  
  51.         // 设置引用this对象的全局指针  
  52.         g_GameObjectManager = this;  
  53.  
  54.         // 取得画布元素及其2D上下文的引用  
  55.         this.canvas = document.getElementById('canvas');  
  56.         thisthis.context2D = this.canvas.getContext('2d');  
  57.         this.backBuffer = document.createElement('canvas');  
  58.         thisthis.backBuffer.width = this.canvas.width;  
  59.         thisthis.backBuffer.height = this.canvas.height;  
  60.         thisthis.backBufferContext2D = this.backBuffer.getContext('2d');  
  61.  
  62.         // 创建一个新的ApplicationManager  
  63.         this.applicationManager = new ApplicationManager().startupApplicationManager();  
  64.  
  65.         // 使用setInterval来调用draw函数  
  66.         setInterval(function(){g_GameObjectManager.draw();}, SECONDS_BETWEEN_FRAMES);  
  67.  
  68.         return this;  
  69.     }  
  70.  
  71.     /**  
  72.         渲染循环  
  73.     */  
  74.     this.draw = function ()  
  75.     {  
  76.         // 计算从上一帧到现在的时间  
  77.         var thisFrame = new Date().getTime();  
  78.         var dt = (thisFrame - this.lastFrame)/1000;  
  79.         this.lastFrame = thisFrame;  
  80.  
  81.         // 清理绘制上下文  
  82.         this.backBufferContext2D.clearRect(0, 0, this.backBuffer.width, this.backBuffer.height);  
  83.         this.context2D.clearRect(0, 0, this.canvas.width, this.canvas.height);  
  84.  
  85.         // 首先更新所有游戏对象  
  86.         for (x in this.gameObjects)  
  87.         {  
  88.             if (this.gameObjects[x].update)  
  89.             {  
  90.                 this.gameObjects[x].update(dt, this.backBufferContext2D, this.xScroll, this.yScroll);  
  91.             }  
  92.         }  
  93.  
  94.         // 然后绘制所有游戏对象  
  95.         for (x in this.gameObjects)  
  96.         {  
  97.             if (this.gameObjects[x].draw)  
  98.             {  
  99.                 this.gameObjects[x].draw(dt, this.backBufferContext2D, this.xScroll, this.yScroll);  
  100.             }  
  101.         }  
  102.  
  103.         // 将后台缓冲复制到当前显示的画布  
  104.         this.context2D.drawImage(this.backBuffer, 0, 0);  
  105.     };  
  106.  
  107.     /**  
  108.         向gameObjects集合中添加一个GameObject  
  109.         @param gameObject The object to add  
  110.     */  
  111.     this.addGameObject = function(gameObject)  
  112.     {  
  113.         this.gameObjects.push(gameObject);  
  114.         this.gameObjects.sort(function(a,b){return a.zOrder - b.zOrder;})  
  115.     };  
  116.  
  117.     /**  
  118.         从gameObjects集合中删除一个GameObject  
  119.         @param gameObject The object to remove  
  120.     */  
  121.     this.removeGameObject = function(gameObject)  
  122.     {  
  123.         this.gameObjects.removeObject(gameObject);  
  124.     }  
  125. }  

首先看一看GameObjectManager类。GameObjectManager是一个引擎类,用于管理画布的绘制操作,还负责分派GameObject类(下一篇文章里介绍)的事件。

GameObjectManager类的startupGameObjectManager函数的代码如下:
广州网站建设,网站建设,广州网页设计,广州网站设计


  1.  
  2. /**  
  3.     初始化这个对象  
  4.     @return A reference to the initialised object  
  5. */  
  6. this.startupGameObjectManager = function()  
  7. {  
  8.     // 设置引用this对象的全局指针  
  9.     g_GameObjectManager = this;  
  10.  
  11.     // 取得画布元素及其2D上下文的引用  
  12.     this.canvas = document.getElementById('canvas');  
  13.     thisthis.context2D = this.canvas.getContext('2d');  
  14.     this.backBuffer = document.createElement('canvas');  
  15.     thisthis.backBuffer.width = this.canvas.width;  
  16.     thisthis.backBuffer.height = this.canvas.height;  
  17.     thisthis.backBufferContext2D = this.backBuffer.getContext('2d');  
  18.  
  19.     // 创建一个新的ApplicationManager  
  20.     this.applicationManager = new ApplicationManager().startupApplicationManager();  
  21.  
  22.     // 使用setInterval来调用draw函数  
  23.     setInterval(function(){g_GameObjectManager.draw();}, SECONDS_BETWEEN_FRAMES);  
  24.  
  25.     return this;  
  26. }  

前面已经说过,我们会把每个类的初始化工作放在startupClassName函数中来做。因此,GameObjectManager类将由startupGameObjectManager函数进行初始化。

而引用这个GameObjectManager实例的全局变量g_GameObjectManager经过重新赋值,指向了这个新实例。


  1. // 设置引用this对象的全局指针  
  2. g_GameObjectManager = this;  

对画布元素及其绘图上下文的引用也同样保存起来:


  1. // 取得画布元素及其2D上下文的引用  
  2. this.canvas = document.getElementById('canvas');  
  3. thisthis.context2D = this.canvas.getContext('2d');  

在前面的例子中,所有绘图操作都是直接在画布元素上完成的。这种风格的渲染一般称为单缓冲渲染。在此,我们要使用一种叫做双缓冲渲染的技术:任意游戏对象的所有绘制操作,都将在一个内存中的附加画布元素(后台缓冲)上完成,完成后再通过一次操作把它复制到网页上的画布元素(前台缓冲)。

双缓冲技术(http://www.brighthub.com/internet/web-development/articles/11012.aspx)通常用于减少画面抖动。我自己在测试的时候从没发现直接向画布元素上绘制有抖动现象,但我在网上的确听别人念叨过,使用单缓冲渲染会导致某些浏览器在渲染时发生抖动。

不管怎么说,双缓冲还是能够避免最终用户看到每个游戏对象在绘制过程中最后一帧的组合过程。在通过JavaScript执行某些复杂绘制操作时(例如透明度、反锯齿及可编程纹理),这种情况是完全可能发生的。

使用附加缓冲技术占用的内存非常少,多执行一次图像复制操作(把后台缓冲绘制到前台缓冲)导致的性能损失也可以忽略不计,可以说实现双缓冲系统没有什么缺点。

如果将在HTML页面中定义的画布元素作为前台缓冲,那就需要再创建一个画布来充当后台缓冲。为此,我们使用了document.createElement函数在内存里创建了一个画布元素,把它用作后台缓冲。
广州网站建设,网站建设,广州网页设计,广州网站设计


  1. this.backBuffer = document.createElement('canvas');  
  2. thisthis.backBuffer.width = this.canvas.width;  
  3. thisthis.backBuffer.height = this.canvas.height;  
  4. thisthis.backBufferContext2D = this.backBuffer.getContext('2d');  

接下来,我们创建了ApplicationManager类的一个新实例,并调用startupApplicationManager来初始化它。这个ApplicationManager类将在下一篇文章中介绍。


  1. // 创建一个新的ApplicationManager  
  2. this.applicationManager = new ApplicationManager().startupApplicationManager();  

最后,使用setInterval函数重复调用draw函数,这个函数是渲染循环的核心所在。


  1. // 使用setInterval来调用draw函数  
  2. setInterval(function(){g_GameObjectManager.draw();}, SECONDS_BETWEEN_FRAMES);  
标签:
飞机