方形瓦片地图实现:滚动地图

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

还有一个 可用的演示,它展示了如何让相机跟随角色。