方形瓦片地圖實現:滾動地圖

本文介紹瞭如何使用 Canvas API 實現滾動方形瓦片地圖。

注意:撰寫本文時,我們假設讀者已具備 Canvas 基礎知識,例如如何獲取 2D Canvas 上下文、載入影像等,這些內容已在 Canvas API 教程 中解釋,並且還包含了我們 瓦片地圖 入門文章中的基本資訊。本文也建立在 實現靜態方形瓦片地圖 的基礎上,如果您還沒有閱讀過,請務必先閱讀。

相機

相機是一個物件,它儲存有關當前顯示的遊戲世界或關卡部分的資訊。相機可以是自由形式的,由玩家控制(如策略遊戲中),也可以跟隨一個物件(如平臺遊戲中的主角)。

無論相機型別如何,我們總是需要關於其當前位置、視口大小等資訊。在本文提供的 演示 中,相機的引數如下:

  • xy:相機的當前位置。在此實現中,我們假設 (x,y) 指向地圖可見部分的左上角。
  • widthheight:相機視口的大小。
  • maxXmaxY:相機的移動限制 — 下限幾乎總是 (0,0),在本例中,上限等於世界大小減去相機視口大小。

渲染地圖

渲染滾動地圖與渲染靜態地圖之間存在兩個主要區別:

  • 可能會顯示部分瓦片。在靜態地圖中,渲染通常從視口左上角的瓦片開始。而在渲染滾動瓦片地圖時,第一個瓦片通常會被裁剪。

  • 只會渲染地圖的一部分。如果地圖比視口大,我們一次只能顯示一部分,而不會滾動的地圖通常會完整渲染。

為了處理這些問題,我們需要稍微修改渲染演算法。我們假設相機指向 (5,10)。這意味著第一個瓦片將是 0x0。在演示程式碼中,起始點儲存在 startColstartRow 中。方便的是,我們還可以預先計算要渲染的最後一個瓦片。

js
const startCol = Math.floor(this.camera.x / map.tsize);
const endCol = startCol + this.camera.width / map.tsize;
const startRow = Math.floor(this.camera.y / map.tsize);
const endRow = startRow + this.camera.height / map.tsize;

一旦我們確定了第一個瓦片,就需要計算它的渲染(以及其他瓦片的渲染)偏移量。由於相機指向 (5, 10),我們知道第一個瓦片應該向左上方偏移 (-5,-10) 畫素。在我們的演示中,偏移量儲存在 offsetXoffsetY 變數中。

js
const offsetX = -this.camera.x + startCol * map.tsize;
const offsetY = -this.camera.y + startRow * map.tsize;

有了這些值,渲染地圖的迴圈與用於渲染靜態瓦片地圖的迴圈非常相似。主要區別在於我們將 offsetXoffsetY 值加到了目標 xy 座標上,並且這些值會被四捨五入,以避免相機指向浮點數位置時產生的偽影。

js
for (let c = startCol; c <= endCol; c++) {
  for (let r = startRow; r <= endRow; r++) {
    const tile = map.getTile(c, r);
    const x = (c - startCol) * map.tsize + offsetX;
    const y = (r - startRow) * map.tsize + offsetY;
    if (tile !== 0) {
      // 0 => empty tile
      this.ctx.drawImage(
        this.tileAtlas, // image
        (tile - 1) * map.tsize, // source x
        0, // source y
        map.tsize, // source width
        map.tsize, // source height
        Math.round(x), // target x
        Math.round(y), // target y
        map.tsize, // target width
        map.tsize, // target height
      );
    }
  }
}

演示

我們的滾動瓦片地圖實現演示將上述程式碼整合在一起,展示了這種地圖的實現方式。您可以檢視 線上演示,並檢視 其原始碼

Animated gif of a section grass, dirt areas, and trees made from repeated sections of a tilemap showing how you see different sections of the area when you scroll.

還有一個 可用的演示,它展示瞭如何讓相機跟隨角色。