chrome 恐龙 html源码,Chrome 小恐龙游戏源码探究二 -- 让地面动起来

前言

上一篇文章:《 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 的原型链上添加方法:

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();

},

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();

},

};

现在地面就可以进行无限滚动了,效果如下:

c0b451f21b16a1e4ca13529092bcc423.gif

查看添加的代码: 戳这里

响应空格键

下面要实现的效果是:刚开始地面不动,按下空格后地面滚动。

修改 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 方法:

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 的方法(如果有)。

到此,就实现了预期的效果:

e80b8942cfe05869174ad68f2e8e78d0.gif

查看添加的代码, 戳这里

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值