在上一篇《Chrome自带恐龙小游戏的源码研究(四)》中实现了障碍物的绘制及移动,从这一篇开始主要研究恐龙的绘制及一系列键盘动作的实现。

会眨眼睛的恐龙

  在游戏开始前的待机界面,如果仔细观察会发现恐龙会时不时地眨眼睛。这是通过交替绘制这两个图像实现的:

aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAALIAAABmCAYAAABvJctRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAkbSURBVHhe7dBRDuuoEkXRnuOd/zjuZ79nKW2Vt7YhGGMKK5HWD8HFOfXP/3///vy8gB7+/KxGD39+VqOHaf39+zc1y7wS65SJZf7Qw7SsXCaWeSXWKRPL/KGHaVm5TCzzSqxTJpb5Qw/TsnKZWOaVWKdMLPOHHqZhZaI/f/5MxTzWITPmJ+v8JOaxDh96mAaLkJV/EvNYh8yYn6zzk5jHOnzoYRosQlb+ScxjHTJjfrLOT2Ie6/Chh2mwCFn5JzGPdciM+ck6P4l5rMOHHqbBIlY2E+a1Tpkwr3XKhHlDl2OxbBjcymXCvNYpE+a1Tpkwb+hyLJYNg1u5iD+7MxLzWqdMmNc6ZcK8ocuxWDYMbuUi/uzOSMxrnTJhXuuUCfOGLsdi2TC4lYv4szsjMa91yoR5rVMmzBu6HItlw+BWLuLP7ozEvNYpE+a1Tpkwb+hyLJYNg1u5TJjXOmXCvNYpE+YNXY7FsmFwK5cJ81qnTJjXOmXCvKHLsVg2DG7lMmFe65QJ81qnTJg3dDkWy4bBrVwmzGudMmFe65QJ84Yux2LZMLiVy4R5rVMmzGudMmHe0OVYLBsGt3KZMK91yoR5rVMmzBu6HItlw+BWLhPmtU6ZMK91yoR5Q5djsWwY3MplwrzWKRPmtU6ZMG/ociyWDYNbuUyY1zplwrzWKRPmDV2OxbJhcCuXCfNap0yY1zplwryhy7FYNgxu5TJhXuuUCfNap0yYN3Q5FsuGwa1cJsxrnTJhXuuUCfOGLsdi2TC4levB+XezTpkwr+2oB+ffLXQ5FsuGwW1ZPTj/btYpE+a1HfXg/LuFLsdi2TC4LasH59/NOmXCvLajHpx/t9DlWCwbBrdl9eD8u1mnTJjXdtSD8+8WupSLEe/fzd6MbFlPYh7rUMLvyb65k70ZWecnMY91OHE84CDi/bvZm5GVfxLzWIcSfk/2zZ3szcg6P4l5rMOJ4wEHEe/fzd6MrPyTmMc6lPB7sm/uZG9G1vlJzGMdThwPOIh4/272ZmTln8Q81qGE35N9cyd7M7LOT2Ie66D44W2DKzj3DPP0sjd6WLeI92t5bMYVnHuGeXrZGz2sW7Tfix9tasFs2BWce4Z5etkbPaxbxPu1PDbjCs49wzy97I0e1i3a78WPNrVgNuwKzj3DPL3sjR7WLeL9Wh6bcQXnnmGeXvZGD+sW7ffiR5taMBt2BeeeYZ5e9kYP6xbxfi2PzbiCc88wTy97o4d1i/Z78aNNLZgNK+H3Z/juKPZ2iXVqwXm1PDajhN+f4buj2Nsl1qnFPicO3dSC2bASfn+G745ib5dYpxacV8tjM0r4/Rm+O4q9XWKdWuxz4tBNLZgNK+H3Z/juKPZ2iXVqwXm1PDajhN+f4buj2Nsl1qnFPicO3dSC2bASfn+G745ib5dYpxacV8tjM0r4/Rm+O4q9XWKdWuxz4tCNhYt4v5XNzIR5bXktOM/ejHi/lc3MhHltZy32OXHoxh6PeL+VzcyEeW15LTjP3ox4v5XNzIR5bWct9jlx6MYej3i/lc3MhHlteS04z96MeL+VzcyEeW1nLfY5cejGHo94v5XNzIR5bXktOM/ejHi/lc3MhHltZy32OXHoxh4v4fdk38xUy8f/bXktOI/v1fB7sm9mquXj/7azFvucOHTDh2v4Pdk3M9Xy8X9bXgvO43s1/J7sm5lq+fi/7azFPicO3fDhGn5P9s1MtXz835bXgvP4Xg2/J/tmplo+/m87a7HPiUM3fLiG35N9M1MtH/+35bXgPL5Xw+/Jvpmplo//285a7HPi0A0ffhv2rbHlteA8y/Qm7FtjO2uxz4lDNxbuTdi3xpbXgvMs05uwb43trMU+Jw7dWLg3Yd8aW14LzrNMb8K+NbazFvucOHRj4d6EfWtseS04zzK9CfvW2M5a7HPi0I2FexP2teXcie9ZpjdhX9vJnfZ34qMbC/cm7GvLuRPfs0xvwr62kzvt78RHNxbuTdjXlnMnvmeZ3oR9bSd32t+Jj24s3Juwry3nTnzPMr0J+9pO7hTe8T/+Y2FXxn7sPxrft4wrYz/bwSDHAwaxsCtjP/Yfje9bxpWxn+1gkOMBg1jYlbAP+z6NeSzzStjHOj/keMBgFn4l7MO+T2Mey7wS9rHOD9HDHYNamcyY3zrOxHzWITPmt44P0cMdg1qZzJjfOs7EfNYhM+a3jg/Rwx2DWpnMmN86zsR81iEz5reOD9HDHYNamcyY3zrOxHzWITPmt44P0cNTDG7lZmI+sk6ZMK91nIn5yDo9RA9PMbiVnYn5yDplwrzWcSbmI+v0ED08xeBWdibmI+uUCfNax5mYj6zTQ/TwFINb2ZmYj6xTJsxrHWdiPrJOD9HDr7GIlR+J75NlXgn72A5G4vtkmSfRw6+xmC1jJL5Plnkl7GM7GInvk2WeRA+/xmK2jJH4PlnmlbCP7WAkvk+WeRI9/BqL2TJG4vtkmVfCPraDkfg+WeZJ9PBrVi6y5ZS0fs/7ZJlXYp0i20lJ6/e8T5Z5Ej38mpWLbDklrd/zPlnmlVinyHZS0vo975NlnkQPv2blIltOSev3vE+WeSXWKbKdlLR+z/tkmSfRw69ZuciWU9L6Pe+TZV6JdYpsJyWt3/M+WeZJ9PAyFrXlRLzfOo//8/u3YV/ug3i/dR7/5/eJ6OFlLM7FEO+3zuP//P5t2Jf7IN5vncf/+X0iengZi3MxxPut8/g/v38b9uU+iPdb5/F/fp+IHl7G4lwM8X7rPP7P79+GfbkP4v3Wefyf3yeih5exeO9ieL933urYt3cfvN87byI9vIzFexfD+73zVse+vfvg/d55E+nhZSzeuxje7523Ovbt3Qfv986bSA8vY/HexfB+77zVsW/vPni/d95EengZi3MxxPuj562O/WwHEe+PnjeRHl7GoraMiPdHz1sd+9kOIt4fPW8iPbyMRW0ZEe+Pnrc69rMdRLw/et5EengZi9oyIt4fPW917Gc7iHh/9LyJ9PAyFm01et7qrGOL0fMm0sPLrGyL0fNWZx1bjJ43kR5eZmVbjJ63OuvYYvS8ifTwMivbYvS81VnHFqPnTaSHPz+r0cOfn9Xo4c/PavTw52c1evjzs5B//v0f0zA5kqU1TugAAAAASUVORK5CYII=" alt="" width="178" height="102" />

可以通过一张图片来了解这个过程:

  为实现图片的切换,需要一个计时器timer,并且需要知道两张图片切换的时间间隔msPerFrame。当计时器timer的时间大于切换的时间间隔msPerFrame时,将图片切换到下一张,到达最后一张时又从第一张开始,如此反复。下面是实现代码:

 Trex.config = {
BLINK_TIMING:3000, //眨眼间隔
WIDTH: 44, //站立时宽度
WIDTH_DUCK: 59, //闪避时宽度
HEIGHT: 47, //站立时高度
BOTTOM_PAD: 10,
MIN_JUMP_HEIGHT: 30 //最小起跳高度
};
//状态
Trex.status = {
CRASHED: 'CRASHED', //与障碍物发生碰撞
DUCKING: 'DUCKING', //闪避
JUMPING: 'JUMPING', //跳跃
RUNNING: 'RUNNING', //跑动
WAITING: 'WAITING' //待机
};
//元数据(metadata),记录各个状态的动画帧和帧率
Trex.animFrames = {
WAITING: {//待机状态
frames: [44, 0],//动画帧x坐标在44和0之间切换,由于在雪碧图中的y坐标是0所以不用记录
msPerFrame: 1000 / 3 //一秒3帧
},
RUNNING: {
frames: [88, 132],
msPerFrame: 1000 / 12
},
CRASHED: {
frames: [220],
msPerFrame: 1000 / 60
},
JUMPING: {
frames: [0],
msPerFrame: 1000 / 60
},
DUCKING: {
frames: [262, 321],
msPerFrame: 1000 / 8
}
}; function Trex(canvas,spritePos){
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.spritePos = spritePos; //在雪碧图中的位置
this.xPos = 0; //在画布中的x坐标
this.yPos = 0; //在画布中的y坐标
this.groundYPos = 0; //初始化地面的高度
this.currentFrame = 0; //初始化动画帧
this.currentAnimFrames = []; //记录当前状态的动画帧
this.blinkDelay = 0; //眨眼延迟(随机)
this.animStartTime = 0; //动画开始的时间
this.timer = 0; //计时器
this.msPerFrame = 1000 / FPS; //默认帧率
this.config = Trex.config; //拷贝一个配置的副本方便以后使用
this.jumpVelocity = 0; //跳跃的初始速度 this.status = Trex.status.WAITING; //初始化默认状态为待机状态 //为各种状态建立标识
this.jumping = false; //角色是否处于跳跃中
this.ducking = false; //角色是否处于闪避中
this.reachedMinHeight = false; //是否到达最小跳跃高度
this.speedDrop = false; //是否加速降落
this.jumpCount = 0; //跳跃次数 this.init();
}

  首先还是和以往一样,对Trex这个构造函数进行基本的配置,然后在原型链中添加操作方法:

 Trex.prototype = {
init:function() {
this.groundYPos = DEFAULT_HEIGHT - this.config.HEIGHT - this.config.BOTTOM_PAD;
this.yPos = this.groundYPos;
//计算出最小起跳高度
this.minJumpHeight = this.groundYPos - this.config.MIN_JUMP_HEIGHT; this.draw(0,0);
this.update(0,Trex.status.WAITING);
},
setBlinkDelay:function () {
//设置随机眨眼间隔时间
this.blinkDelay = Math.ceil(Math.random() * Trex.config.BLINK_TIMING);
},
update:function (deltaTime,opt_status) {
this.timer += deltaTime; if(opt_status) {
this.status = opt_status;
this.currentFrame = 0;
//得到对应状态的帧率 e.g. WAITING 1000ms / 3fps = 333ms/fps
this.msPerFrame = Trex.animFrames[opt_status].msPerFrame;
//对应状态的动画帧 e.g. WAITING [44,0]
this.currentAnimFrames = Trex.animFrames[opt_status].frames; if(opt_status === Trex.status.WAITING) {
//开始计y时
this.animStartTime = getTimeStamp();
//设置延时
this.setBlinkDelay();
}
} //计时器超过一帧的运行时间,切换到下一帧
if (this.timer >= this.msPerFrame) {
this.currentFrame = this.currentFrame === this.currentAnimFrames.length - 1 ?
0 : this.currentFrame + 1;
this.timer = 0; //重置计时器
} //待机状态
if(this.status === Trex.status.WAITING) {
//执行眨眼动作
this.blink(getTimeStamp());
}
},
blink:function (time) {
var deltaTime = time - this.animStartTime; if(deltaTime >= this.blinkDelay) {
this.draw(this.currentAnimFrames[this.currentFrame],0); if (this.currentFrame === 1) {//0闭眼 1睁眼
//设置新的眨眼间隔时间
this.setBlinkDelay();
this.animStartTime = time;
}
}
},
draw:function (x,y) {
var sourceX = x;
var sourceY = y;
var sourceWidth = this.ducking && this.status != Trex.status.CRASHED ?
this.config.WIDTH_DUCK : this.config.WIDTH;
var sourceHeight = this.config.HEIGHT;
sourceX += this.spritePos.x;
sourceY += this.spritePos.y; this.ctx.drawImage(imgSprite,
sourceX, sourceY,
sourceWidth, sourceHeight,
this.xPos, this.yPos,
this.config.WIDTH, this.config.HEIGHT);
}
};

  先来看update方法中的这段代码:

 if (this.timer >= this.msPerFrame) {
this.currentFrame = this.currentFrame === this.currentAnimFrames.length - 1 ?
0 : this.currentFrame + 1;
this.timer = 0;
}

  这段代码实现了两个帧之间的切换,但如果只是单纯地以相同时间间隔来切换两张图片,那么得到的效果是不正确的,会出现频繁眨眼的情况。而实际情况是,闭眼只是一瞬间,睁开眼睛的时间则比较长。Chrome开发人员非常巧妙地解决了这个问题:

 blink:function (time) {
var deltaTime = time - this.animStartTime; if(deltaTime >= this.blinkDelay) {
this.draw(this.currentAnimFrames[this.currentFrame],0); if (this.currentFrame === 1) {//0闭眼 1睁眼
//设置新的眨眼间隔时间
this.setBlinkDelay();
this.animStartTime = time;
}
}
}

  只要计时器没有超过blinkDelay就不绘制新的图片,这样图片就会停留在上一次绘制的状态,恐龙此时是睁着眼睛的。当时间超过了blinkDelay,即执行眨眼的时间到了,这时会绘制this.currentFrame这一帧。如果这一帧是0(闭眼),由于之前设置了this.timer >= this.msPerFrame时会切换帧,当时间再次超过blinkDelay时,这时就会绘制帧1(睁眼),我们看到的效果就是眼睛闭上只有一瞬然后立刻睁开了。 如果当前帧是1(睁眼),重新设置blinkDelay,于是在deltaTime没有超过重新设置blinkDelay的情况下,都不会绘制新图片(始终保持在帧1(睁眼)),这样我们看到的效果就是睁眼的时间稍长。

下面是运行后的效果:

 

// this.bumpThreshold ? this.dimensions.WIDTH : 0;
},
draw:function() {
this.ctx.drawImage(imgSprite,
this.sourceXPos[0], this.spritePos.y,
this.dimensions.WIDTH, this.dimensions.HEIGHT,
this.xPos[0],this.yPos,
this.dimensions.WIDTH,this.dimensions.HEIGHT);

this.ctx.drawImage(imgSprite,
this.sourceXPos[1], this.spritePos.y,
this.dimensions.WIDTH, this.dimensions.HEIGHT,
this.xPos[1],this.yPos,
this.dimensions.WIDTH,this.dimensions.HEIGHT);

},
updateXPos:function(pos,increment) {
var line1 = pos,
line2 = pos === 0 ? 1 : 0;

this.xPos[line1] -= increment;
this.xPos[line2] = this.xPos[line1] + this.dimensions.WIDTH;

if(this.xPos[line1] = this.msPerFrame) {
this.currentFrame = this.currentFrame === this.currentAnimFrames.length - 1 ?
0 : this.currentFrame + 1;
this.timer = 0;
}

//待机状态
if(this.status === Trex.status.WAITING) {
//执行眨眼动作
this.blink(getTimeStamp());
}
},
blink:function (time) {
var deltaTime = time - this.animStartTime;

if(deltaTime >= this.blinkDelay) {
this.draw(this.currentAnimFrames[this.currentFrame],0);

if (this.currentFrame === 1) {//0闭眼 1开眼
//设置新的眨眼间隔时间
this.setBlinkDelay();
this.animStartTime = time;
}
}
},
draw:function (x,y) {
var sourceX = x;
var sourceY = y;
var sourceWidth = this.ducking && this.status != Trex.status.CRASHED ?
this.config.WIDTH_DUCK : this.config.WIDTH;
var sourceHeight = this.config.HEIGHT;
sourceX += this.spritePos.x;
sourceY += this.spritePos.y;

this.ctx.drawImage(imgSprite,
sourceX, sourceY,
sourceWidth, sourceHeight,
this.xPos, this.yPos,
this.config.WIDTH, this.config.HEIGHT);
}
};

window.onload = function () {
var h = new HorizonLine(c,spriteDefinition.HORIZON);
var trex = new Trex(c,spriteDefinition.TREX);
var startTime = 0;
var deltaTime;
var speed = 3;

(function draw(time) {
time = time || 0;
deltaTime = time - startTime;
trex.update(deltaTime);
startTime = time;
requestAnimationFrame(draw,c);
})();
};
// ]]>

Chrome自带恐龙小游戏的源码研究(五)的更多相关文章

  1. Chrome自带恐龙小游戏的源码研究(六)

    在上一篇<Chrome自带恐龙小游戏的源码研究(五)>中实现了眨眼睛的恐龙,这一篇主要研究恐龙的跳跃. 恐龙的跳跃 游戏通过敲击键盘的Spacebar或者Up来实现恐龙的跳跃.先用一张图来 ...

  2. Chrome自带恐龙小游戏的源码研究(一)

    目录 Chrome自带恐龙小游戏的源码研究(一)——绘制地面 Chrome自带恐龙小游戏的源码研究(二)——绘制云朵 Chrome自带恐龙小游戏的源码研究(三)——昼夜交替 Chrome自带恐龙小游戏 ...

  3. Chrome自带恐龙小游戏的源码研究(七)

    在上一篇<Chrome自带恐龙小游戏的源码研究(六)>中研究了恐龙的跳跃过程,这一篇研究恐龙与障碍物之间的碰撞检测. 碰撞盒子 游戏中采用的是矩形(非旋转矩形)碰撞.这类碰撞优点是计算比较 ...

  4. Chrome自带恐龙小游戏的源码研究(完)

    在上一篇<Chrome自带恐龙小游戏的源码研究(七)>中研究了恐龙与障碍物的碰撞检测,这一篇主要研究组成游戏的其它要素. 游戏分数记录 如图所示,分数及最高分记录显示在游戏界面的右上角,每 ...

  5. Chrome自带恐龙小游戏的源码研究(四)

    在上一篇<Chrome自带恐龙小游戏的源码研究(三)>中实现了让游戏昼夜交替,这一篇主要研究如何绘制障碍物. 障碍物有两种:仙人掌和翼龙.仙人掌有大小两种类型,可以同时并列多个:翼龙按高. ...

  6. Chrome自带恐龙小游戏的源码研究(三)

    在上一篇<Chrome自带恐龙小游戏的源码研究(二)>中实现了云朵的绘制和移动,这一篇主要研究如何让游戏实现昼夜交替. 昼夜交替的效果主要是通过样式来完成,但改变样式的时机则由脚本控制. ...

  7. Chrome自带恐龙小游戏的源码研究(二)

    在上一篇<Chrome自带恐龙小游戏的源码研究(一)>中实现了地面的绘制和运动,这一篇主要研究云朵的绘制. 云朵的绘制通过Cloud构造函数完成.Cloud实现代码如下: Cloud.co ...

  8. WinFom中经典小游戏(含源码)

    最近整理了若干经典的小游戏,无聊时可以打发时间.程序本身不大,练手非常不错,主要是GDI编程,主界面地址如下图所示 源码下载方式 1,关注微信公众号:小特工作室(也可直接扫描签名处二维码) 2,发送: ...

  9. github下载下来的C#控制台小游戏[含源码]

    早就听说了github是世界最大的源码库,但自己却不是很懂,今天去研究了下,注册了一个帐号,然后在上面搜索了一下C# game,然后发现有许多的游戏. 随意地选择了一个,感觉比较简单,于是就下载了下来 ...

随机推荐

  1. ofbiz数据库表结构设计(3)- 订单ORDER

    对于订单来说,主要的表就是ORDER_HEADER和ORDER_ITEM.ORDER_HEADER就是所谓的订单头,一条记录代表一条订单. ORDER_PAYMENT_PREFERENCE是订单的支付 ...

  2. hdu 5930 GCD 线段树上二分/ 强行合并维护信息

    from NOIP2016模拟题28 题目大意 n个点的序列,权值\(<=10^6\) q个操作 1.单点修改 2.求所有区间gcd中,不同数个数 分析 1.以一个点为端点,向左或向右的gcd种 ...

  3. hihocoder #1407 : 后缀数组二·重复旋律2

    #1407 : 后缀数组二·重复旋律2 Time Limit:5000ms Case Time Limit:1000ms Memory Limit:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢 ...

  4. Sql常用日期格式

    原文发布时间为:2010-09-16 -- 来源于本人的百度文章 [由搬家工具导入] SQL Server中文版的默认的日期字段datetime格式是yyyy-mm-dd Thh:mm:ss.mmm ...

  5. Func<T1, T2, TResult> Delegate 系统Func委托类型

    原文发布时间为:2011-03-25 -- 来源于本人的百度文章 [由搬家工具导入] http://msdn.microsoft.com/en-us/library/bb534647%28v=VS.1 ...

  6. win10易升卸载

  7. 牛客网 Wannafly挑战赛8 B.LBJX的三角形

    B-LBJX的三角形 链接:https://www.nowcoder.com/acm/contest/57/B来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒空间限制:C/C++ 32768K, ...

  8. python 设计模式之门面模式

    facade:建筑物的表面 门面模式是一个软件工程设计模式,主要用于面向对象编程. 一个门面可以看作是为大段代码提供简单接口的对象,就像类库.   门面模式被归入建筑设计模式.门面模式隐藏系统内部的细 ...

  9. Visual Studio开启SSL的支持

    前提: 请确保已经安装了IIS Express 具体操作: 1.web项目->[右键]->[使用IIS Express]转换工程的Web服务器. 2.点击web项目,按[ctrl]+[w] ...

  10. windows service 调试

    在程序运行入口调试 #if DEBUG Debugger.Launch(); #endif