We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
上一篇文章:《Chrome 小恐龙游戏源码探究一 -- 绘制静态地面》 中定义了游戏的主体类 Runner,并实现了静态地面的绘制。这一篇文章中,将实现效果:1、地面无限滚动。2、刚开始地面不动,按下空格后地面滚动。
要实现地面的移动就要不断更新地面的 x 坐标。定义如下方法:
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 后面,然后为其随机地形。这时,原来的第二段地面就变成了第一段地面继续向前移动,以此类推。这样就实现了地面的不断移动和更新。
updateXPos
canvas
2
上面的函数只是实现了地面移动相关的逻辑,真正让地面动起来还需要调用这个函数。添加方法:
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
update
HorizonLine
Horizon.prototype = { // 更新背景 update: function (deltaTime, currentSpeed) { this.horizonLine.update(deltaTime, currentSpeed); }, };
同理,按照上面的逻辑,现在应该在 Runner 上定义 update 方法来调用 Horizon 上的 update 方法:
Runner
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 方法:
init
Runner.prototype = { init: function () { // ... + // 更新 canvas + this.update(); }, };
由于类层层抽象的原因,方法的也需要层层调用。
现在地面就可以进行无限滚动了,效果如下:
你可以通过查看我的 commit 信息,来查看添加或修改的代码:戳这里
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 事件:
startListening
keydown
keyup
Runner.prototype = { init: function () { // ... + // 开始监听用户动作 + this.startListening(); }, };
当浏览器监听到用户按下键盘时,会执行 handleEvent 方法来处理键盘事件:
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 方法定义如下:
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 事件的解释。
addEventListener
到这里,就实现了地面对空格键的响应,效果如下:
查看添加或修改的代码,戳这里
Demo 体验地址:https://liuyib.github.io/demo/game/google-dino/horizonline-move/
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
上一篇文章:《Chrome 小恐龙游戏源码探究一 -- 绘制静态地面》 中定义了游戏的主体类 Runner,并实现了静态地面的绘制。这一篇文章中,将实现效果:1、地面无限滚动。2、刚开始地面不动,按下空格后地面滚动。
地面无限滚动
要实现地面的移动就要不断更新地面的
x
坐标。定义如下方法:其中
updateXPos
实现了地面x
坐标的更新。当第一段地面移出canvas
时,会将第一段地面x
坐标乘2
放到canvas
后面,然后为其随机地形。这时,原来的第二段地面就变成了第一段地面继续向前移动,以此类推。这样就实现了地面的不断移动和更新。上面的函数只是实现了地面移动相关的逻辑,真正让地面动起来还需要调用这个函数。添加方法:
然后需要通过
Horizon
上的update
方法来调用HorizonLine
上的update
方法:同理,按照上面的逻辑,现在应该在
Runner
上定义update
方法来调用Horizon
上的update
方法:最后在
Runner
的init
方法中调用它的update
方法:由于类层层抽象的原因,方法的也需要层层调用。
现在地面就可以进行无限滚动了,效果如下:
响应空格键
下面来实现地面对空格键的响应,具体效果就是,初始地面不动,按下空格键后地面移动。
修改
Runner
原型链中的update
方法:监听键盘事件:
添加数据:
然后在
Runner
的init
方法中调用startListening
方法,来监听键盘的keydown
和keyup
事件:当浏览器监听到用户按下键盘时,会执行
handleEvent
方法来处理键盘事件:上面用到的
onKeyDown
方法定义如下:这里需要提一下,当按下空格键后, 为什么浏览器会执行 handleEvent 事件:原因是当使用
addEventListener
监听某个对象上的事件时,只要被监听的事件触发了,就会执行该对象上名字为handleEvent
的方法(如果有)。MDN 上有对handleEvent
事件的解释。到这里,就实现了地面对空格键的响应,效果如下:
Demo 体验地址:https://liuyib.github.io/demo/game/google-dino/horizonline-move/
The text was updated successfully, but these errors were encountered: