摘要:首先是繪制靜態的地面。上一篇下一篇無小恐龍游戲源碼探究二讓地面動起來
文章首發于我的 GitHub 博客目錄
Chrome 小恐龍游戲源碼探究一 -- 繪制靜態地面
Chrome 小恐龍游戲源碼探究二 -- 讓地面動起來
Chrome 小恐龍游戲源碼探究三 -- 進入街機模式
Chrome 小恐龍游戲源碼探究四 -- 隨機繪制云朵
Chrome 小恐龍游戲源碼探究五 -- 隨機繪制障礙
Chrome 小恐龍游戲源碼探究六 -- 記錄游戲分數
Chrome 小恐龍游戲源碼探究七 -- 晝夜模式交替
Chrome 小恐龍游戲源碼探究八 -- 奔跑的小恐龍
Chrome 小恐龍游戲源碼探究九 -- 游戲碰撞檢測
Chrome 小恐龍游戲源碼探究完 -- 游戲結束和其他要素
前言當 Chrome 處于離線情況下,會顯示以下頁面:
當按下空格鍵或者 ↑ 鍵,小恐龍游戲彩蛋就觸發啦 (??????)??
游戲雖然簡單,但源碼卻有三千多行,代碼嚴謹且富有邏輯,值得拿來學習研究。這個教程將會從零開始,一步步解讀源碼并最終實現這個游戲。
獲取源碼、素材要獲取游戲的源碼,可以通過下面幾種方式:
斷網后,訪問任意網址,進入小恐龍頁面,用開發者工具獲取源碼
在瀏覽器地址欄輸入 chrome://dino,進入小恐龍頁面,用開發者工具獲取源碼
官方提供的源碼網址
有人將源碼提取出來放在了 GitHub 上:t-rex-runner
游戲用到的雪碧圖,音頻文件可以在官方提供的源碼網址里獲取到。
為了方便食用,我將雪碧圖中各個小圖片的坐標信息標了出來(W: Width, H: Height, L: Left, T: Top):
關于上面雪碧圖的坐標信息,我是用一個在線工具獲取的:http://www.spritecow.com/,個別坐標信息通過這個網站獲取的不太準,這里我已經通過參考源碼里的數據進行了修正。
戳這里獲取上面這張圖片的 JPG 原圖和 PSD 原圖。開始探究
游戲源碼主要包括九個類:
游戲的主體類 Runner
背景類 Horizon
地面類 HorizonLine
云朵類 Cloud
障礙物類 Obstacle
晝夜更替類 NightMode
小恐龍類 Trex
分數類 DistanceMeter
游戲結束面板類 GameOverPanel
這個教程并不會完全按照源碼來,而是抽取主要的內容來一步步實現這個游戲。這樣做并不意味著改變源碼的思路,而是去除了一些目前可以先不考慮的代碼,比如:去除了適配 HDPI 和 LDPI、適配移動端等。
這個游戲源碼的探究已經有前輩 @逐影 寫了系列教程。在這里,我寫這個教程的目的,一是當做學習筆記,二是提供與前輩不一樣的源碼解讀思路。游戲主體搭建
游戲文件結構目錄:
chrome-dino - index.html - index.css - index.js // JS 入口文件 - offline.js // 游戲邏輯實現 - imgs - sounds
想要獲取整個教程的源代碼,戳這里:GitHub
HTML、CSS 就不過多解釋,直接貼代碼:
Chrome Dino
* { margin: 0; padding: 0; } *, *::before, *::after { box-sizing: border-box; } #chrome-dino { width: 100%; max-width: 600px; margin: 0 auto; } #offline-resources { display: none; } .offline .runner-container { position: absolute; top: 35px; width: 100%; max-width: 600px; height: 150px; overflow: hidden; } .offline .runner-canvas { z-index: 10; position: absolute; top: 0; height: 150px; max-width: 600px; overflow: hidden; opacity: 1; }
下面來分析 JS 代碼:
首先看一下游戲的主體類 Runner,這個類用于控制游戲的主要邏輯:
/** * 游戲主體類,控制游戲的整體邏輯 * @param {String} containerSelector 畫布外層容器的選擇器 * @param {Object} opt_config 配置選項 */ function Runner(containerSelector, opt_config) { // 獲取游戲的 “根” DOM 節點,整個游戲都會輸出到這個節點里 this.outerContainerEl = document.querySelector(containerSelector); // canvas 的外層容器 this.containerEl = null; this.config = opt_config || Runner.config; this.dimensions = Runner.defaultDimensions; this.time = 0; // 時鐘計時器 this.currentSpeed = this.config.SPEED; // 當前的速度 this.activated = false; // 游戲彩蛋是否被激活(沒有被激活時,游戲不會顯示出來) this.playing = false; // 游戲是否進行中 this.crashed = false; // 小恐龍是否碰到了障礙物 this.paused = false // 游戲是否暫停 // 加載雪碧圖,并初始化游戲 this.loadImages(); } window["Runner"] = Runner; // 將 Runner 類掛載到 window 對象上
相關的數據和配置參數:
var DEFAULT_WIDTH = 600; // 游戲畫布默認寬度 var FPS = 60; // 游戲默認幀率 // 游戲配置參數 Runner.config = { SPEED: 6, // 移動速度 }; // 游戲畫布的默認尺寸 Runner.defaultDimensions = { WIDTH: DEFAULT_WIDTH, HEIGHT: 150, }; // 游戲用到的 className Runner.classes = { CONTAINER: "runner-container", CANVAS: "runner-canvas", PLAYER: "", // 預留出的 className,用來控制 canvas 的樣式 }; // 雪碧圖中圖片的坐標信息 Runner.spriteDefinition = { LDPI: { HORIZON: { x: 2, y: 54 }, // 地面 }, }; // 游戲中用到的鍵盤碼 Runner.keyCodes = { JUMP: { "38": 1, "32": 1 }, // Up, Space DUCK: { "40": 1 }, // Down RESTART: { "13": 1 }, // Enter }; // 游戲中用到的事件 Runner.events = { LOAD: "load", };
在 Runner 原型鏈上添加的方法:
Runner.prototype = { // 初始化游戲 init: function () { // 生成 canvas 容器元素 this.containerEl = document.createElement("div"); this.containerEl.className = Runner.classes.CONTAINER; // 生成 canvas this.canvas = createCanvas(this.containerEl, this.dimensions.WIDTH, this.dimensions.HEIGHT, Runner.classes.PLAYER); this.ctx = this.canvas.getContext("2d"); this.ctx.fillStyle = "#f7f7f7"; this.ctx.fill(); // 加載背景類 Horizon this.horizon = new Horizon(this.canvas, this.spriteDef); // 將游戲添加到頁面中 this.outerContainerEl.appendChild(this.containerEl); }, // 加載雪碧圖資源 loadImages() { // 圖片在雪碧圖中的坐標 this.spriteDef = Runner.spriteDefinition.LDPI; // 獲取雪碧圖 Runner.imageSprite = document.getElementById("offline-resources-1x"); // 當圖片加載完成(complete 是 DOM 中 Image 對象自帶的一個屬性) if (Runner.imageSprite.complete) { this.init(); } else { // 圖片沒有加載完成,監聽其 load 事件 Runner.imageSprite.addEventListener(Runner.events.LOAD, this.init.bind(this)); } }, };
其中 createCanvas 方法定義如下:
/** * 生成 canvas 元素 * @param {HTMLElement} container canva 的容器 * @param {Number} width canvas 的寬度 * @param {Number} height canvas 的高度 * @param {String} opt_className 給 canvas 添加的類名(可選) * @return {HTMLCanvasElement} */ function createCanvas(container, width, height, opt_className) { var canvas = document.createElement("canvas"); canvas.className = opt_className ? opt_className + " " + Runner.classes.CANVAS : Runner.classes.CANVAS; canvas.width = width; canvas.height = height; container.appendChild(canvas); return canvas; }地面類 HorizonLine
定義好 Runner 類之后,為了方便探究,接下來從簡單的背景開始說起。首先是繪制靜態的地面。
定義地面類 HorizonLine:
/** * 地面類 * @param {HTMLCanvasElement} canvas 畫布 * @param {Object} spritePos 雪碧圖中的位置 */ function HorizonLine(canvas, spritePos) { this.canvas = canvas; this.ctx = this.canvas.getContext("2d"); this.dimensions = {}; // 地面的尺寸 this.spritePos = spritePos; // 雪碧圖中地面的位置 this.sourceXPos = []; // 雪碧圖中地面的兩種地形的 x 坐標 this.xPos = []; // canvas 中地面的 x 坐標 this.yPos = 0; // canvas 中地面的 y 坐標 this.bumpThreshold = 0.5; // 隨機地形系數,控制兩種地形的出現頻率 this.init(); this.draw(); } HorizonLine.dimensions = { WIDTH: 600, HEIGHT: 12, YPOS: 127, // 繪制到 canvas 中的 y 坐標 };
在 HorizonLine 原型鏈上添加方法:
HorizonLine.prototype = { // 初始化地面 init: function () { for (const d in HorizonLine.dimensions) { if (HorizonLine.dimensions.hasOwnProperty(d)) { const elem = HorizonLine.dimensions[d]; this.dimensions[d] = elem; } } this.sourceXPos = [this.spritePos.x, this.spritePos.x + this.dimensions.WIDTH]; this.xPos = [0, HorizonLine.dimensions.WIDTH]; this.yPos = HorizonLine.dimensions.YPOS; }, // 繪制地面 draw: function () { // 使用 canvas 中 9 個參數的 drawImage 方法 this.ctx.drawImage( Runner.imageSprite, // 原圖片 this.sourceXPos[0], this.spritePos.y, // 原圖中裁剪區域的起點坐標 this.dimensions.WIDTH, this.dimensions.HEIGHT, this.xPos[0], this.yPos, // canvas 中繪制區域的起點坐標 this.dimensions.WIDTH, this.dimensions.HEIGHT, ); this.ctx.drawImage( Runner.imageSprite, this.sourceXPos[1], this.spritePos.y, this.dimensions.WIDTH, this.dimensions.HEIGHT, this.xPos[1], this.yPos, this.dimensions.WIDTH, this.dimensions.HEIGHT, ); }, };
背景類 Horizon 負責管理 HorizonLine、Cloud、Obstacle、NightMode 這幾個類。
所以接下來需要通過 Horizon 類來調用 HorizonLine 類。
背景類 Horizon定義背景類 Horizon:
/** * 背景類 * @param {HTMLCanvasElement} canvas 畫布 * @param {Object} spritePos 雪碧圖中的位置 */ function Horizon(canvas, spritePos) { this.canvas = canvas; this.ctx = this.canvas.getContext("2d"); this.spritePos = spritePos; // 地面 this.horizonLine = null; this.init(); }
在 Horizon 原型鏈上添加方法:
Horizon.prototype = { // 初始化背景 init: function () { this.horizonLine = new HorizonLine(this.canvas, this.spritePos.HORIZON); }, };
最后,通過調用 Runner 類來運行游戲:
index.js:
window.onload = function () { var chromeDino = document.getElementById("chrome-dino"); chromeDino.classList.add("offline"); new Runner("#chrome-dino"); };
到這里,不出意外的話,就可以繪制出靜態的地面,如圖:
查看完整的代碼:戳這里
這里各個方法和類之間的調用邏輯是(箭頭代指調用):
new Runner() -> loadImage() // Runner -> init() // Runner -> new Horizon() -> init() // Horizon -> new HorizonLine() -> init() // HorizonLine -> draw() // HorizonLine
簡單來說就是:游戲主體類 Runner 控制背景類 Horizon,再由背景類 Horizon 控制地面類 HorizonLine。
遵循的思想就是把游戲層層抽象,由抽象程度高的類一層一層向下調用抽象程度低的類。這樣做的好處是,思路清晰并且易于擴展。
上一篇 | 下一篇 | 無 ?????????? ?????????? | Chrome 小恐龍游戲源碼探究二 -- 讓地面動起來 |
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/103890.html
摘要:文章首發于我的博客前言上一篇文章小恐龍游戲源碼探究一繪制靜態地面中定義了游戲的主體類,并實現了靜態地面的繪制。 文章首發于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究一 -- 繪制靜態地面》 中定義了游戲的主體類 Runner,并實現了靜態地面的繪制。這一篇文章中,將實現效果:1、地面無限滾動。2、剛開始地面不動,按下空格后地面滾動。 地面無限滾動 ...
摘要:例如,將函數修改為小恐龍眨眼這樣小恐龍會不停的眨眼睛。小恐龍的開場動畫下面來實現小恐龍對鍵盤按鍵的響應。接下來還需要更新動畫幀才能實現小恐龍的奔跑動畫。 文章首發于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究七 -- 晝夜模式交替》實現了游戲晝夜模式的交替,這一篇文章中,將實現:1、小恐龍的繪制 2、鍵盤對小恐龍的控制 3、頁面失焦后,重新聚焦會重置...
摘要:文章首發于我的博客前言上一篇文章小恐龍游戲源碼探究二讓地面動起來實現了地面的移動。街機模式的效果就是游戲開始后,進入全屏模式。例如可以看到,進入街機模式之前,有一段開場動畫。 文章首發于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究二 -- 讓地面動起來》 實現了地面的移動。這一篇文章中,將實現效果:1、瀏覽器失焦時游戲暫停,聚焦游戲繼續。 2、開場動...
摘要:文章首發于我的博客前言上一篇文章小恐龍游戲源碼探究四隨機繪制云朵實現了云朵的隨機繪制,這一篇文章中將實現仙人掌翼龍障礙物的繪制游戲速度的改變障礙物的類型有兩種仙人掌和翼龍。 文章首發于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究四 -- 隨機繪制云朵》 實現了云朵的隨機繪制,這一篇文章中將實現:1、仙人掌、翼龍障礙物的繪制 2、游戲速度的改變 障礙物...
摘要:文章首發于我的博客前言上一篇文章小恐龍游戲源碼探究三進入街機模式實現了開場動畫和街機模式。 文章首發于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究三 -- 進入街機模式》 實現了開場動畫和街機模式。這一篇文章中,將實現云朵的隨機繪制。 云朵類 Cloud 定義云朵類 Cloud: /** * 云朵類 * @param {HTMLCanvasEle...
閱讀 2238·2021-11-15 11:39
閱讀 994·2021-09-26 09:55
閱讀 937·2021-09-04 16:48
閱讀 2846·2021-08-12 13:23
閱讀 927·2021-07-30 15:30
閱讀 2461·2019-08-29 14:16
閱讀 892·2019-08-26 10:15
閱讀 533·2019-08-23 18:40