摘要:文章首發于我的博客前言上一篇文章小恐龍游戲源碼探究六記錄游戲分數實現了游戲分數最高分數的記錄和繪制。這一篇文章中將實現晝夜模式交替的的效果。原來的游戲中,晝夜交替每米觸發一次,這里為了演示,改成了米觸發一次。
文章首發于我的 GitHub 博客前言
上一篇文章:《Chrome 小恐龍游戲源碼探究六 -- 記錄游戲分數》實現了游戲分數、最高分數的記錄和繪制。這一篇文章中將實現晝夜模式交替的的效果。
夜晚模式定義夜晚模式類 NightMode:
/** * 夜晚模式 * @param {HTMLCanvasElement} canvas 畫布 * @param {Object} spritePos 雪碧圖中的坐標信息 * @param {Number} containerWidth 容器寬度 */ function NightMode(canvas, spritePos, containerWidth) { this.canvas = canvas; this.ctx = this.canvas.getContext("2d"); this.spritePos = spritePos; this.containerWidth = containerWidth; this.xPos = containerWidth - 50; // 月亮的 x 坐標 this.yPos = 30; // 月亮的 y 坐標 this.currentPhase = 0; // 月亮當前所處的時期 this.opacity = 0; // 星星和月亮的透明度 this.stars = []; // 存儲星星 this.drawStars = false; // 是否繪制星星 // 放置星星 this.placeStars(); }
相關的配置參數:
NightMode.config = { WIDTH: 20, // 半月的寬度 HEIGHT: 40, // 月亮的高度 FADE_SPEED: 0.035, // 淡入淡出的速度 MOON_SPEED: 0.25, // 月亮的速度 NUM_STARS: 2, // 星星的數量 STAR_SIZE: 9, // 星星的大小 STAR_SPEED: 0.3, // 星星的速度 STAR_MAX_Y: 70, // 星星在畫布上的最大 y 坐標 }; // 月亮所處的時期(不同的時期有不同的位置) NightMode.phases = [140, 120, 100, 60, 40, 20, 0];
補充本篇文章中會用到的一些數據:
function Runner(containerSelector, opt_config) { // ... + this.inverted = false; // 是否開啟夜晚模式 + this.invertTimer = 0; // 夜晚模式的時間 } Runner.config = { // ... + INVERT_FADE_DURATION: 12000, // 夜晚模式的持續時間 + INVERT_DISTANCE: 100, // 觸發夜晚模式的距離 }; Runner.spriteDefinition = { LDPI: { // ... + MOON: {x: 484, y: 2}, + STAR: {x: 645, y: 2}, }, }; Runner.classes = { // ... + INVERTED: "inverted", };
body { transition: filter 1.5s cubic-bezier(0.65, 0.05, 0.36, 1), background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1); will-change: filter, background-color; } .inverted { filter: invert(100%); background-color: #000; }
來看下 NightMode 原型鏈上的方法:
NightMode.prototype = { // 繪制星星和月亮 draw: function () { // 月期為 3 時,月亮為滿月 var moonSourceWidth = this.currentPhase == 3 ? NightMode.config.WIDTH * 2 : NightMode.config.WIDTH; var moonSourceHeight = NightMode.config.HEIGHT; // 月亮在雪碧圖中的 x 坐標 var moonSourceX = this.spritePos.x + NightMode.phases[this.currentPhase]; var moonOutputWidth = moonSourceWidth; // 星星在雪碧圖中的 x 坐標 var starSourceX = Runner.spriteDefinition.LDPI.STAR.x; var starSize = NightMode.config.STAR_SIZE; this.ctx.save(); this.ctx.globalAlpha = this.opacity; // 畫布的透明度隨之變化 // 繪制星星 if (this.drawStars) { for (var i = 0; i < NightMode.config.NUM_STARS; i++) { this.ctx.drawImage( Runner.imageSprite, starSourceX, this.stars[i].sourceY, starSize, starSize, Math.round(this.stars[i].x), this.stars[i].y, NightMode.config.STAR_SIZE, NightMode.config.STAR_SIZE, ); } } // 繪制月亮 this.ctx.drawImage( Runner.imageSprite, moonSourceX, this.spritePos.y, moonSourceWidth, moonSourceHeight, Math.round(this.xPos), this.yPos, moonOutputWidth, NightMode.config.HEIGHT ); this.ctx.globalAlpha = 1; this.ctx.restore(); }, /** * 更新星星和月亮的位置,改變月期 * @param {Boolean} activated 是否夜晚模式被激活 */ update: function (activated) { // 改變月期 if (activated && this.opacity === 0) { this.currentPhase++; if (this.currentPhase >= NightMode.phases.length) { this.currentPhase = 0; } } // 淡入 if (activated && (this.opacity < 1 || this.opacity === 0)) { this.opacity += NightMode.config.FADE_SPEED; } else if (this.opacity > 0) { // 淡出 this.opacity -= NightMode.config.FADE_SPEED; } // 設置月亮和星星的位置 if (this.opacity > 0) { // 更新月亮的 x 坐標 this.xPos = this.updateXPos(this.xPos, NightMode.config.MOON_SPEED); // 更新星星的 x 坐標 if (this.drawStars) { for (var i = 0; i < NightMode.config.NUM_STARS; i++) { this.stars[i].x = this.updateXPos(this.stars[i].x, NightMode.config.STAR_SPEED); } } this.draw(); } else { this.opacity = 0; this.placeStars(); } this.drawStars = true; }, // 更新 x 坐標 updateXPos: function (currentPos, speed) { // 月亮移出畫布半個月亮寬度,將其位置移動到畫布右邊 if (currentPos < -NightMode.config.WIDTH) { currentPos = this.containerWidth; } else { currentPos -= speed; } return currentPos; }, // 隨機放置星星 placeStars: function () { // 將畫布分為若干組 var segmentSize = Math.round(this.containerWidth / NightMode.config.NUM_STARS); for (var i = 0; i < NightMode.config.NUM_STARS; i++) { this.stars[i] = {}; // 分別隨機每組畫布中星星的位置 this.stars[i].x = getRandomNum(segmentSize * i, segmentSize * (i + 1)); this.stars[i].y = getRandomNum(0, NightMode.config.STAR_MAX_Y); // 星星在雪碧圖中的 y 坐標 this.stars[i].sourceY = Runner.spriteDefinition.LDPI.STAR.y + NightMode.config.STAR_SIZE * i; } }, };
定義好 NightMode 類以及相關方法后,接下來需要通過 Horizon 來進行調用。
修改 Horizon 類:
function Horizon(canvas, spritePos, dimensions, gapCoefficient) { // ... + // 夜晚模式 + this.nightMode = null; }
初始化 NightMode 類:
Horizon.prototype = { init: function () { // ... + this.nightMode = new NightMode(this.canvas, this.spritePos.MOON, + this.dimensions.WIDTH); }, };
更新夜晚模式:
Horizon.prototype = { - update: function (deltaTime, currentSpeed, updateObstacles) { + update: function (deltaTime, currentSpeed, updateObstacles, showNightMode) { // ... + this.nightMode.update(showNightMode); }, };
然后修改 Runner 的 update 方法:
Runner.prototype = { update: function () { this.updatePending = false; // 等待更新 if (this.playing) { // ... // 直到開場動畫結束再移動地面 if (this.playingIntro) { this.horizon.update(0, this.currentSpeed, hasObstacles); } else { deltaTime = !this.activated ? 0 : deltaTime; - this.horizon.update(deltaTime, this.currentSpeed, hasObstacles); + this.horizon.update(deltaTime, this.currentSpeed, hasObstacles, + this.inverted); } + // 夜晚模式 + if (this.invertTimer > this.config.INVERT_FADE_DURATION) { // 夜晚模式結束 + this.invertTimer = 0; + this.invertTrigger = false; + this.invert(); + } else if (this.invertTimer) { // 處于夜晚模式,更新其時間 + this.invertTimer += deltaTime; + } else { // 還沒進入夜晚模式 + // 游戲移動的距離 + var actualDistance = + this.distanceMeter.getActualDistance(Math.ceil(this.distanceRan)); + + if(actualDistance > 0) { + // 每移動指定距離就觸發一次夜晚模式 + this.invertTrigger = !(actualDistance % this.config.INVERT_DISTANCE); + + if (this.invertTrigger && this.invertTimer === 0) { + this.invertTimer += deltaTime; + this.invert(); + } + } + } } if (this.playing) { // 進行下一次更新 this.scheduleNextUpdate(); } }, };
上面用到的 invert 方法定義如下:
Runner.prototype = { /** * 反轉當前頁面的顏色 * @param {Boolea} reset 是否重置顏色 */ invert: function (reset) { var bodyElem = document.body; if (reset) { bodyElem.classList.toggle(Runner.classes.INVERTED, false); // 刪除 className this.invertTimer = 0; // 重置夜晚模式的時間 this.inverted = false; // 關閉夜晚模式 } else { this.inverted = bodyElem.classList.toggle(Runner.classes.INVERTED, this.invertTrigger); } }, };
這樣就是實現了晝夜交替的效果。原來的游戲中,晝夜交替每 700 米觸發一次,這里為了演示,改成了 100 米觸發一次。效果如下:
查看添加或修改的代碼,戳這里
Demo 體驗地址:https://liuyib.github.io/blog/demo/game/google-dino/night-mode/
上一篇 | 下一篇 | Chrome 小恐龍游戲源碼探究六 -- 記錄游戲分數 | Chrome 小恐龍游戲源碼探究八 -- 奔跑的小恐龍 |
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103900.html
摘要:例如,將函數修改為小恐龍眨眼這樣小恐龍會不停的眨眼睛。小恐龍的開場動畫下面來實現小恐龍對鍵盤按鍵的響應。接下來還需要更新動畫幀才能實現小恐龍的奔跑動畫。 文章首發于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究七 -- 晝夜模式交替》實現了游戲晝夜模式的交替,這一篇文章中,將實現:1、小恐龍的繪制 2、鍵盤對小恐龍的控制 3、頁面失焦后,重新聚焦會重置...
摘要:首先是繪制靜態的地面。上一篇下一篇無小恐龍游戲源碼探究二讓地面動起來 文章首發于我的 GitHub 博客 目錄 Chrome 小恐龍游戲源碼探究一 -- 繪制靜態地面 Chrome 小恐龍游戲源碼探究二 -- 讓地面動起來 Chrome 小恐龍游戲源碼探究三 -- 進入街機模式 Chrome 小恐龍游戲源碼探究四 -- 隨機繪制云朵 Chrome 小恐龍游戲源碼探究五 -- 隨機繪...
摘要:文章首發于我的博客前言上一篇文章小恐龍游戲源碼探究五隨機繪制障礙實現了障礙物仙人掌和翼龍的繪制。在游戲中,小恐龍移動的距離就是游戲的分數。 文章首發于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究五 -- 隨機繪制障礙》 實現了障礙物仙人掌和翼龍的繪制。這一篇將實現當前分數、最高分數的記錄和繪制。 在游戲中,小恐龍移動的距離就是游戲的分數。分數每達 1...
摘要:文章首發于我的博客前言上一篇文章小恐龍游戲源碼探究二讓地面動起來實現了地面的移動。街機模式的效果就是游戲開始后,進入全屏模式。例如可以看到,進入街機模式之前,有一段開場動畫。 文章首發于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究二 -- 讓地面動起來》 實現了地面的移動。這一篇文章中,將實現效果:1、瀏覽器失焦時游戲暫停,聚焦游戲繼續。 2、開場動...
摘要:文章首發于我的博客前言上一篇文章小恐龍游戲源碼探究三進入街機模式實現了開場動畫和街機模式。 文章首發于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究三 -- 進入街機模式》 實現了開場動畫和街機模式。這一篇文章中,將實現云朵的隨機繪制。 云朵類 Cloud 定義云朵類 Cloud: /** * 云朵類 * @param {HTMLCanvasEle...
閱讀 2946·2021-11-22 15:25
閱讀 2240·2021-11-18 10:07
閱讀 1045·2019-08-29 15:29
閱讀 472·2019-08-29 13:25
閱讀 1504·2019-08-29 12:58
閱讀 3201·2019-08-29 12:55
閱讀 2911·2019-08-29 12:28
閱讀 500·2019-08-29 12:16