读取关卡
下面的类存储了关卡对象。它的参数应该是定义关卡的字符串。
class Level {
constructor(plan) {
let rows = plan.trim().split("\n").map(l => [...l]);
this.height = rows.length;
this.width = rows[0].length;
this.startActors = [];
this.rows = rows.map((row, y) => {
return row.map((ch, x) => {
let type = levelChars[ch];
if (typeof type == "string") return type;
this.startActors.push(
type.create(new Vec(x, y), ch));
return "empty";
});
});
}
}
trim
方法用于移除平面图字符串起始和终止处的空白。这允许我们的示例平面图以换行开始,以便所有行都在彼此的正下方。其余的字符串由换行符拆分,每一行扩展到一个数组中,生成了字符数组。
因此,rows
包含字符数组、平面图的行。我们可以从中得出水平宽度和高度。但是我们仍然必须将可移动元素与背景网格分开。我们将其称为角色(Actor)。它们将存储在一个对象数组中。背景将是字符串的数组的数组,持有字段类型,如"empty"
,"wall"
,或"lava"
。
为了创建这些数组,我们在行上映射,然后在它们的内容上进行映射。请记住,map
将数组索引作为第二个参数传递给映射函数,它告诉我们给定字符的x
和y
坐标。游戏中的位置将存储为一对坐标,左上角为0, 0
,并且每个背景方块为 1 单位高和宽。
为了解释平面图中的字符,Level
构造器使用levelChars
对象,它将背景元素映射为字符串,角色字符映射为类。当type
是一个角色类时,它的create
静态方法用于创建一个对象,该对象被添加到startActors
,映射函数为这个背景方块返回"empty"
。
角色的位置存储为一个Vec
对象,它是二维向量,一个具有x
和y
属性的对象,像第六章一样。
当游戏运行时,角色将停在不同的地方,甚至完全消失(就像硬币被收集时)。我们将使用一个State
类来跟踪正在运行的游戏的状态。
class State {
constructor(level, actors, status) {
this.level = level;
this.actors = actors;
this.status = status;
}
static start(level) {
return new State(level, level.startActors, "playing");
}
get player() {
return this.actors.find(a => a.type == "player");
}
}
当游戏结束时,status
属性将切换为"lost"
或"won"
。
这又是一个持久性数据结构,更新游戏状态会创建新状态,并使旧状态保持完整。