瓦片和瓦片地圖概述

瓦片地圖是 2D 遊戲開發中一種非常流行的技術,它由稱為瓦片的小型、規則形狀的影像構建遊戲世界或關卡地圖。這帶來了效能和記憶體使用方面的優勢——不需要包含整個關卡地圖的大影像檔案,因為它們是透過多次使用小型影像或影像片段構建的。本系列文章將介紹使用 JavaScriptCanvas 建立瓦片地圖的基礎知識(儘管相同的高階技術也可以用於任何程式語言)。

除了效能優勢之外,瓦片地圖還可以對映到一個邏輯網格,該網格可以在遊戲邏輯的其他方面使用(例如建立尋路圖,或處理碰撞),或者用於建立關卡編輯器。

一些使用這種技術的流行遊戲包括《超級馬里奧兄弟》、《吃豆人》、《塞爾達傳說:眾神的三角力量》、《星際爭霸》和《模擬城市 2000》。想想任何使用規則重複的背景方塊的遊戲,你很可能就會發現它使用了瓦片地圖。

瓦片圖集

儲存瓦片影像的最有效方法是將其放在圖集或精靈表中。這是所有必需的瓦片組合在一個影像檔案中。在繪製瓦片時,只渲染這個大影像的一小部分到遊戲畫布上。下面的圖片展示了一個 8x4 瓦片的圖集。

Tile atlas image

使用圖集還有一個優點,就是可以自然地為每個瓦片分配一個索引。這個索引非常適合用作建立瓦片地圖物件時的瓦片識別符號。

瓦片地圖資料結構

通常會將處理瓦片地圖所需的所有資訊分組到相同的資料結構或物件中。這些資料物件(地圖物件示例)應包含:

  • 瓦片大小:每個瓦片在畫素寬度 / 畫素高度上的尺寸。
  • 影像圖集:將要使用的影像圖集(一個或多個)。
  • 地圖尺寸:地圖的尺寸,以瓦片寬度 / 瓦片高度,或畫素寬度 / 畫素高度表示。
  • 視覺網格:包含索引,指示應將哪種型別的瓦片放置在網格的每個位置。
  • 邏輯網格:這可以是一個碰撞網格、一個尋路網格等,具體取決於遊戲的型別。

注意:對於視覺網格,需要一個特殊值(通常是負數、0null)來表示空瓦片。

方形瓦片

基於方形的瓦片地圖是最簡單的實現。更通用的情況是基於矩形的瓦片地圖——而不是方形——但它們遠不如方形常見。方形瓦片允許兩種視角

  • 俯視(例如許多 RPG 或策略遊戲,如《魔獸爭霸 2》或《最終幻想》的世界視角)。
  • 側視(例如平臺遊戲,如《超級馬里奧兄弟》)。

靜態瓦片地圖

瓦片地圖可以適配可見螢幕區域,也可以比螢幕大。在第一種情況下,瓦片地圖是靜態的——無需滾動即可完全顯示。這種情況在街機遊戲中很常見,例如《吃豆人》、《打磚塊》或《箱男》。

渲染靜態瓦片地圖很容易,可以透過巢狀迴圈遍歷列和行來完成。一個高層演算法可以是:

js
for (let column = 0; column < map.columns; column++) {
  for (let row = 0; row < map.rows; row++) {
    const tile = map.getTile(column, row);
    const x = column * map.tileSize;
    const y = row * map.tileSize;
    drawTile(tile, x, y);
  }
}

您可以閱讀更多關於此內容,並檢視實現示例,請參閱 方形瓦片地圖實現:靜態地圖

滾動瓦片地圖

滾動瓦片地圖一次只顯示世界的一小部分。它們可以跟隨角色——就像在平臺遊戲或 RPG 中一樣——或者允許玩家控制攝像機——就像在策略或模擬遊戲中一樣。

定位和攝像機

在所有滾動遊戲中,我們需要在世界座標(精靈或其他元素在關卡或遊戲世界中的位置)和螢幕座標(這些元素在螢幕上的實際渲染位置)之間進行轉換。世界座標可以根據遊戲的不同,以瓦片位置(地圖的行和列)或地圖的畫素寬度來表示。為了能夠將世界座標轉換為螢幕座標,我們需要攝像機的座標,因為它們決定了顯示的是世界的哪個部分。

以下示例展示瞭如何從世界座標轉換為螢幕座標以及反向轉換:

js
// these functions assume that the camera points to the top left corner

function worldToScreen(x, y) {
  return { x: x - camera.x, y: y - camera.y };
}

function screenToWorld(x, y) {
  return { x: x + camera.x, y: y + camera.y };
}

渲染

一種簡單的渲染方法只是遍歷所有瓦片(就像在靜態瓦片地圖中一樣),減去攝像機座標(如上面 `worldToScreen()` 示例所示),並讓落在檢視視窗外的部分留在那裡,隱藏起來。然而,繪製所有不可見的瓦片是浪費的,並且會影響效能。理想情況下,應該只渲染可見的瓦片——有關提高渲染效能的更多想法,請參閱 效能 部分。

您可以閱讀更多關於實現滾動瓦片地圖的內容,並檢視一些實現示例,請參閱 方形瓦片地圖實現:滾動地圖

圖層

視覺網格通常由多個圖層組成。這允許我們使用更少的瓦片建立更豐富的遊戲世界,因為相同的影像可以與不同的背景一起使用。例如,一個可以出現在多種地形型別(如草地、沙地或磚塊)上的岩石,可以包含在其自己的單獨瓦片中,然後渲染在新圖層上,而不是多個帶有不同背景地形的岩石瓦片。

如果角色或其他遊戲精靈繪製在圖層堆疊的中間,這可以產生有趣的效果,例如讓角色穿過樹木或建築物。

以下截圖展示了這兩個方面的示例:一個角色出現在瓦片後面(騎士出現在樹頂後面),以及一個瓦片(灌木叢)被渲染在不同地形型別之上。

A grid of layered background terrains. A bush tile is rendered at the top, over a large grass terrain, and again over a layered rectangular terrain with brown sand at the bottom. A tree tile is rendered over the grass terrain at the bottom left and again at the bottom right. A knight tile appears behind the tree tile that is rendered at the bottom left.

邏輯網格

由於瓦片地圖是實際的視覺瓦片網格,因此通常在此視覺網格和邏輯網格之間建立對映。最常見的情況是使用此邏輯網格來處理碰撞,但也有其他用途:角色生成點、檢測某些元素是否以正確的方式組合在一起以觸發特定操作(如《俄羅斯方塊》或《寶石迷陣》),尋路演算法等。

注意:您可以檢視我們的演示,其中展示了 如何使用邏輯網格來處理碰撞

等距瓦片地圖

等距瓦片地圖創造了 3D 環境的錯覺,在 2D 模擬、策略或 RPG 遊戲中非常流行。其中一些遊戲包括《模擬城市 2000》、《法老》或《最終幻想戰略版》。下圖展示了一個等距瓦片集圖集的示例。

A 3x4 map of variously colored tiles in isometric projection

效能

繪製滾動瓦片地圖可能會影響效能。通常,需要實現一些技術來使滾動順暢。第一種方法,如上所述,是僅繪製可見的瓦片。但有時這還不夠。

一種簡單的技術是預先將地圖渲染到一個單獨的 Canvas 上(使用 Canvas API 時),或渲染到一個紋理上(使用 WebGL 時),這樣就不需要在每一幀重新繪製瓦片,並且渲染可以一次完成blit操作。當然,如果地圖很大,這並不能真正解決問題——而且有些系統對紋理大小的限制並不太寬鬆。

一種方法是 在 Canvas 外繪製可見的部分(而不是整個地圖)。這意味著只要沒有滾動,就不需要渲染地圖。

這種方法的缺點是,當滾動時,該技術效率不高。更好的方法是建立一個比可見區域大 2x2 瓦片的 Canvas,這樣在邊緣周圍有一層“溢位”。這意味著地圖只需要在滾動進展一整瓦片時才在 Canvas 上重新繪製——而不是在每一幀——在滾動期間。

對於快速遊戲來說,這可能仍然不夠。另一種方法是將瓦片地圖分成大的部分(例如,將整個地圖分成 10x10 的瓦片塊),預先在 Canvas 外渲染每個部分,然後將每個渲染的部分視為一個“大瓦片”,並結合前面討論的一種演算法。

另見