canvas教程

使用JavaScript和Canvas开发游戏(三)

字号+ 作者:H5之家 来源:H5之家 2017-07-16 16:01 我要评论( )

原文作者:Matthew Casperson 原文链接:Game Development with JavaScript and the Canvas element 1、认识一下Canvas 2、在Canvas上绘图 3、通过Canvas元素实现

原文作者:Matthew Casperson
原文链接: 

1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、写一个游戏框架(一)
5、写一个游戏框架(二)
6、通过Canvas实现视差滚动
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单

4、写一个游戏框架(一)


在知道了如何使用画布元素之后,接下来我教大家写一个框架,有了这个框架,我们就可以把它作为基础来创建游戏。在这第一部分,我们会介绍前两个文件/类。

 

编写代码之前,我们先来看一看随后几篇文章将致力于创建的示例Demo。表面上看起来,这个Demo跟第二篇文章里的那个没啥区别,但如果你看看后台(查看网页源代码)就会发现,为了更方便地创建这个最终效果,一个凝聚不少心血的基础框架已经写好了。

下面我们要介绍的JavaScript代码使用面向对象的方式来编写。对于没有编写过多少JavaScript代码的人来说,恐怕第一眼看到它们会觉得有点奇怪。如果你真的不太熟悉JavaScript的面向对象编程,建议通过Mozilla Developer Network的这个教程来补补课。这篇教程里解释了我们稍后会用到的一些编程技术。

从设计思想上来看,这个框架可以分成两部分:与底层的2D引擎交互的类(用于操作画布、控制渲染循环、处理输入等的代码)和用来创建对象以便构成游戏的类。前者可以归为引擎类,后者可以归为应用类。由于应用类要构建于引擎类之上,所以我们需要先来创建引擎类。

Main.js

如果你研究了前面例子中的代码,就会发现Main.js文件中包含了不少代码。

/** 每秒多少帧 @type Number */ var FPS = 30; /** 两帧间间隔的秒数 @type Number */ var SECONDS_BETWEEN_FRAMES = 1 / FPS; /** GameObjectManager 实例的全局引用 @type GameObjectManager */ var g_GameObjectManager = null; /** 应用中用到的图像 @type Image */ var g_image = new Image(); g_image.src = "jsplatformer3-smiley.jpg"; // 将应用的入口设置为init函数 window.onload = init; /** 应用的入口 */ function init() { new GameObjectManager().startupGameObjectManager(); }

首先是定义全局变量的代码。然后,跟以前一样,当页面加载完毕后立即运行init函数。在init函数里,创建GameObjectManager类的实例。

这里在GameObjectManager类的实例上调用了startupGameObjectManager函数。这篇文章以及后面的几篇文章还将多次提到几个命名上具有startupClassName形式的函数。这些函数实际上充当了各自类的构造函数,这样做有两个原因。

首先,JavaScript不支持函数重载(至少不容易实现)。如果你想让一个类有多个构造函数,那么这就成了问题。而通过把构造工作分配给另一组函数(如startupClassName1、startupClassName2),就可以比较容易地定义构造类的不同方式了。

第二个原因(很大程度上也是个人的问题)是我经常会在构造函数中引用尚未定义的变量。这可能是我使用C++、Java和C#这些语言落下的毛病,在这些语言里,类变量在源代码中的位置对其在构造函数中的可见性没有影响。拿下面这个C#类为例:

class Test { public void Test() {this.a = 5;} public int a; }

这些代码是合乎语法的,可以正常工作。下面再看看JavaScript中一个相同的例子:

function Test() { this.a = 5; var a; }

这段代码的问题在于,局部变量a在我们把数值5赋给它的时候还不存在。只有运行到var a;这一行,变量a才存在。尽管这个例子有点故意编排的意味,但的确能够说明我所遇到的问题。通过把类的创建放到一个类似startupClassName这样的函数中完成,并且在构造函数中定义(但不初始化)局部变量,然后当我在这些构建函数中引用相应的局部变量时,就能够确保它们一定是存在的。

GameObjectManager.js /** 管理游戏中所有对象的管理器 @class */ function GameObjectManager() { /** 保存游戏中对象的数组 @type Arary */ this.gameObjects = new Array(); /** 上一次帧被渲染的时间 @type Date */ this.lastFrame = new Date().getTime(); /** x轴的全局滚动值 @type Number */ this.xScroll = 0; /** y轴的全局滚动值 @type Number */ this.yScroll = 0; /** 对ApplicationManager实例的引用 @type ApplicationManager */ this.applicationManager = null; /** 对画布元素的引用 @type HTMLCanvasElement */ this.canvas = null; /** 对画布元素2D上下文的引用 @type CanvasRenderingContext2D */ this.context2D = null; /** 对内存中用作后台缓冲区的画布的引用 @type HTMLCanvasElement */ this.backBuffer = null; /** 对后台缓冲画布的2D上下文的引用 @type CanvasRenderingContext2D */ this.backBufferContext2D = null; /** 初始化这个对象 @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; } /** 渲染循环 */ 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); }; /** 向gameObjects集合中添加一个GameObject @param gameObject The object to add */ this.addGameObject = function(gameObject) { this.gameObjects.push(gameObject); this.gameObjects.sort(function(a,b){return a.zOrder - b.zOrder;}) }; /** 从gameObjects集合中删除一个GameObject @param gameObject The object to remove */ this.removeGameObject = function(gameObject) { this.gameObjects.removeObject(gameObject); } }

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

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • JavaScript html5 canvas绘制时钟效果

    JavaScript html5 canvas绘制时钟效果

    2017-07-16 16:03

  • HTML5 Canvas 梦幻的文字飞扬动画教程

    HTML5 Canvas 梦幻的文字飞扬动画教程

    2017-07-16 15:06

  • HTML5 Canvas 时钟

    HTML5 Canvas 时钟

    2017-07-16 10:03

  • h5 canvas 星空

    h5 canvas 星空

    2017-07-16 09:04

网友点评
'