方形瓦片地图实现:静态地图
本文介绍如何使用 Canvas API 实现静态方形瓦片地图。
注意:撰写本文时,我们假设读者已具备 Canvas 基础知识,例如如何获取 2D Canvas 上下文、加载图像等,这些内容在 Canvas API 教程 中有详细解释,以及我们 瓦片地图 简介文章中包含的基本信息。
瓦片图集
瓦片地图可能使用一个或多个图集——或精灵表——其中包含所有瓦片图像。这是我们将用作示例的图集,其中包含五个不同的瓦片。
要将瓦片从图集绘制到画布上,我们使用 Canvas 2D 上下文中的 drawImage()
方法。我们需要提供图集图像、图集内瓦片的坐标和尺寸,以及目标坐标和大小(此处不同的瓦片大小会缩放瓦片)。
例如,要绘制树瓦片(图集中的第三个瓦片)到屏幕坐标 (128, 320)
,我们将使用以下值调用 drawImage()
context.drawImage(atlasImage, 192, 0, 64, 64, 128, 320, 64, 64);
为了支持具有多行多列的图集,您需要知道有多少行和列,以便能够计算源 x
和 y
。
瓦片地图数据结构
为了存储地图数据,我们可以使用普通对象或自定义类。为了简单起见,示例代码中使用了普通对象。它包含基本的地图属性
cols
:地图宽度,以列为单位。rows
:地图高度,以行为单位。tsize
:瓦片大小,以像素为单位。tiles
:包含视觉网格的一维数组。getTile()
:获取特定位置瓦片索引的辅助方法。
tiles
包含实际的视觉地图数据。我们使用索引表示瓦片,根据其在图集中的位置分配给瓦片(例如,最左侧的瓦片为 0
)。但是,我们必须考虑空瓦片,因为它们对于实现图层至关重要——空瓦片通常分配负索引值、0
或空值。在这些示例中,空瓦片将由索引 0
表示,因此我们将图集的索引偏移一个(因此图集的第一个瓦片将分配索引 1
,第二个索引 2
,依此类推)。
getTile()
辅助方法返回指定列和行中包含的瓦片。如果 tiles
是一个二维矩阵,则返回值将只是 tiles[column][row]
。但是,通常更常见的是使用一维数组表示网格。在这种情况下,我们需要将列和行映射到数组索引。
const index = row * map.cols + column;
总结一下,瓦片地图对象的示例可能如下所示。它具有一个 8 x 8 的地图,瓦片大小为 64 x 64 像素。
const map = {
cols: 8,
rows: 8,
tsize: 64,
tiles: [
1, 3, 3, 3, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1,
1, 1, 2, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1,
],
getTile(col, row) {
return this.tiles[row * map.cols + col];
},
};
渲染地图
我们可以通过遍历地图的列和行来渲染地图。此代码段假设以下定义
context
:2D Canvas 上下文。tileAtlas
:包含瓦片图集的图像对象。map
:上面讨论的瓦片地图对象。
for (let c = 0; c < map.cols; c++) {
for (let r = 0; r < map.rows; r++) {
const tile = map.getTile(c, r);
if (tile !== 0) {
// 0 => empty tile
context.drawImage(
tileAtlas, // image
(tile - 1) * map.tsize, // source x
0, // source y
map.tsize, // source width
map.tsize, // source height
c * map.tsize, // target x
r * map.tsize, // target y
map.tsize, // target width
map.tsize, // target height
);
}
}
}