摘要:文章首发于我的博客前言上一篇文章小恐龙游戏源码探究一绘制静态地面中定义了游戏的主体类,并实现了静态地面的绘制。
文章首发于我的 GitHub 博客前言
上一篇文章:《Chrome 小恐龙游戏源码探究一 -- 绘制静态地面》 中定义了游戏的主体类 Runner,并实现了静态地面的绘制。这一篇文章中,将实现效果:1、地面无限滚动。2、刚开始地面不动,按下空格后地面滚动。
地面无限滚动要实现地面的移动就要不断更新地面的 x 坐标。定义如下方法:
HorizonLine.prototype = { /** * 更新地面的 x 坐标 * @param {Number} pos 地面的位置 * @param {Number} incre 移动距离 */ updateXPos: function (pos, incre) { var line1 = pos; var line2 = pos === 0 ? 1 : 0; // 第一段地面向左移动,第二段地面随之 this.xPos[line1] -= incre; this.xPos[line2] = this.xPos[line1] + this.dimensions.WIDTH; // 第一段地面移出了 canvas if (this.xPos[line1] <= -this.dimensions.WIDTH) { // 将第一段地面放到 canvas 右侧 this.xPos[line1] += this.dimensions.WIDTH * 2; // 此时第二段地面的 x 坐标刚好和 canvas 的 x 坐标对齐 this.xPos[line2] = this.xPos[line1] - this.dimensions.WIDTH; // 给放到 canvas 后面的地面随机地形 this.sourceXPos[line1] = this.getRandomType() + this.spritePos.x; } }, // 获取随机的地形 getRandomType: function () { return Math.random() > this.bumpThreshold ? this.dimensions.WIDTH : 0; }, };
其中 updateXPos 实现了地面 x 坐标的更新。当第一段地面移出 canvas 时,会将第一段地面 x 坐标乘 2 放到 canvas 后面,然后为其随机地形。这时,原来的第二段地面就变成了第一段地面继续向前移动,以此类推。这样就实现了地面的不断移动和更新。
上面的函数只是实现了地面移动相关的逻辑,真正让地面动起来还需要调用这个函数。添加方法:
HorizonLine.prototype = { /** * 更新地面 * @param {Number} deltaTime 间隔时间 * @param {Number} speed 速度 */ update: function (deltaTime, speed) { // 计算地面每次移动的距离(距离 = 速度 x 时间)时间由帧率和间隔时间共同决定 var incre = Math.floor(speed * (FPS / 1000) * deltaTime); if (this.xPos[0] <= 0) { this.updateXPos(0, incre); } else { this.updateXPos(1, incre); } this.draw(); }, };
然后需要通过 Horizon 上的 update 方法来调用 HorizonLine 上的 update 方法:
Horizon.prototype = { // 更新背景 update: function (deltaTime, currentSpeed) { this.horizonLine.update(deltaTime, currentSpeed); }, };
同理,按照上面的逻辑,现在应该在 Runner 上定义 update 方法来调用 Horizon 上的 update 方法:
Runner.prototype = { // 更新游戏帧并进行下一次更新 update: function () { this.updatePending = false; // 等待更新 var now = getTimeStamp(); var deltaTime = now - (this.time || now); this.time = now; this.clearCanvas(); this.horizon.update(deltaTime, this.currentSpeed); // 进行下一次更新 this.scheduleNextUpdate(); }, // 清空 canvas clearCanvas: function () { this.ctx.clearRect(0, 0, this.dimensions.WIDTH, this.dimensions.HEIGHT); }, // 进行下一次更新 scheduleNextUpdate: function () { if (!this.updatePending) { this.updatePending = true; this.raqId = requestAnimationFrame(this.update.bind(this)); } }, }; // 获取时间戳 function getTimeStamp() { return performance.now(); }
最后在 Runner 的 init 方法中调用它的 update 方法:
Runner.prototype = { init: function () { // ... + // 更新 canvas + this.update(); }, };
由于类层层抽象的原因,方法的也需要层层调用。
现在地面就可以进行无限滚动了,效果如下:
你可以通过查看我的 commit 信息,来查看添加或修改的代码:戳这里响应空格键
下面来实现地面对空格键的响应,具体效果就是,初始地面不动,按下空格键后地面移动。
修改 Runner 原型链中的 update 方法:
Runner.prototype = { update: function () { var now = getTimeStamp(); var deltaTime = now - (this.time || now); this.time = now; + if (this.playing) { this.clearCanvas(); this.horizon.update(deltaTime, this.currentSpeed); + } + if (this.playing) { // 进行下一次更新 this.scheduleNextUpdate(); + } }, };
监听键盘事件:
Runner.prototype = { startListening: function () { document.addEventListener(Runner.events.KEYDOWN, this); document.addEventListener(Runner.events.KEYUP, this); }, stopListening: function () { document.removeEventListener(Runner.events.KEYDOWN, this); document.removeEventListener(Runner.events.KEYUP, this); }, };
添加数据:
Runner.events = { // ... + KEYDOWN: "keydown", + KEYUP: "keyup", };
然后在 Runner 的 init 方法中调用 startListening 方法,来监听键盘的 keydown 和 keyup 事件:
Runner.prototype = { init: function () { // ... + // 开始监听用户动作 + this.startListening(); }, };
当浏览器监听到用户按下键盘时,会执行 handleEvent 方法来处理键盘事件:
Runner.prototype = { // 用来处理 EventTarget(这里就是 Runner 类) 上发生的事件 // 当事件被发送到 EventListener 时,浏览器就会自动调用这个方法 handleEvent: function (e) { return (function (eType, events) { switch (eType) { case events.KEYDOWN: this.onKeyDown(e); break; default: break; } }.bind(this))(e.type, Runner.events); }, };
上面用到的 onKeyDown 方法定义如下:
Runner.prototype = { onKeyDown: function (e) { if (!this.crashed && !this.paused) { if (Runner.keyCodes.JUMP[e.keyCode]) { e.preventDefault(); if (!this.playing) { this.setPlayStatus(true); this.update(); } } } }, // 设置游戏是否为进行状态 setPlayStatus: function (isPlaying) { this.playing = isPlaying; }, };
这里需要提一下,当按下空格键后, 为什么浏览器会执行 handleEvent 事件:原因是当使用 addEventListener 监听某个对象上的事件时,只要被监听的事件触发了,就会执行该对象上名字为 handleEvent 的方法(如果有)。MDN 上有对 handleEvent 事件的解释。
到这里,就实现了地面对空格键的响应,效果如下:
查看添加或修改的代码,戳这里
Demo 体验地址:https://liuyib.github.io/blog/demo/game/google-dino/horizonline-move/
上一篇 | 下一篇 | Chrome 小恐龙游戏源码探究一 -- 绘制静态地面 | Chrome 小恐龙游戏源码探究三 -- 进入街机模式 |
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/103885.html
摘要:首先是绘制静态的地面。上一篇下一篇无小恐龙游戏源码探究二让地面动起来 文章首发于我的 GitHub 博客 目录 Chrome 小恐龙游戏源码探究一 -- 绘制静态地面 Chrome 小恐龙游戏源码探究二 -- 让地面动起来 Chrome 小恐龙游戏源码探究三 -- 进入街机模式 Chrome 小恐龙游戏源码探究四 -- 随机绘制云朵 Chrome 小恐龙游戏源码探究五 -- 随机绘...
摘要:文章首发于我的博客前言上一篇文章小恐龙游戏源码探究二让地面动起来实现了地面的移动。街机模式的效果就是游戏开始后,进入全屏模式。例如可以看到,进入街机模式之前,有一段开场动画。 文章首发于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龙游戏源码探究二 -- 让地面动起来》 实现了地面的移动。这一篇文章中,将实现效果:1、浏览器失焦时游戏暂停,聚焦游戏继续。 2、开场动...
摘要:例如,将函数修改为小恐龙眨眼这样小恐龙会不停的眨眼睛。小恐龙的开场动画下面来实现小恐龙对键盘按键的响应。接下来还需要更新动画帧才能实现小恐龙的奔跑动画。 文章首发于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龙游戏源码探究七 -- 昼夜模式交替》实现了游戏昼夜模式的交替,这一篇文章中,将实现:1、小恐龙的绘制 2、键盘对小恐龙的控制 3、页面失焦后,重新聚焦会重置...
摘要:文章首发于我的博客前言上一篇文章小恐龙游戏源码探究四随机绘制云朵实现了云朵的随机绘制,这一篇文章中将实现仙人掌翼龙障碍物的绘制游戏速度的改变障碍物的类型有两种仙人掌和翼龙。 文章首发于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龙游戏源码探究四 -- 随机绘制云朵》 实现了云朵的随机绘制,这一篇文章中将实现:1、仙人掌、翼龙障碍物的绘制 2、游戏速度的改变 障碍物...
摘要:文章首发于我的博客前言上一篇文章小恐龙游戏源码探究三进入街机模式实现了开场动画和街机模式。 文章首发于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龙游戏源码探究三 -- 进入街机模式》 实现了开场动画和街机模式。这一篇文章中,将实现云朵的随机绘制。 云朵类 Cloud 定义云朵类 Cloud: /** * 云朵类 * @param {HTMLCanvasEle...
阅读 2736·2021-09-24 10:34
阅读 1839·2021-09-22 10:02
阅读 2235·2021-09-09 09:33
阅读 1444·2021-08-13 15:02
阅读 3254·2020-12-03 17:10
阅读 1142·2019-08-30 15:44
阅读 2131·2019-08-30 12:58
阅读 3216·2019-08-26 13:40