Chrome 小恐龙游戏源码探讨二 -- 让地面动起来

文章首发于我的
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();
}

末了在 Runnerinit 要领中挪用它的 update 要领:

Runner.prototype = {
  init: function () {
    // ...

+   // 更新 canvas
+   this.update();
  },
};

因为类层层笼统的缘由,要领的也须要层层挪用。

如今地面就能够举行无穷转动了,结果以下:

《Chrome 小恐龙游戏源码探讨二 -- 让地面动起来》

你能够经由过程检察我的
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',
};

然后在 Runnerinit 要领中挪用 startListening 要领,来监听键盘的 keydownkeyup 事宜:

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 事宜的诠释。

到这里,就完成了地面对空格键的相应,结果以下:

《Chrome 小恐龙游戏源码探讨二 -- 让地面动起来》

检察增加或修正的代码,
戳这里

Demo 体验地点:https://liuyib.github.io/blog/demo/game/google-dino/horizonline-move/

上一篇下一篇
Chrome 小恐龙游戏源码探讨一 — 绘制静态地面 Chrome 小恐龙游戏源码探讨三 — 进入街机形式
    原文作者:liuyib
    原文地址: https://segmentfault.com/a/1190000018998734
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞