读取关卡

下面的类存储了关卡对象。它的参数应该是定义关卡的字符串。

  1. class Level {
  2. constructor(plan) {
  3. let rows = plan.trim().split("\n").map(l => [...l]);
  4. this.height = rows.length;
  5. this.width = rows[0].length;
  6. this.startActors = [];
  7. this.rows = rows.map((row, y) => {
  8. return row.map((ch, x) => {
  9. let type = levelChars[ch];
  10. if (typeof type == "string") return type;
  11. this.startActors.push(
  12. type.create(new Vec(x, y), ch));
  13. return "empty";
  14. });
  15. });
  16. }
  17. }

trim方法用于移除平面图字符串起始和终止处的空白。这允许我们的示例平面图以换行开始,以便所有行都在彼此的正下方。其余的字符串由换行符拆分,每一行扩展到一个数组中,生成了字符数组。

因此,rows包含字符数组、平面图的行。我们可以从中得出水平宽度和高度。但是我们仍然必须将可移动元素与背景网格分开。我们将其称为角色(Actor)。它们将存储在一个对象数组中。背景将是字符串的数组的数组,持有字段类型,如"empty""wall",或"lava"

为了创建这些数组,我们在行上映射,然后在它们的内容上进行映射。请记住,map将数组索引作为第二个参数传递给映射函数,它告诉我们给定字符的xy坐标。游戏中的位置将存储为一对坐标,左上角为0, 0,并且每个背景方块为 1 单位高和宽。

为了解释平面图中的字符,Level构造器使用levelChars对象,它将背景元素映射为字符串,角色字符映射为类。当type是一个角色类时,它的create静态方法用于创建一个对象,该对象被添加到startActors,映射函数为这个背景方块返回"empty"

角色的位置存储为一个Vec对象,它是二维向量,一个具有xy属性的对象,像第六章一样。

当游戏运行时,角色将停在不同的地方,甚至完全消失(就像硬币被收集时)。我们将使用一个State类来跟踪正在运行的游戏的状态。

  1. class State {
  2. constructor(level, actors, status) {
  3. this.level = level;
  4. this.actors = actors;
  5. this.status = status;
  6. }
  7. static start(level) {
  8. return new State(level, level.startActors, "playing");
  9. }
  10. get player() {
  11. return this.actors.find(a => a.type == "player");
  12. }
  13. }

当游戏结束时,status属性将切换为"lost""won"

这又是一个持久性数据结构,更新游戏状态会创建新状态,并使旧状态保持完整。