HTML5 2D游戏开发入门——结合实例,一步一步开发2D小游戏 – HTML5开发 – 游戏开发者社区

HTML5 2D游戏开发入门——结合实例,一步一步开发2D小游戏

第一节 静态图片的绘制

在开始开发游戏之前, 首先要了解一下如何利用HTML5来绘制静态的图片, 这是开发2D游戏的基础。在HTML5 Canvas中实现绘图非常简单,大致流程如下: 创建一个canvas,并设置大小

2. 取得canvas的context

3. 加载图片

4. 通过context绘制图片

实现的代码:

  1. // 创建canvas,并初始化(我们也可以直接以标签形式写在页面中,然后通过id等方式取得canvas)var canvas=document.createElement(”canvas”);canvas.width=600;canvas.height=400;document.body.appendChild(canvas);

复制代码

  1. // 取得2d绘图上下文var context= canvas.getContext(”2d”);

  2. // 加载图片,加载后在context上进行绘制。(图片是异步加载,所以在onload事件里进行绘制)var image = new Image();image.src = ”./res/bg。png”;image.onload=function(event){var loadedImg=event.target;// 将加载后的图片,绘制在画布坐标[dx,dy]处,也就是图片的左上角坐标为[dx,dy]var dx=0,dy=0 ;context.drawImage(loadedImg,dx,dy);};

复制代码

上面的例子只是加载并绘制了一张图片。 而开发游戏时,我们往往会需要多张的图片。

为了更好的加载并管理这些图片,通常需要编写一个简单的资源加载的工具函数。

引入该函数后的代码如下:

  1. //一个简单的图片加载函数function loadImage(srcList,callback){/* 代码略,详见 /step1/step1-2.html 内的loadImage函数 */}

  2. ImgCache=loadImage( [{  id : “player”,url : “../res/player.png”},{  id : “bg”,url : “../res/bg.png”}],startDemo );

  3. function startDemo(){

  4. // 绘制背景var dx=0, dy=0 ;context.drawImage(ImgCache[“bg”],dx,dy);

  5. //绘制站在地上的player,坐标为200,284var sx=0, sy=60, sw=50, sh=60;var dx=400, dy=284, dw=50, dh=60;context.drawImage(ImgCache[“player”], sx, sy, sw, sh, dx, dy, dw, dh );

  6. }

复制代码

实现上述需求的代码如下

  1. // 一些简单的初始化,var FPS=30;var sleep=Math.floor(1000/FPS);var img=ImgCache[“player”];

  2. //初始坐标var x=0, y=284;//移动速度speedY<0,向上移动。var speedX = 65/1000 , speedY=-45/1000 ;//x/y坐标的最大值和最小值,可用来限定移动范围。var minX=0, maxX=500, minY=0, maxY=284;

  3. //主循环var mainLoop=setInterval(function(){//距上一次执行相隔的时间。(时间变化量),目前可近似看作sleep。var deltaTime=sleep;

  4. //每次循环,改变一下绘制的坐标.x=x+speedX*deltaTime; //向右移动y=y+speedY*deltaTime; //向上移动,,坐标y减小,这点和数学中的坐标系不同.

  5. //限定移动范围x=Math.max(minX,Math.min(x,maxX));y=Math.max(minY,Math.min(y,maxY));

  6. //使用清空画布的方式,清空之前绘制的图片//context.clearRect(0,0,canvas.width,canvas.height);

  7. //使用背景覆盖的方式,清空之前绘制的图片context.drawImage(ImgCache[“bg”],0,0);

  8. //在新位置上绘制图片var sx=0, sy=60, sw=50, sh=60;context.drawImage(img, sx, sy, sw, sh, Math.floor(x), Math.floor(y), sw, sh );

  9. },sleep);

  10. 要实现一个Animation,首先要定义Frame。 看一个简单的示例:

  11. var frame={img : ImgCache[“player”] ,x : 0,y : 60,w : 50,h : 60,duration : 100}

复制代码

其中img属性 ,就是这帧要显示的图片—-之前已经加入到 ImgCache中的那张马里奥的“帧集合”图片。一个Frame只对应这张图片中的一个区域,接下来的4个属性定义了该区域:X,y 区域左上角坐 标。 W,h区域的宽度和高度。duration属性定义了该帧在播放时显示的时间,单位毫秒。上面的定义的帧 最后对应的图像就是下图中蓝框中的部分。

把若干个这样的frame放在一起,然后依次进行更新和绘制 就可以产生动画。一个Animation对象的结构大致如下:

  1. var animation = {frames : null ,frameCount : -1 ,currentFrame : null ,currentFrameIndex : -1 ,currentFramePlayed : -1 ,img : ImgCache[“player”]}

复制代码

不难发现,这和第二节中,移动图片时的流程非常相像。为了更好的实现这个流程, 也为后面更复杂的场景打下一个坚实的技术,我们可以引入面向对象的思想,对animation相关代码进行重构,封装出如下的Animation类。

  1. // Animation类。// cfg为Object类型的参数集, 其属性会覆盖Animation原型中定义的同名属性。function Animation(cfg){for (var attr in cfg ){this[attr]=cfg[attr];}}

  2. Animation。prototype={constructor :Animation ,

  3. // Animation 包含的Frame,类型:数组frames : null,// 包含的Frame数目frameCount : -1 ,// 所使用的图片id(在ImgCache中存放的Key), 字符串类型。img : null,currentFrame : null ,currentFrameIndex : -1 ,currentFramePlayed : -1 ,

  4. // 初始化Animationinit : function(){// 根据id取得Image对象this。img = ImgCache[this。img]||this。img;

  5. this。frames=this。frames||[];this。frameCount = this。frames。length;

  6. // 缺省从第0帧播放This.setFrame(0);},

  7. //设置当前帧setFrame : function(index){this.currentFrameIndex=index;this.currentFrame=this。frames[index];this.currentFramePlayed=0;},

  8. // 更新Animation状态。 deltaTime表示时间的变化量。update : function(deltaTime){//判断当前Frame是否已经播放完成,if (this。currentFramePlayed>=this。currentFrame。duration){//播放下一帧

  9. if (this.currentFrameIndex >= this。frameCount-1){//当前是最后一帧,则播放第0帧This.currentFrameIndex=0;}else{//播放下一帧this。currentFrameIndex++;}//设置当前帧信息This.setFrame(this。currentFrameIndex);

  10. }else{//增加当前帧的已播放时间。This.currentFramePlayed += deltaTime;}},

  11. //绘制Animationdraw : function(gc,x,y){var f=this.currentFrame;gc.drawImage (this.img, f.x , f.y, f.w, f.h , x, y, f.w, f.h );}};

复制代码

其中稍微复杂点的是类的update方法。update方法中的 deltaTime 参数 表示距上一次执行update方法到这次执行所流逝的时间。在本例中,它约等于sleep ,所以在这里我们就使用 sleep。实现动画效果的代码如下:

  1. // 一些简单的初始化,var FPS=30;var sleep=Math.floor(1000/FPS);

  2. //初始坐标var x=0, y=284;

  3. // 创建一个Animation对象var animation = new Animation({img : ”player” ,//该动画由3帧构成,对应图片中的第一行。frames : [{x : 0, y : 0, w : 50, h : 60, duration : 100},{x : 50, y : 0, w : 50, h : 60, duration : 100},} );// 初始化Animationanimation。init();

  4. //主循环var mainLoop=setInterval(function(){

  5. //距上一次执行相隔的时间。(时间变化量), 目前可近似看作sleep。var deltaTime=sleep;

  6. // 更新Animation状态Animation.update(deltaTime);

  7. //使用背景覆盖的方式 清空之前绘制的图片Context.drawImage(ImgCache[“bg”],0,0);

  8. //绘制AnimationAnimation.draw(context, x,y);

  9. },sleep);

复制代码

运行这个示例 可以看到一个原地踏步的马里奥。下面我们结合移动图片的示 例, 来让这个原地踏步的马里奥真正的走动起来。实现Anmation 和 移动的主循环可以合并成一个。 事实上,在游戏开发中通常也都是合并的。无论 有多少个动画 游戏的主线程都只有一个。代码如下:

  1. // 一些简单的初始化,var FPS=30;var sleep=Math.floor(1000/FPS);

  2. //初始坐标var x=0, y=284;//移动速度。speedY<0,向上移动。var speedX = 65/1000 , speedY=-45/1000 ;//x/y坐标的最大值和最小值, 可用来限定移动范围。var minX=0, maxX=500, minY=0, maxY=284;

  3. // 创建一个Animation对象var animation = new Animation({img : ”player” ,//该动画由3帧构成frames : [{x : 0, y : 0, w : 50, h : 60, duration : 100},{x : 50, y : 0, w : 50, h : 60, duration : 100},} );// 初始化AnimationAnimation.init();

  4. //主循环var mainLoop=setInterval(function(){

  5. //距上一次执行相隔的时间。(时间变化量), 目前可近似看作sleep。var deltaTime=sleep;

  6. //每次循环,改变一下绘制的坐标。x=x+speedX*deltaTime; //向右移动y=y+speedY*deltaTime; //向上移动, 坐标y减小,这点和数学中的坐标系不同。

  7. //限定移动范围x=Math.max(minX,Math.min(x,maxX));y=Math.max(minY,Math.min(y,maxY));// 更新Animation状态Animation.update(deltaTime);

  8. //使用背景覆盖的方式,清空之前绘制的图片Context.drawImage(ImgCache[“bg”],0,0);

  9. //绘制Animationanimation。draw(context, x,y);

  10. },sleep);

复制代码

显然,把一个人物的各种动作和属性,放到一个对象里,进行统一的管理,是一个不错的主意。而这个对象,就是我们常说的精灵(Sprite)。 下面我们引入一些面向对象的思想来编写一个Sprite类:

  1. function Sprite(cfg){for (var attr in cfg){this[attr]=cfg[attr];}}

  2. Sprite.prototype={constructor :Sprite ,

  3. //精灵的坐标x : 0,y : 0,//精灵的速度speedX : 0,speedY : 0,

  4. //精灵的坐标区间minX : 0,maxX : 9999,minY : 0,maxY : 9999,

  5. //精灵包含的所有 Animation 集合. Object类型, 数据存放方式为” id : animation ”.anims : null,//默认的Animation的Id , string类型defaultAnimId : null,

  6. //当前的Animation.currentAnim : null,

  7. //初始化方法init : function(){//初始化所有Animtionfor (var animId in this.anims){var anim=this.anims[animId];anim.id=animId;anim.init();}//设置当前Animationthis.setAnim(this.defaultAnimId);},

  8. //设置当前Animation, 参数为Animation的id, String类型setAnim : function(animId){this.currentAnim=this.anims[animId];//重置Animation状态(设置为第0帧)this.currentAnim.setFrame(0);},

  9. // 更新精灵当前状态。update : function(deltaTime){//每次循环,改变一下绘制的坐标。this.x=this.x+this.speedX*deltaTime; //向右移动this.y=this.y+this.speedY*deltaTime; //向上移动,坐标y减小,这点和数学中的坐标系不同。

  10. //限定移动范围this.x=Math.max(this.minX,Math.min(this.x,this.maxX));this.y=Math.max(this.minY,Math.min(this.y,this.maxY));

  11. if (this.currentAnim){this.currentAnim.update(deltaTime);}

  12. },

  13. //绘制精灵draw : function(gc){if (this.currentAnim){this.currentAnim.draw(gc, this.x, this.y);}}

  14. };

复制代码

一个Sprite对象可以是游戏里的一个人物,也可以是一个道具(门、食 物),也可以是一颗子弹、一个景物。具体把把游戏中的哪些物体定义成Sprite,要视游戏而定。在本文示例中, 主角马里奥,以及敌人都定义为 Sprite。前面定义好了Sprite类, 下面看一下如何创建我们的主角马里奥。

  1. var sprite = new Sprite({

  2. //初始坐标x : 0,y : 284,

  3. //移动速度. speedY=0,垂直方向不移动.speedX : 90/1000,speedY : 0,

  4. //x/y坐标的最大值和最小值, 可用来限定移动范围.minX : 0,maxX : 500,minY : 0,maxY : 284,

  5. defaultAnimId : ”walk-right”,

  6. //定义两个Animation,向左走 和 向右走.anims : {“walk-left” : new Animation({img : ”player” ,frames : [{x : 0, y : 60, w : 50, h : 60, duration : 100},{x : 50, y : 60, w : 50, h : 60, duration : 100},{x : 100, y : 60, w : 50, h : 60, duration : 100}} ),

  7. “walk-right” : new Animation({img : ”player” ,frames : [{x : 0, y : 0, w : 50, h : 60, duration : 100},{x : 50, y : 0, w : 50, h : 60, duration : 100},{x : 100, y : 0, w : 50, h : 60, duration : 100}} )}

  8. });

复制代码

前面曾用来表示Animation位置和运动方式的几个变量:x、y、 speedX、speedY等,因为都是马里奥的基本属性,所以都设置到Sprite对象上。这个Sprite由两个Animation构成,id分别 为 walk-left 和 walk-right。 想要显示哪个动画,就执行 sprite.setAnim(”动画id”);下面我们改变一下动画的逻辑:马里奥在画面上左右来回走动(垂直方向不动),那么动画的主循环变为:

  1. // 定义sprite走路速度的绝对值,和默认的speedXSprite.walkSpeed=90/1000;Sprite.speedX=sprite.walkSpeed;

  2. // 初始化spriteSprite.init();

  3. //主循环var mainLoop=setInterval(function(){

  4. //距上一次执行相隔的时间。(时间变化量),目前可近似看作sleep。var deltaTime=sleep;

  5. // 更新sprite状态Sprite.update(deltaTime);

  6. //如果做到最右侧,则折向左走,如果走到最左侧,则向右走。//通过改变speedX的正负,来改变移动的方向。if (sprite.x>=sprite.maxX){sprite.speedX=-sprite.walkSpeed;sprite.setAnim(”walk-left”);}else if (sprite.x<=sprite.minX){Sprite.speedX=sprite.walkSpeed;Sprite.setAnim(”walk-right”);}

  7. //使用背景覆盖的方式 清空之前绘制的图片Context.drawImage(ImgCache[“bg”],0.0);

  8. //绘制spriteSprite.draw(context);

  9. },sleep);

  10. 现在代码变得越来越多, 为了便于维护和阅读, 从本节开始将js代码分文件存放。输入与控制一个可以走来走去的马里奥,显然不能称作是一款游戏, 这里就来说一说,如何实现玩家对游戏角色的控制。对于通过键盘操控的游戏,我们可以通过监听浏览器的keydown/keyup事件来进行检测。代码如下:

  11. var Key={A : 65,W : 87,D : 68}//用来记录按键状态var KeyState={};

  12. function initEvent(){//监听整个document的keydown,keyup事件,为了保证能够监听到,监听方式使用Capture

  13. document.addEventListener(”keydown”,function(evt){//按下某按键,该键状态为trueKeyState[evt.keyCode]=true;},true);document.addEventListener(”keyup”,function(evt){//放开下某按键,该键状态为trueKeyState[evt.keyCode]=false;},true);

复制代码

}通过上面的代码记录按键状态后,就可以在游戏的主循环里根据不同按键状态来执行不同的操作。前面,我们通过判断马里奥的位置来改变运动方向和Animation,下 面来看一看如何通过键盘来控制马里奥左右移动以及跳跃。左右方向是匀速直线运动,所以的原理和前述的类似, 通过改变speedX的正负来改变方向。但是 跳跃相对复杂些,跳跃在垂直方向上属于一种上抛运动, 要引入起跳初速度和重力加速度。由于处理玩家输入以及改变马里奥运动状态的代码比较多,且有相关 性,所以给马里奥的精灵增加一个handleInput方法:

  1. handleInput : function(){// 读取按键状态,如果A键为按下状态,则向左移动,如果D键为按下状态,则向右移动。var left= KeyState[Key.A];var right= KeyState[Key.D];var up= KeyState[Key.W];

  2. //取得人物当前面对的方向var dirX=this.currentAnim.id.split(”-”)[1];

  3. // 判断是否落地if (this.y==this.maxY){this.jumping=false;this.speedY=0;}

  4. //如果按了上,且当前不是跳跃中,那么开始跳跃。跳跃和走路使用同一个Animation。if (up && !this.jumping){this.jumping=true;this.speedY=this.jumpSpeed;this.setAnim(”walk-”+dirX);}

  5. if (left && right || !left && !right){// 如果左右都没有按或者都按了,那么水平方向速度为0,不移动this.speedX=0;

  6. //如果不是在跳跃中,那么进入站立状态,站立时面对的方向根据之前的速度来决定if (!this.jumping){this.setAnim(”stand-”+dirX);}

  7. }else if(left && this.speedX!=-this.walkSpeed){//如果按下了左,且当前不是向左走,则设置为向左走this.setAnim(”walk-left”);this.speedX=-sprite.walkSpeed;}else if(right && this.speedX!=this.walkSpeed){//如果按下了右,且当前不是向右走,则设置为向右走this.setAnim(”walk-right”);this.speedX=sprite.walkSpeed;}

  8. }

复制代码

这个handleInput方法还是比较好理解的,但是何时调用它是一个 值得注意的问题。我们通过按键,改变的是移动的速度,而不是直接改变坐标。 如果要让改变的速度生效,这个速度必须要持续一段时间。而这一段时间通常就是 主循环两次迭代之间的间隔。也就是说,我们第n次迭代时改变的速度,要让它在第n+1次生效才有意义。

所以我们在迭代的最后调用 handleInput 方法。// 定义走路速度的绝对值, 默认speedX

Sprite.walkSpeed=200/1000;

Sprite.speedX=0;//定义跳跃初速度,垂直加速度, 默认speedY

Sprite.jumpSpeed=-700/1000;

Sprite.acceY=1。0/1000;

Sprite.speedY=0;

//默认情况下向右站立。

Sprite.defaultAnimId=”stand-right”;// 初始化sprite

Sprite.init();//主循环

var mainLoop=setInterval(function(){//距上一次执行相隔的时间。(时间变化量), 目前可近似看作sleep。

var deltaTime=sleep;

// 更新sprite状态

sprite。update(deltaTime);//使用背景覆盖的方式 清空之前绘制的图片

Context.drawImage(ImgCache[“bg”],0,0);//绘制sprite

Sprite.draw(context);//处理输入,当前输入,影响下一次迭代。

Sprite.handleInput();},sleep);

游戏主控类文示例中,除了主角马里奥之外,还有5个来回移动的敌人。他们也是精 灵。随着精灵的增加和代码的进一步复杂话,我们需要再次重构,封装出游戏总的控制类:Game 。Game类中包含 多个Sprite,在每次迭代时,由 Game负责所有精灵的状态更新和绘制。同时游戏的canvas context、全局的基本属性(如宽高、FPS)、启动函数、主循环等也都包含在 Game里。代码如下:

有了这个主控类之后,创建游戏的流程如下:

1. 创建Animation对象;

2. 创建Sprite对象,并加入相应的Animation;

3. 创建Game对象,并把Sprite加入Game;

4. 初始化Game对象,同时初始化内部的Sprite,以及Sprite内的Animation;

5. 开始游戏。

当游戏中 产生新的精灵(如出现新敌人), 旧的精灵消失(被消灭的敌人)时, 只要对 Game中的sprites集合进行操作即可。

下面看一下重构后的启动代码,简单了许多:

  1. //声明全局game对象var game;

  2. // Demo的启动函数function startDemo(){

  3. //创建game对象game=new Game({FPS : 30,width : 600,height : 400,sprites : [ ]

  4. });

  5. //将必要的精灵加入game的精灵列表里//加入马里奥game.sprites.push(createPlayer());

  6. //初始化gamegame.init();

  7. //开始gamegame.start();

  8. }

  9. }

复制代码

敌人与碰撞检测

有了主控类,向游戏中添加敌人就变得容易了许多。下面试着向游戏中加入5个敌人,由于敌人的外观和属性基本相同,不同的就是初始的坐标,和移动的速度,所以可以将创建敌人的过程封装成一个函数。

  1. function createEnemy(){

  2. var r=genRandom(0,1);

  3. var cfg = {img : ”enemy”,

  4. x : r?500:0,y : 294,

  5. //x/y坐标的最大值和最小值,可用来限定移动范围。minX : 0,maxX : 500,minY : 0,maxY : 294,

  6. handleInput : function(){var s=genRandom(-4,4);var moveSpeed=(150+s*10)/1000;this.speedX=this.speedX||moveSpeed;if (this.x<=this.minX){this.x=this.minX;this.speedX= moveSpeed;}else if (this.x>=this.maxX){this.x=this.maxX;this.speedX=-moveSpeed;}},

  7. defaultAnimId : ”move”,anims : {

  8. “move” : new Animation({img : ”enemy” ,frames : [{x : 0, y : 0, w : 50, h : 50, duration : 100 },{x : 50, y : 0, w : 50, h : 50, duration : 100  },{x : 100, y : 0, w : 50, h : 50, duration : 100  },{x : 150, y : 0, w : 50, h : 50, duration : 100  }})}

  9. };return new Sprite(cfg) ;}

复制代码

在游戏初始化之前,将精灵加入到Game主控类中:

  1. //将必要的精灵加入game的精灵列表里//加入马里奥game.sprites.push(createPlayer());//加入五个敌人for(var i=0;i<5;i++){game.sprites.push(createEnemy());}

  2. //初始化gamegame.init();

  3. //开始game

复制代码

game.start();现在运行游戏后,就可以看到一个马里奥和一群来回移动的敌人了。有了主角 和敌人,下一步要做的就是对两者进行碰撞检测, 判断主角和敌人是否有接触,如果接触到了便Game Over。对于简单的2D游戏,通常只需取 Sprite当前Frame的外框,作为碰撞区域,进行简单的矩形碰撞检测即可。当上图中红色和蓝色矩形有交集时,则认为马里奥和敌人它们发生了,检测函数如下://返回true为两矩形发生碰撞。

  1. checkIntersect : function(rect1, rect2){return !(rect1.x1>rect2.x2 || rect1.y1>rect2.y2 || rect1.x2<rect2.x1 ||  rect1.y2<rect2.y1);}

复制代码

其中x1,y1为矩形左上角坐标, x2,y2为矩形右下角坐标。

下面为Sprite类添加两个方法:

  1. //取得精灵的碰撞区域,getCollRect : function(){if (this.currentAnim){var f=this.currentAnim.currentFrame;return {x1 : this.x,y1 : this.y,x2 : this.x+f.w,y2 : this.y+f.h}}

  2. },

复制代码

  1. //判断是否和另外一个精灵碰撞collideWidthOther : function(sprite2){var rect1=this.getCollideRect();var rect2=sprite2.getCollideRect();

  2. return rect1 && rect2 && !(rect1.x1>rect2.x2 || rect1.y1>rect2.y2 || rect1.x2<rect2.x1 ||  rect1.y2<rect2.y1);}

复制代码

现在我们就可以使用emeny.collideWidthOther(player)来判断敌人是否和主角发生了碰撞, 如果碰撞了就提示“Game Over”。

为Game类加入碰撞检方法:

  1. //碰撞检测,返回true 则发生了主角和敌人的碰撞。checkCollide : function(){//本示例中,主角为第一个精灵。var player=this.sprites[0];for (var i=1,len=this.sprites.length;i<len;i++){var sprite=this.sprites;var coll=sprite.collideWidthOther(player);if (coll){return coll;}}return false;},

复制代码

在主循环那恰当的位置进行碰撞检测:

  1. //主循环中要执行的操作run : function(deltaTime){

  2. //碰撞检测var coll=this.checkCollide();if (coll){//如果发生敌人和玩家的碰撞,则结束游戏。clearInterval(this.mainLoop);alert(”Game Over”);return;}

  3. this.update(deltaTime);this.clear(deltaTime);this.draw(deltaTime);

复制代码

//处理输入,当前输入,影响下一次迭代。

this.handleInput();,

现在我们就可以通过键盘上的A/D/W键来控制人物进行移动和躲避敌人了。(要先关闭输入法)。

记录分数

现在,已经完成了游戏核心部分的开发,下面加入对分数的记录和显示,这里的分数就是在游戏中坚持的时间。首先在页面中加入一个简单的浮动div, 用来显示分数。

  1. <div id=”statebar” style=”font-size:24px; position:absolute; top:10px; left:280px;” >Time : <span id=”timeCount”></span></div>

  2. 然后在游戏主循环中,更新这个div的显示。修改Game类的start 和run方法如下:

  3. start : function(){var Me=this;

  4. // 记录游戏开始时间Me.startTime=Date.now();

  5. //主循环this.mainLoop=setInterval(function(){//距上一次执行相隔的时间(时间变化量),目前可近似看作sleep。var deltaTime=Me.sleep;Me.run(deltaTime);},Me.sleep);

  6. },

  7. //主循环中要执行的操作run : function(deltaTime){

  8. //显示游戏时间var palyedTime = Date.now()-this.startTime;$(”timeCount”).innerHTML=palyedTime;

  9. //碰撞检测var coll=this.checkCollide();if (coll){//如果发生敌人和玩家的碰撞,则结束游戏.clearInterval(this.mainLoop);alert(”Game OVer.n Your score : ”+palyedTime);return;}

  10. this.update(deltaTime);this.clear(deltaTime);this.draw(deltaTime);

  11. //处理输入,当前输入,影响下一次迭代。this.handleInput();

  12. },

复制代码

原文链接:http://www.html5so.com/forum.php?mod=viewthread&tid=2615&extra=page%3D3

来源URL:http://bbs.9ria.com/thread-186581-1-1.html