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

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

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