方形瓦片地图实现:静态地图

本文介绍如何使用 Canvas API 来实现静态方形瓦片地图。

注意: 在撰写本文时,我们假设读者已具备 canvas 基础知识,例如如何获取 2D canvas 上下文、加载图像等,这些内容都在 Canvas API 教程 中进行了详细介绍,并且包含了我们 瓦片地图 入门文章中的基本信息。

瓦片图集

瓦片地图可能会使用一个或多个图集——或称精灵图——来包含所有瓦片图像。我们将使用以下图集作为示例,其中包含五种不同的瓦片:

Tiles packaged in an atlas

为了将图集中的瓦片绘制到 canvas 上,我们使用了 2D canvas 上下文中的 drawImage() 方法。我们需要提供图集图像、图集内瓦片的坐标和尺寸,以及目标坐标和尺寸(此处不同的瓦片尺寸将导致瓦片缩放)。

因此,例如,要绘制图集中的第三个瓦片——树瓦片,在屏幕坐标 (128, 320) 处,我们将调用 drawImage() 并传入以下值:

js
context.drawImage(atlasImage, 192, 0, 64, 64, 128, 320, 64, 64);

为了支持包含多行多列的图集,您需要知道有多少行和多少列,以便能够计算出源 xy 坐标。

瓦片地图数据结构

为了存储地图数据,我们可以使用一个普通对象或自定义类。为了简化起见,示例代码中使用了普通对象。它包含了基本的地图属性:

  • cols:地图的宽度,以列为单位。
  • rows:地图的高度,以行为单位。
  • tsize:瓦片的大小,以像素为单位。
  • tiles:一个包含视觉网格的一维数组。
  • getTile():一个辅助方法,用于获取特定位置的瓦片索引。

tiles 包含实际的视觉地图数据。我们用索引来表示瓦片,这些索引是根据瓦片在图集中的位置分配的(例如,最左边的瓦片索引为 0)。但是,我们必须考虑空瓦片,因为它们对于实现图层至关重要——空瓦片通常被赋予一个负索引值、0 或 null 值。在这些示例中,空瓦片将由索引 0 表示,因此我们将图集瓦片的索引偏移一位(这样图集中的第一个瓦片将被赋予索引 1,第二个索引 2,依此类推)。

getTile() 辅助方法返回指定列和行所包含的瓦片。如果 tiles 是一个二维矩阵,则返回值将是 tiles[column][row]。然而,通常更常见的做法是用一维数组来表示网格。在这种情况下,我们需要将列和行映射到一个数组索引:

js
const index = row * map.cols + column;

总而言之,一个瓦片地图对象的例子可能如下所示。它包含一个 8x8 的地图,瓦片大小为 64x64 像素:

js
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:上面讨论的瓦片地图对象。
js
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
      );
    }
  }
}

演示

我们的静态瓦片地图实现演示将上述代码整合在一起,展示了这种地图的实现效果。您可以查看 在线演示 并获取 完整的源代码

Aerial view of a field with trees, grass, and ground made from repeated sections of the tilemap.