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

会眨眼睛的恐龙

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

aaarticlea/png;base64,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" 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. [ARC082F] Sandglass(线段树)

    Description 有一个沙漏由两个上下相通玻璃球 \(A\) 和 \(B\) 构成,这两个玻璃球都含有一定量的沙子,我们暂且假定 \(AB\) 中位于上方的玻璃球的为 \(U\),下方的玻璃球为 ...

  2. ASP.NET的最新安全漏洞Important: ASP.NET Security Vulnerability

    原文发布时间为:2010-09-20 -- 来源于本人的百度文章 [由搬家工具导入] 原文:http://weblogs.asp.net/scottgu/archive/2010/09/18/impo ...

  3. practical system design with mef & mef[ trans from arup.codeplex.com/]

    Practical System Design using MEF MVVM RX MOQ Unit Tests in WPF Posted on May 21, 2015 by Arup Baner ...

  4. hdu 3980 Paint Chain 组合游戏 SG函数

    题目链接 题意 有一个\(n\)个珠子的环,两人轮流给环上的珠子涂色.规定每次涂色必须涂连续的\(m\)颗珠子,无法继续操作的人输.问先手能否赢. 思路 参考 转化 第一个人取完之后就变成了一条链,现 ...

  5. mysql 连接远程服务器

    想要在本地连接远程服务器上的mysql, 需要在远程服务器的mysql配置里面,修改一下访问权限 mysql的配置里面,默认只能本地访问,在服务器上,修改/etc/mysql/my.cnf文件找到这一 ...

  6. Web安全-XSS-SQL注入-CSRF

    一.XSS 跨站脚本攻击(Cross Site Scripting): 1.指攻击者在网页中嵌入客户端脚本(例如JavaScript), 当用户浏览此网页时,脚本就会在用户的浏览器上执行,从而达到攻击 ...

  7. bytearray和file的后端上传方式

    public static String readAndUpload(String serverpath,String imgid) { if(serverpath==null){ serverpat ...

  8. Netty内存池

    参考资料:http://blog.csdn.net/youaremoon/article/details/47910971 主要思想:buddy allocation,jemalloc

  9. 在C#用进程打开cmd与直接手动打开cmd是不一样的

    网上找不着答案,想了一下觉得可能是不同的用户,用set看了一下环境变量果然是不同用户,要改一下 按以下方法改: http://www.cnblogs.com/babycool/p/3569183.ht ...

  10. ACM的奇计淫巧_扩栈C++/G++

    C++ #pragma comment(linker, "/STACK:102400000,102400000") G++ << ; // 256MB char *p ...