热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Chrome小恐龙游戏源码探讨八奔驰的小恐龙

文章首发于我的GitHub博客媒介上一篇文章:《Chrome小恐龙游戏源码探讨七—日夜形式交替》完成了游戏日夜形式的交替,这一篇文章中,将完成:1、小恐龙的绘制2、键盘对小恐龙的掌

文章首发于我的
GitHub 博客

媒介

上一篇文章:《Chrome 小恐龙游戏源码探讨七 — 日夜形式交替》完成了游戏日夜形式的交替,这一篇文章中,将完成:1、小恐龙的绘制 2、键盘对小恐龙的掌握 3、页面失焦后,从新聚焦会重置小恐龙的状况

绘制静态的小恐龙

定义小恐龙类 Trex

/**
* 小恐龙类
* @param {HTMLCanvasElement} canvas 画布
* @param {Object} spritePos 图片在雪碧图中的坐标
*/
function Trex(canvas, spritePos) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.spritePos = spritePos;
this.xPos = 0;
this.yPos = 0;
this.groundYPos = 0; // 小恐龙在地面上时的 y 坐标
this.currentFrame = 0; // 当前的动画帧
this.currentAnimFrames = []; // 存储当前状况的动画帧在雪碧图中的 x 坐标
this.blinkDelay = 0; // 眨眼间隔的时候(随 机)
this.blinkCount = 0; // 眨眼次数
this.animStartTime = 0; // 小恐龙眨眼动画最先时候
this.timer = 0; // 计时器
this.msPerFrame = 1000 / FPS; // 帧率
this.status = Trex.status.WAITING; // 当前的状况
this.cOnfig= Trex.config;
this.jumping = false; // 是不是腾跃
this.ducking = false; // 是不是闪避(俯身)
this.jumpVelocity = 0; // 腾跃的速率
this.reachedMinHeight = false; // 是不是到达最低高度
this.speedDrop = false; // 是不是加快着落
this.jumpCount = 0; // 腾跃的次数
this.jumpspotX = 0; // 腾跃点的 x 坐标
this.init();
}

相干的设置参数:

Trex.cOnfig= {
GRAVITY: 0.6, // 引力
WIDTH: 44, // 站立时的宽度
HEIGHT: 47,
WIDTH_DUCK: 59, // 俯身时的宽度
HEIGHT_DUCK: 25,
MAX_JUMP_HEIGHT: 30, // 最大腾跃高度
MIN_JUMP_HEIGHT: 30, // 最小腾跃高度
SPRITE_WIDTH: 262, // 站立的小恐龙在雪碧图中的总宽度
DROP_VELOCITY: -5, // 着落的速率
INITIAL_JUMP_VELOCITY: -10, // 初始腾跃速率
SPEED_DROP_COEFFICIENT: 3, // 着落时的加快系数(越大着落的越快)
INTRO_DURATION: 1500, // 收场动画的时候
START_X_POS: 50, // 收场动画完毕后,小恐龙在 canvas 上的 x 坐标
};
Trex.BLINK_TIMING = 7000; // 眨眼最大间隔的时候
// 小恐龙的状况
Trex.status = {
CRASHED: 'CRASHED', // 撞到障碍物
DUCKING: 'DUCKING', // 正在闪避(俯身)
JUMPING: 'JUMPING', // 正在腾跃
RUNNING: 'RUNNING', // 正在奔驰
WAITING: 'WAITING', // 正在守候(未最先游戏)
};
// 为差别的状况设置差别的动画帧
Trex.animFrames = {
WAITING: {
frames: [44, 0],
msPerFrame: 1000 / 3
},
RUNNING: {
frames: [88, 132],
msPerFrame: 1000 / 12
},
CRASHED: {
frames: [220],
msPerFrame: 1000 / 60
},
JUMPING: {
frames: [0],
msPerFrame: 1000 / 60
},
DUCKING: {
frames: [264, 323],
msPerFrame: 1000 / 8
},
};

补充本篇文章中会用到的一些数据:

Runner.cOnfig= {
// ...
BOTTOM_PAD: 10, // 小恐龙距 canvas 底部的间隔
MAX_BLINK_COUNT: 3, // 小恐龙的最大眨眼次数
};
Runner.spriteDefinition = {
LDPI: {
// ...
TREX: {x: 848, y: 2}, // 小恐龙
},
};

然后来看下 Trex 原型链上的要领。我们起首来绘制静态的小恐龙:

Trex.prototype = {
// 初始化小恐龙
init: function() {
// 猎取小恐龙站在地面上时的 y 坐标
this.groundYPos = Runner.defaultDimensions.HEIGHT - this.config.HEIGHT -
Runner.config.BOTTOM_PAD;
this.yPos = this.groundYPos; // 小恐龙的 y 坐标初始化
this.draw(0, 0); // 绘制小恐龙的第一帧图片
},
/**
* 绘制小恐龙
* @param {Number} x 当前帧相关于第一帧的 x 坐标
* @param {Number} y 当前帧相关于第一帧的 y 坐标
*/
draw: function(x, y) {
// 在雪碧图中的坐标
var sourceX = x + this.spritePos.x;
var sourceY = y + this.spritePos.y;
// 在雪碧图中的宽高
var sourceWidth = this.ducking && this.status != Trex.status.CRASHED ?
this.config.WIDTH_DUCK : this.config.WIDTH;
var sourceHeight = this.config.HEIGHT;
// 绘制到 canvas 上时的高度
var outputHeight = sourceHeight;
// 隐匿状况.
if (this.ducking && this.status != Trex.status.CRASHED) {
this.ctx.drawImage(
Runner.imageSprite,
sourceX, sourceY,
sourceWidth, sourceHeight,
this.xPos, this.yPos,
this.config.WIDTH_DUCK, outputHeight
);
} else {
// 躲闪状况下撞到障碍物
if (this.ducking && this.status == Trex.status.CRASHED) {
this.xPos++;
}
// 奔驰状况
this.ctx.drawImage(
Runner.imageSprite,
sourceX, sourceY,
sourceWidth, sourceHeight,
this.xPos, this.yPos,
this.config.WIDTH, outputHeight
);
}
this.ctx.globalAlpha = 1;
},
};

前面进入街机形式那一章中,用到了 Trex 类中的数据,暂时定义了 Trex 类,别忘了将其删除。

接下来须要经由过程 Runner 类挪用 Trex 类。增加属性用于存储小恐龙类的实例:

function Runner(containerSelector, opt_config) {
// ...
+ this.tRex = null; // 小恐龙
}

初始化小恐龙类:

Runner.prototype = {
init: function () {
// ...
+ // 加载小恐龙类
+ this.tRex = new Trex(this.canvas, this.spriteDef.TREX);
},
};

如许在游戏初始化时就绘制出了静态的小恐龙,如图:

《Chrome 小恐龙游戏源码探讨八 -- 奔驰的小恐龙》

完成眨眼结果

游戏初始化以后,小恐龙会随机眨眼睛。默许的是最多只能眨三次。下面将完成这个结果。

增加更新小恐龙的要领:

Trex.prototype = {
/**
* 更新小恐龙
* @param {Number} deltaTime 间隔时候
* @param {String} opt_status 小恐龙的状况
*/
update: function(deltaTime, opt_status) {
this.timer += deltaTime;
// 更新状况的参数
if (opt_status) {
this.status = opt_status;
this.currentFrame = 0;
this.msPerFrame = Trex.animFrames[opt_status].msPerFrame;
this.currentAnimFrames = Trex.animFrames[opt_status].frames;
if (opt_status == Trex.status.WAITING) {
this.animStartTime = getTimeStamp(); // 设置眨眼动画最先的时候
this.setBlinkDelay(); // 设置眨眼间隔的时候
}
}
if (this.status == Trex.status.WAITING) {
// 小恐龙眨眼
this.blink(getTimeStamp());
} else {
// 绘制动画帧
this.draw(this.currentAnimFrames[this.currentFrame], 0);
}
if (this.timer >= this.msPerFrame) {
// 更新当前动画帧,假如处于末了一帧就更新为第一帧,不然更新为下一帧
this.currentFrame = this.currentFrame ==
this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1;
// 重置计时器
this.timer = 0;
}
},
// 设置眨眼间隔的时候
setBlinkDelay: function() {
this.blinkDelay = Math.ceil(Math.random() * Trex.BLINK_TIMING);
},
// 小恐龙眨眼
blink: function (time) {
var deltaTime = time - this.animStartTime; // 间隔时候大于随机猎取的眨眼间隔时候才眨眼
if (deltaTime >= this.blinkDelay) {
this.draw(this.currentAnimFrames[this.currentFrame], 0);
// 正在眨眼
if (this.currentFrame == 1) {
console.log('眨眼');
this.setBlinkDelay(); // 从新设置眨眼间隔的时候
this.animStartTime = time; // 更新眨眼动画最先的时候
this.blinkCount++; // 眨眼次数加一
}
}
},
};

然后将小恐龙初始更新为守候状况:

Trex.prototype = {
init: function () {
// ...
this.update(0, Trex.status.WAITING); // 初始为守候状况
},
};

末了在 Runnerupdate 要领中挪用 Trexupdate 要领来完成小恐龙眨眼:

Runner.prototype = {
update: function () {
// ...
// 游戏变成最先状况或小恐龙还没有眨三次眼
- if (this.playing) {
+ if (this.playing || (!this.activated &&
+ this.tRex.blinkCount + this.tRex.update(deltaTime);
// 举行下一次更新
this.scheduleNextUpdate();
}
},
};

结果以下:

《Chrome 小恐龙游戏源码探讨八 -- 奔驰的小恐龙》

能够看到,眨眼的代码逻辑触发了 3 次,然则现实小恐龙只眨眼了 1 次。这就是前面说的,小恐龙默许最多只能眨三次眼。详细缘由以下:

先来看下 Trexupdate 要领中的这段代码:

if (this.timer >= this.msPerFrame) {
// 更新当前动画帧,假如处于末了一帧就更新为第一帧,不然更新为下一帧
this.currentFrame = this.currentFrame ==
this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1;
// 重置计时器
this.timer = 0;
}

这段代码会将当前动画帧不断更新为下一帧。关于小恐龙来讲就是不断切换睁眼闭眼这两帧。假如当前帧为 “睁眼”,那末实行 blink 函数后小恐龙照样睁眼,也就是说现实小恐龙没眨眼;同理,只要当前帧为 “闭眼” 时,实行 blink 函数后,小恐龙才会真正的眨眼。

至于如许做的目标,就是为了防备小恐龙不断的眨眼睛。比方,将 blink 函数修正成:

// 小恐龙眨眼
blink: function () {
this.draw(this.currentAnimFrames[this.currentFrame], 0);
},

如许小恐龙会不断的眨眼睛。所以须要对其举行限定,这里 Chrome 开发人员的做法就是:设置一个间隔时候,当小恐龙眨眼的间隔时候大于这个设置的间隔时候,而且当前动画帧为 “闭眼” 时,才许可小恐龙眨眼睛。然后每次眨完眼后,从新设置眨眼间隔(默许设置为 0~7 秒),就完成了小恐龙的随机眨眼。

小恐龙的收场动画

下面来完成小恐龙对键盘按键的相应。

起首,当触发游戏彩蛋后,小恐龙会腾跃一次,并向右挪动 50 像素(默许设置的是 50 像素)。

增加让小恐龙最先腾跃的要领:

Trex.prototype = {
// 最先腾跃
startJump: function(speed) {
if (!this.jumping) {
// 更新小恐龙为腾跃状况
this.update(0, Trex.status.JUMPING);
// 依据游戏的速率调解腾跃的速率
this.jumpVelocity = this.config.INITIAL_JUMP_VELOCITY - (speed / 10);
this.jumping = true;
this.reachedMinHeight = false;
this.speedDrop = false;
}
},
};

举行挪用:

Runner.prototype = {
onKeyDown: function (e) {
if (!this.crashed && !this.paused) {
if (Runner.keyCodes.JUMP[e.keyCode]) {
e.preventDefault();

// ...
+ // 最先腾跃
+ if (!this.tRex.jumping && !this.tRex.ducking) {
+ this.tRex.startJump(this.currentSpeed);
+ }
}
}
},
};

如许,按下空格键后,小恐龙仍然会静止在地面上。接下来还须要更新动画帧才完成小恐龙的奔驰动画。

增加更新小恐龙动画帧的要领:

Trex.prototype = {
// 更新小恐龙腾跃时的动画帧
updateJump: function(deltaTime) {
var msPerFrame = Trex.animFrames[this.status].msPerFrame; // 猎取当前状况的帧率
var framesElapsed = deltaTime / msPerFrame;
// 加快着落
if (this.speedDrop) {
this.yPos += Math.round(this.jumpVelocity *
this.config.SPEED_DROP_COEFFICIENT * framesElapsed);
} else {
this.yPos += Math.round(this.jumpVelocity * framesElapsed);
}
// 腾跃的速率受重力的影响,向上逐步减小,然后反向
this.jumpVelocity += this.config.GRAVITY * framesElapsed;
// 到达了最低许可的腾跃高度
if (this.yPos this.reachedMinHeight = true;
}
// 到达了最高许可的腾跃高度
if (this.yPos this.endJump(); // 完毕腾跃
}
// 从新回到地面,腾跃完成
if (this.yPos > this.groundYPos) {
this.reset(); // 重置小恐龙的状况
this.jumpCount++; // 腾跃次数加一
}
},
// 腾跃完毕
endJump: function() {
if (this.reachedMinHeight &&
this.jumpVelocity this.jumpVelocity = this.config.DROP_VELOCITY; // 着落速率重置为默许
}
},
// 重置小恐龙状况
reset: function() {
this.yPos = this.groundYPos;
this.jumpVelocity = 0;
this.jumping = false;
this.ducking = false;
this.update(0, Trex.status.RUNNING);
this.speedDrop = false;
this.jumpCount = 0;
},
};

个中 minJumpHeight 的属性值为:

Trex.prototype = {
init: function() {
+ // 最低腾跃高度
+ this.minJumpHeight = this.groundYPos - this.config.MIN_JUMP_HEIGHT; // ...
},
}

然后举行挪用:

Runner.prototype = {
update: function () {
// ...
if (this.playing) {
this.clearCanvas();
+ if (this.tRex.jumping) {
+ this.tRex.updateJump(deltaTime);
+ }
this.runningTime += deltaTime;
var hasObstacles = this.runningTime > this.config.CLEAR_TIME;
// 刚最先 this.playingIntro 未定义 !this.playingIntro 为真
- if (!this.playingIntro) {
+ if (this.tRex.jumpCount == 1 && !this.playingIntro) {
this.playIntro(); // 实行收场动画
}
// ...
}
// ...
},
};

如许在按下空格键后,小恐龙就会腾跃一次并举行奔驰动画。如图:

《Chrome 小恐龙游戏源码探讨八 -- 奔驰的小恐龙》

下面来完成结果:小恐龙第一次腾跃后,向右挪动 50 像素。

修正 Trexupdate 要领。当推断到正在实行收场动画时,挪动小恐龙:

Trex.prototype = {
update: function(deltaTime, opt_status) {
this.timer += deltaTime;
// 更新状况的参数
if (opt_status) {
// ...
}
// 正在实行收场动画,将小恐龙向右挪动 50 像素
+ if (this.playingIntro && this.xPos + this.xPos += Math.round((this.config.START_X_POS /
+ this.config.INTRO_DURATION) * deltaTime);
+ }
// ...
},
};

能够看出当 playingIntro 属性为 true 时,小恐龙就会向右挪动。所以须要经由过程掌握这个属性的值来掌握小恐龙第一次腾跃后的挪动。

修正 Runner 上的 playIntro 要领,将小恐龙标记为正在实行收场动画:

Runner.prototype = {
playIntro: function () {
if (!this.activated && !this.crashed) {
+ this.tRex.playingIntro = true; // 小恐龙实行收场动画
// ...
}
},
};

然后须要在最先游戏后也就是实行 startGame 要领时,完毕小恐龙的收场动画:

Runner.prototype = {
startGame: function () {
this.setArcadeMode(); // 进入街机形式+ this.tRex.playingIntro = false; // 小恐龙的收场动画完毕 // ...
},
};

结果以下:

《Chrome 小恐龙游戏源码探讨八 -- 奔驰的小恐龙》

能够很明显的看到,小恐龙在第一次腾跃后向右挪动了一段间隔(默许 50 像素)。

运用键盘掌握小恐龙

在这个游戏中,当按下 键后,假如小恐龙正在腾跃,就会疾速着落,假如小恐龙在地上,就会进入躲闪状况,下面来完成这些结果。

加快着落:

Trex.prototype = {
// 设置小恐龙为加快着落,马上作废当前的腾跃
setSpeedDrop: function() {
this.speedDrop = true;
this.jumpVelocity = 1;
},
};

设置小恐龙是不是躲闪:

Trex.prototype = {
// 设置小恐龙奔驰时是不是躲闪
setDuck: function(isDucking) {
if (isDucking && this.status != Trex.status.DUCKING) { // 躲闪状况
this.update(0, Trex.status.DUCKING);
this.ducking = true;
} else if (this.status == Trex.status.DUCKING) { // 奔驰状况
this.update(0, Trex.status.RUNNING);
this.ducking = false;
}
},
};

onKeyDown 要领中挪用:

Runner.prototype = {
onKeyDown: function () {
if (!this.crashed && !this.paused) {
if (Runner.keyCodes.JUMP[e.keyCode]) {
// ...
+ } else if (this.playing && Runner.keyCodes.DUCK[e.keyCode]) {
+ e.preventDefault();
+
+ if (this.tRex.jumping) {
+ this.tRex.setSpeedDrop(); // 加快着落
+ } else if (!this.tRex.jumping && !this.tRex.ducking) {
+ this.tRex.setDuck(true); // 进入躲闪状况
+ }
+ }
}
},
};

如许就完成了前面所说的结果。然则小恐龙进入躲闪状况后,假如松开按键并不会从新站起来。由于如今还没有定义松开键盘按键时相应的事宜。下面来定义:

Runner.prototype = {
onKeyUp: function(e) {
var keyCode = String(e.keyCode);
if (Runner.keyCodes.DUCK[keyCode]) { // 隐匿状况
this.tRex.speedDrop = false;
this.tRex.setDuck(false);
}
},
};

然后挪用,修正 handleEvent 要领:

Runner.prototype = {
handleEvent: function (e) {
return (function (eType, events) {
switch (eType) {
// ...
+ case events.KEYUP:
+ this.onKeyUp(e);
+ break;
default:
break;
}
}.bind(this))(e.type, Runner.events);
},
};

结果以下:

《Chrome 小恐龙游戏源码探讨八 -- 奔驰的小恐龙》

第一次跳是一般着落,第二次跳是加快着落

处置惩罚小恐龙的腾跃

小恐龙的腾跃分为大跳和小跳,如图:

《Chrome 小恐龙游戏源码探讨八 -- 奔驰的小恐龙》

要完成这个结果,只须要在 键被松开时,马上完毕小恐龙的腾跃即可。

修正 onKeyUp 要领:

Runner.prototype = {
onKeyUp: function(e) {
var keyCode = String(e.keyCode);
+ var isjumpKey = Runner.keyCodes.JUMP[keyCode];
+ if (this.isRunning() && isjumpKey) { // 腾跃
+ this.tRex.endJump();
} else if (Runner.keyCodes.DUCK[keyCode]) { // 隐匿状况
this.tRex.speedDrop = false;
this.tRex.setDuck(false);
}
},
};

个中 isRunning 要领定义以下:

Runner.prototype = {
// 是不是游戏正在举行
isRunning: function() {
return !!this.raqId;
},
};

如许就完成了小恐龙的大跳和小跳。

末了是要完成的结果是:假如页面失焦时,小恐龙正在腾跃,就重置小恐龙的状况(也就是会马上回到地面上)。这个结果完成很简单,直接挪用前面定义的 reset 要领即可:

Runner.prototype = {
play: function () {
if (!this.crashed) {
// ...
+ this.tRex.reset();
}
},
};

结果以下:

《Chrome 小恐龙游戏源码探讨八 -- 奔驰的小恐龙》

检察增加或修正的代码,
戳这里

Demo 体验地点:https://liuyib.github.io/blog/demo/game/google-dino/dino-gogogo/

上一篇下一篇
Chrome 小恐龙游戏源码探讨七 — 日夜形式交替 Chrome 小恐龙游戏源码探讨九 — 游戏碰撞检测

推荐阅读
author-avatar
Super小泰森_
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有