摘要:文章首發(fā)于我的博客前言上一篇文章小恐龍游戲源碼探究五隨機(jī)繪制障礙實(shí)現(xiàn)了障礙物仙人掌和翼龍的繪制。在游戲中,小恐龍移動(dòng)的距離就是游戲的分?jǐn)?shù)。
文章首發(fā)于我的 GitHub 博客前言
上一篇文章:《Chrome 小恐龍游戲源碼探究五 -- 隨機(jī)繪制障礙》 實(shí)現(xiàn)了障礙物仙人掌和翼龍的繪制。這一篇將實(shí)現(xiàn)當(dāng)前分?jǐn)?shù)、最高分?jǐn)?shù)的記錄和繪制。
在游戲中,小恐龍移動(dòng)的距離就是游戲的分?jǐn)?shù)。分?jǐn)?shù)每達(dá) 100,就會(huì)有閃動(dòng)的特效。首次進(jìn)行游戲的時(shí)候,由于沒(méi)有記錄過(guò)游戲的歷史得分,所以不會(huì)顯示最高分,只有當(dāng)?shù)谝淮?game over 后才能顯示歷史最高分。
分?jǐn)?shù)記錄定義分?jǐn)?shù)類 DistanceMeter:
/** * 記錄移動(dòng)的距離(分?jǐn)?shù)等于移動(dòng)距離) * @param {HTMLCanvasElement} canvas 畫(huà)布 * @param {Object} spritePos 圖片在雪碧圖中的位置 * @param {Number} canvasWidth 畫(huà)布的寬度 */ function DistanceMeter(canvas, spritePos, canvasWidth) { this.canvas = canvas; this.ctx = canvas.getContext("2d"); this.config = DistanceMeter.config; this.spritePos = spritePos; this.x = 0; // 分?jǐn)?shù)顯示在 canvas 中的 x 坐標(biāo) this.y = 5; this.maxScore = 0; // 游戲分?jǐn)?shù)上限 this.highScore = []; // 存儲(chǔ)最高分?jǐn)?shù)的每一位數(shù)字 this.digits = []; // 存儲(chǔ)分?jǐn)?shù)的每一位數(shù)字 this.achievement = false; // 是否進(jìn)行閃動(dòng)特效 this.defaultString = ""; // 游戲的默認(rèn)距離(00000) this.flashTimer = 0; // 動(dòng)畫(huà)計(jì)時(shí)器 this.flashIterations = 0; // 特效閃動(dòng)的次數(shù) this.maxScoreUnits = this.config.MAX_DISTANCE_UNITS; // 分?jǐn)?shù)的最大位數(shù) this.init(canvasWidth); }
有關(guān)的配置參數(shù):
DistanceMeter.config = { MAX_DISTANCE_UNITS: 5, // 分?jǐn)?shù)的最大位數(shù) ACHIEVEMENT_DISTANCE: 100, // 每 100 米觸發(fā)一次閃動(dòng)特效 COEFFICIENT: 0.025, // 將像素距離轉(zhuǎn)換為比例單位的系數(shù) FLASH_DURATION: 1000 / 4, // 一閃的時(shí)間(一次閃動(dòng)分別兩閃:從有到無(wú),從無(wú)到有) FLASH_ITERATIONS: 3, // 閃動(dòng)的次數(shù) }; DistanceMeter.dimensions = { WIDTH: 10, HEIGHT: 13, DEST_WIDTH: 11, // 加上間隔后每個(gè)數(shù)字的寬度 };
補(bǔ)充本篇文章中會(huì)用到的一些數(shù)據(jù):
function Runner(containerSelector, opt_config) { // ... + this.msPerFrame = 1000 / FPS; // 每幀的時(shí)間 }, Runner.spriteDefinition = { LDPI: { //... + TEXT_SPRITE: {x: 655, y: 2}, // 文字 }, };
在 DistanceMeter 上添加方法:
DistanceMeter.prototype = { // 初始化分?jǐn)?shù) init: function (width) { var maxDistanceStr = ""; // 游戲的最大距離 this.calcXPos(width); // 計(jì)算分?jǐn)?shù)顯示在 canvas 中的 x 坐標(biāo) for (var i = 0; i < this.maxScoreUnits; i++) { this.draw(i, 0); // 第一次游戲,不繪制最高分 this.defaultString += "0"; // 默認(rèn)初始分?jǐn)?shù) 00000 maxDistanceStr += "9"; // 默認(rèn)最大分?jǐn)?shù) 99999 } this.maxScore = parseInt(maxDistanceStr); }, // 計(jì)算 x 坐標(biāo) calcXPos: function (canvasWidth) { this.x = canvasWidth - (DistanceMeter.dimensions.DEST_WIDTH * (this.maxScoreUnits + 1)); }, /** * 將分?jǐn)?shù)繪制到 canvas 上 * @param {Number} digitPos 數(shù)字在分?jǐn)?shù)中的位置 * @param {Number} value 數(shù)字的具體值(0-9) * @param {Boolean} opt_highScore 是否顯示最高分 */ draw: function (digitPos, value, opt_highScore) { // 在雪碧圖中的坐標(biāo) var sourceX = this.spritePos.x + DistanceMeter.dimensions.WIDTH * value; var sourceY = this.spritePos.y + 0; var sourceWidth = DistanceMeter.dimensions.WIDTH; var sourceHeight = DistanceMeter.dimensions.HEIGHT; // 繪制到 canvas 時(shí)的坐標(biāo) var targetX = digitPos * DistanceMeter.dimensions.DEST_WIDTH; var targetY = this.y; var targetWidth = DistanceMeter.dimensions.WIDTH; var targetHeight = DistanceMeter.dimensions.HEIGHT; this.ctx.save(); if (opt_highScore) { // 顯示最高分 var hightScoreX = this.x - (this.maxScoreUnits * 2) * DistanceMeter.dimensions.WIDTH; this.ctx.translate(hightScoreX, this.y); } else { // 不顯示最高分 this.ctx.translate(this.x, this.y); } this.ctx.drawImage( Runner.imageSprite, sourceX, sourceY, sourceWidth, sourceHeight, targetX, targetY, targetWidth, targetHeight ); this.ctx.restore(); }, /** * 將游戲移動(dòng)的像素距離轉(zhuǎn)換為真實(shí)的距離 * @param {Number} distance 游戲移動(dòng)的像素距離 */ getActualDistance: function (distance) { return distance ? Math.round(distance * this.config.COEFFICIENT) : 0; }, // 更新分?jǐn)?shù) update: function (deltaTime, distance) { var paint = true; // 是否繪制分?jǐn)?shù) var playSound = false; // 是否播放音效 // 沒(méi)有進(jìn)行閃動(dòng)特效 if (!this.achievement) { distance = this.getActualDistance(distance); // 分?jǐn)?shù)超出上限時(shí),上限增加一位數(shù)。超出上限兩位數(shù)時(shí),分?jǐn)?shù)置零 if (distance > this.maxScore && this.maxScoreUnits === this.config.MAX_DISTANCE_UNITS) { this.maxScoreUnits++; this.maxScore = parseInt(this.maxScore + "9"); } else { this.distance = 0; } if (distance > 0) { // 觸發(fā)閃動(dòng)特效 if (distance % this.config.ACHIEVEMENT_DISTANCE == 0) { this.achievement = true; this.flashTimer = 0; playSound = true; } // 分?jǐn)?shù)前面補(bǔ)零來(lái)湊位數(shù) var distanceStr = (this.defaultString + distance).substr(-this.maxScoreUnits); this.digits = distanceStr.split(""); } else { // 將默認(rèn)分?jǐn)?shù) 00000 中的每一位數(shù)字存到數(shù)組中 this.digits = this.defaultString.split(""); } } else { // 控制特效的閃動(dòng)次數(shù) if (this.flashIterations <= this.config.FLASH_ITERATIONS) { this.flashTimer += deltaTime; // 第一閃不繪制數(shù)字 if (this.flashTimer < this.config.FLASH_DURATION) { paint = false; } // 進(jìn)行了兩閃,閃動(dòng)次數(shù)加一 else if (this.flashTimer > this.config.FLASH_DURATION * 2) { this.flashTimer = 0; this.flashIterations++; } } else { // 閃動(dòng)特效結(jié)束 this.achievement = false; this.flashIterations = 0; this.flashTimer = 0; } } // 繪制當(dāng)前分 if (paint) { for (var i = this.digits.length - 1; i >= 0; i--) { this.draw(i, parseInt(this.digits[i])); } } // 繪制最高分 this.drawHighScore(); return playSound; }, // 繪制最高分 drawHighScore: function () { this.ctx.save(); this.ctx.globalAlpha = 0.8; for (var i = this.highScore.length - 1; i >= 0; i--) { this.draw(i, parseInt(this.highScore[i], 10), true); } this.ctx.restore(); }, /** * 將游戲的最高分?jǐn)?shù)存入數(shù)組 * @param {Number} distance 游戲移動(dòng)的像素距離 */ setHighScore: function (distance) { distance = this.getActualDistance(distance); var highScoreStr = (this.defaultString + distance).substr(-this.maxScoreUnits); // 分?jǐn)?shù)前面字母 H、I 在雪碧圖中位于數(shù)字后面,也就是第 10、11 位置 this.highScore = ["10", "11", ""].concat(highScoreStr.split("")); }, };
下面是對(duì)分?jǐn)?shù)類的調(diào)用。
首先給 Runner 類添加屬性:
function Runner(containerSelector, opt_config) { // ... + this.distanceMeter = null; // 距離計(jì)數(shù)類 + this.distanceRan = 0; // 游戲移動(dòng)距離 + this.highestScore = 0; // 最高分 }
然后初始化分?jǐn)?shù)類 DistanceMeter:
Runner.prototype = { init: function () { // ... + // 加載距離計(jì)數(shù)器類 DistanceMeter + this.distanceMeter = new DistanceMeter(this.canvas, + this.spriteDef.TEXT_SPRITE, this.dimensions.WIDTH); }, };
更新游戲分?jǐn)?shù)(移動(dòng)距離):
Runner.prototype = { update: function () { this.updatePending = false; // 等待更新 if (this.playing) { // ... + this.distanceRan += this.currentSpeed * deltaTime / this.msPerFrame; if (this.currentSpeed < this.config.MAX_SPEED) { this.currentSpeed += this.config.ACCELERATION; } + var playAchievementSound = this.distanceMeter.update(deltaTime, + Math.ceil(this.distanceRan)); } // ... }, };
這樣就實(shí)現(xiàn)了分?jǐn)?shù)的繪制,效果如下:
查看添加或修改的代碼,戳這里
上面定義了保存、繪制游戲最高分的方法,但還沒(méi)有調(diào)用,現(xiàn)在只要在游戲結(jié)束時(shí),將分?jǐn)?shù)保存下來(lái),就能實(shí)現(xiàn)最高分的繪制。添加 gameOver 方法:
Runner.prototype = { // 游戲結(jié)束 gameOver: function () { this.stop(); if (this.distanceRan > this.highestScore) { this.highestScore = Math.ceil(this.distanceRan); this.distanceMeter.setHighScore(this.highestScore); // 保存最高分 } // 重置時(shí)間 this.time = getTimeStamp(); }, };
這里為了演示,當(dāng)頁(yè)面失焦時(shí),結(jié)束游戲(等后面講到實(shí)現(xiàn)游戲結(jié)束時(shí),需要?jiǎng)h除這里的臨時(shí)代碼):
Runner.prototype = { onVisibilityChange: function (e) { if (document.hidden || document.webkitHidden || e.type == "blur" || document.visibilityState != "visible") { this.stop(); + this.gameOver(); } else if (!this.crashed) { this.play(); } }, };
效果如下:
查看添加或修改的代碼,戳這里
Demo 體驗(yàn)地址:https://liuyib.github.io/blog/demo/game/google-dino/show-score/
上一篇 | 下一篇 | Chrome 小恐龍游戲源碼探究五 -- 隨機(jī)繪制障礙 | Chrome 小恐龍游戲源碼探究七 -- 晝夜模式交替 |
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/103880.html
摘要:文章首發(fā)于我的博客前言上一篇文章小恐龍游戲源碼探究六記錄游戲分?jǐn)?shù)實(shí)現(xiàn)了游戲分?jǐn)?shù)最高分?jǐn)?shù)的記錄和繪制。這一篇文章中將實(shí)現(xiàn)晝夜模式交替的的效果。原來(lái)的游戲中,晝夜交替每米觸發(fā)一次,這里為了演示,改成了米觸發(fā)一次。 文章首發(fā)于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究六 -- 記錄游戲分?jǐn)?shù)》實(shí)現(xiàn)了游戲分?jǐn)?shù)、最高分?jǐn)?shù)的記錄和繪制。這一篇文章中將實(shí)現(xiàn)晝夜模式...
摘要:首先是繪制靜態(tài)的地面。上一篇下一篇無(wú)小恐龍游戲源碼探究二讓地面動(dòng)起來(lái) 文章首發(fā)于我的 GitHub 博客 目錄 Chrome 小恐龍游戲源碼探究一 -- 繪制靜態(tài)地面 Chrome 小恐龍游戲源碼探究二 -- 讓地面動(dòng)起來(lái) Chrome 小恐龍游戲源碼探究三 -- 進(jìn)入街機(jī)模式 Chrome 小恐龍游戲源碼探究四 -- 隨機(jī)繪制云朵 Chrome 小恐龍游戲源碼探究五 -- 隨機(jī)繪...
摘要:文章首發(fā)于我的博客前言上一篇文章小恐龍游戲源碼探究四隨機(jī)繪制云朵實(shí)現(xiàn)了云朵的隨機(jī)繪制,這一篇文章中將實(shí)現(xiàn)仙人掌翼龍障礙物的繪制游戲速度的改變障礙物的類型有兩種仙人掌和翼龍。 文章首發(fā)于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究四 -- 隨機(jī)繪制云朵》 實(shí)現(xiàn)了云朵的隨機(jī)繪制,這一篇文章中將實(shí)現(xiàn):1、仙人掌、翼龍障礙物的繪制 2、游戲速度的改變 障礙物...
摘要:文章首發(fā)于我的博客前言上一篇文章小恐龍游戲源碼探究八奔跑的小恐龍實(shí)現(xiàn)了小恐龍的繪制以及鍵盤(pán)對(duì)小恐龍的控制,這一篇文章中將實(shí)現(xiàn)游戲的碰撞檢測(cè)。 文章首發(fā)于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究八 -- 奔跑的小恐龍》實(shí)現(xiàn)了小恐龍的繪制以及鍵盤(pán)對(duì)小恐龍的控制,這一篇文章中將實(shí)現(xiàn)游戲的碰撞檢測(cè)。 碰撞檢測(cè)原理 這個(gè)游戲采用的檢測(cè)方法是盒子碰撞,這種檢...
摘要:文章首發(fā)于我的博客前言上一篇文章小恐龍游戲源碼探究二讓地面動(dòng)起來(lái)實(shí)現(xiàn)了地面的移動(dòng)。街機(jī)模式的效果就是游戲開(kāi)始后,進(jìn)入全屏模式。例如可以看到,進(jìn)入街機(jī)模式之前,有一段開(kāi)場(chǎng)動(dòng)畫(huà)。 文章首發(fā)于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究二 -- 讓地面動(dòng)起來(lái)》 實(shí)現(xiàn)了地面的移動(dòng)。這一篇文章中,將實(shí)現(xiàn)效果:1、瀏覽器失焦時(shí)游戲暫停,聚焦游戲繼續(xù)。 2、開(kāi)場(chǎng)動(dòng)...
閱讀 2528·2021-11-12 10:34
閱讀 1527·2019-08-29 16:15
閱讀 2743·2019-08-29 15:17
閱讀 1451·2019-08-23 17:09
閱讀 439·2019-08-23 11:37
閱讀 2510·2019-08-23 10:39
閱讀 549·2019-08-22 16:43
閱讀 3173·2019-08-22 14:53