地图就是一个 100x100 的棋盘,每一个格子有一个 世界ID,这样有碰撞的实体碰到切换开关的时候,应用 BFS/DFS 算法,更新联通的格子(世界ID相同的视为联通)。

然后看渲染内容,我们只需要渲染 100x100 个格子就可以,每一个格子的可能性无非是预制的 5x5 贴图中的一种。传统渲染时确定每一个像素点的颜色,我们这里的渲染是确定每一个格子是什么贴图。唯一特殊一点的是,这个游戏分了三层,玩家层始终在最上方,背景层始终在最下方,墙壁、箱子则是在中间。

其中玩家的位置好处理,单独记录,然后渲染在最上层就可以。

墙壁和空白背景都是静态的,放在一起处理。

然后是“箱子”,他的位置和交互由规则系统处理。

一套统一的移动求解器,和 6 个世界的规则模块。

讨论基础的瓦片

World_Id :: enum u8 {
    NONE;
    ONE;
    TWO;
    THREE;
    FOUR;
    FIVE;
    SIX;
}

Tile_Type :: enum u8 {
    FLOOR;
    WALL;
}

Tell :: struct {
	world : World_Id;
	tile : Tile_Type;
	
	entity : Entity_Id; // 0 = none
}

map: [100][100]Tile;

渲染的时候发现 world(x,y) != world(x+1,y) 就绘制一套竖向的边界,横向同理。

讨论实体如何设计

Entity_Type :: enum {
    PLAYER;
    BOX;
}

Entity :: struct {
    type: Entity_Type;
    pos: Vector2i;

    // 临时状态
    move_dir: Direction;
    moving: bool;

    flags: Entity_Flags;
}

移动

Move_Intent :: struct {
    entity: Entity_Id;
    dir: Direction;
}

例如,玩家按下 RIGHT,首先只是产生 Intent(Player, RIGHT) 然后进入 movement solver

规则系统计算哪些移动成功,最后统一修改坐标。

image.png

image.png

(one)
[> p 1 | o ...] -> [> p 1 | > o]