方形瓦片地圖實現:靜態地圖

本文介紹如何使用 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.