读取关卡
下面的类存储了关卡对象。它的参数应该是定义关卡的字符串。
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"。
这又是一个持久性数据结构,更新游戏状态会创建新状态,并使旧状态保持完整。
