Skip to content
New issue

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 小恐龙游戏源码探究四 -- 随机绘制云朵 #7

Open
liuyib opened this issue Apr 16, 2019 · 0 comments
Open

Chrome 小恐龙游戏源码探究四 -- 随机绘制云朵 #7

liuyib opened this issue Apr 16, 2019 · 0 comments

Comments

@liuyib
Copy link
Owner

liuyib commented Apr 16, 2019

前言

上一篇文章:《Chrome 小恐龙游戏源码探究三 -- 进入街机模式》 实现了开场动画和街机模式。这一篇文章中,将实现云朵的随机绘制。

云朵类 Cloud

定义云朵类 Cloud

/**
 * 云朵类
 * @param {HTMLCanvasElement} canvas 画布
 * @param {Object} spritePos 图片在雪碧图中的位置信息
 * @param {Number} containerWidth 容器的宽度
 */
function Cloud(canvas, spritePos, containerWidth) {
  this.canvas = canvas;
  this.ctx = canvas.getContext('2d');
  this.spritePos = spritePos;
  this.containerWidth = containerWidth;

  // 坐标
  this.xPos = containerWidth;
  this.yPos = 0;

  // 该云朵是否需要删除
  this.remove = false;
  // 随机云朵之间的间隙
  this.cloudGap = getRandomNum(Cloud.config.MIN_CLOUD_GAP,
    Cloud.config.MAX_CLOUD_GAP);

  this.init();
}

相关的配置参数:

Cloud.config = {
  WIDTH: 46,
  HEIGHT: 14,
  MIN_CLOUD_GAP: 100,   // 云之间的最小间隙
  MAX_CLOUD_GAP: 400,   // 云之间的最大间隙
  MIN_SKY_LEVEL: 71,    // 云的最小高度
  MAX_SKY_LEVEL: 30,    // 云的最大高度
  BG_CLOUD_SPEED: 0.2,  // 云的速度
  CLOUD_FREQUENCY: 0.5, // 云的频率
  MAX_CLOUDS: 6         // 云的最大数量
};

补充本篇文章中会用到的数据:

Runner.spriteDefinition = {
  LDPI: {
    // ...

+   CLOUD: {x: 86, y: 2},
  },
};

绘制和更新云朵的方法定义如下:

Cloud.prototype = {
  // 初始化云朵
  init: function () {
    this.yPos = getRandomNum(Cloud.config.MAX_SKY_LEVEL,
      Cloud.config.MIN_SKY_LEVEL);
    this.draw();
  },
  // 绘制云朵
  draw: function () {
    this.ctx.save();

    var sourceWidth = Cloud.config.WIDTH;
    var sourceHeight = Cloud.config.HEIGHT;
    var outputWidth = sourceWidth;
    var outputHeight = sourceHeight;

    this.ctx.drawImage(
      Runner.imageSprite,
      this.spritePos.x, this.spritePos.y,
      sourceWidth, sourceHeight,
      this.xPos, this.yPos,
      outputWidth, outputHeight
    );
    
    this.ctx.restore();
  },
  // 更新云朵
  update: function (speed) {
    if (!this.remove) {
      this.xPos -= speed;
      this.draw();

      // 云朵移出 canvas,将其删除
      if (!this.isVisible()) {
        this.remove = true;
      }
    }
  },
  // 云朵是否移出 canvas
  isVisible: function () {
    return this.xPos + Cloud.config.WIDTH > 0;
  },
};

/**
 * 获取 [min, max] 之间的随机数
 * @param {Number} min 最小值
 * @param {Number} max 最大值
 * @return {Number}
 */
function getRandomNum(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

然后需要通过 Horizon 类调用 Cloud 类。首先在 Horizon 类中添加与云朵相关的属性:

- function Horizon(canvas, spritePos) {
+ function Horizon(canvas, spritePos, dimensions) {
    this.canvas = canvas;
    this.ctx = this.canvas.getContext('2d');
    this.spritePos = spritePos;

+   this.dimensions = dimensions;

+   // 云的频率
+   this.cloudFrequency = Cloud.config.CLOUD_FREQUENCY;

+   // 云
+   this.clouds = [];
+   this.cloudSpeed = Cloud.config.BG_CLOUD_SPEED;
    
    // 地面
    this.horizonLine = null;

    this.init();
  }

修改在 Runner 中调用 Horizon 类时传入的参数:

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

+   // 加载背景类 Horizon
-   this.horizon = new Horizon(this.canvas, this.spriteDef);
+   this.horizon = new Horizon(this.canvas, this.spriteDef,
+     this.dimensions);

    // ...
  },
};

添加生成云朵的方法,将生成的云朵存入数组 clouds

Horizon.prototype = {
  // 添加新的云朵
  addCloud: function () {
    this.clouds.push(new Cloud(this.canvas, this.spritePos.CLOUD,
      this.dimensions.WIDTH));
  },
};

调用 addCloud 方法来初始化云朵:

Horizon.prototype = {
  init: function () {
+   this.addCloud();
    this.horizonLine = new HorizonLine(this.canvas, this.spritePos.HORIZON);
  },
};

添加更新云朵的方法:

Horizon.prototype = {
  // 更新 canvas 上的云朵
  updateCloud: function (deltaTime, speed) {
    var cloudSpeed = Math.ceil(deltaTime * this.cloudSpeed * speed / 1000);
    var numClouds = this.clouds.length;

    if (numClouds) {
      for (var i = numClouds - 1; i >= 0; i--) {
        this.clouds[i].update(cloudSpeed);
      }

      var lastCloud = this.clouds[numClouds - 1];

      // 检查是否需要添加新的云朵
      // 添加云朵的条件:云朵数量少于最大数量、
      // 最后一个云朵后面的空间大于它的间隙、
      // 云朵出现频率符合要求
      if (numClouds < Cloud.config.MAX_CLOUDS &&
        (this.dimensions.WIDTH - lastCloud.xPos) > lastCloud.cloudGap &&
        this.cloudFrequency > Math.random()) {
        this.addCloud();
      }

      // 删除 remove 属性为 true 的云朵
      this.clouds = this.clouds.filter(function (item) {
        return !item.remove;
      });
    } else {
      this.addCloud();
    }
  },
};

调用 updateCloud 方法,来实现对云朵的更新:

Horizon.prototype = {
  update: function (deltaTime, currentSpeed) {
    this.horizonLine.update(deltaTime, currentSpeed);
+   this.updateCloud(deltaTime, currentSpeed);
  },
};

运行效果如下:

add-cloud

查看添加或修改的代码:戳这里

Demo 体验地址:https://liuyib.github.io/demo/game/google-dino/add-cloud/

上一篇 下一篇
Chrome 小恐龙游戏源码探究三 -- 进入街机模式 Chrome 小恐龙游戏源码探究五 -- 随机绘制障碍
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant