方形瓦片地图实现:滚动地图
本文介绍了如何使用 Canvas API 实现滚动方形瓦片地图。
注意:撰写本文时,我们假设读者已具备 Canvas 基础知识,例如如何获取 2D Canvas 上下文、加载图像等,这些内容已在 Canvas API 教程 中解释,并且还包含了我们 瓦片地图 入门文章中的基本信息。本文也建立在 实现静态方形瓦片地图 的基础上,如果您还没有阅读过,请务必先阅读。
相机
相机是一个对象,它保存有关当前显示的游戏世界或关卡部分的信息。相机可以是自由形式的,由玩家控制(如策略游戏中),也可以跟随一个对象(如平台游戏中的主角)。
无论相机类型如何,我们总是需要关于其当前位置、视口大小等信息。在本文提供的 演示 中,相机的参数如下:
x
和y
:相机的当前位置。在此实现中,我们假设(x,y)
指向地图可见部分的左上角。width
和height
:相机视口的大小。maxX
和maxY
:相机的移动限制 — 下限几乎总是(0,0)
,在本例中,上限等于世界大小减去相机视口大小。
渲染地图
渲染滚动地图与渲染静态地图之间存在两个主要区别:
-
可能会显示部分瓦片。在静态地图中,渲染通常从视口左上角的瓦片开始。而在渲染滚动瓦片地图时,第一个瓦片通常会被裁剪。
-
只会渲染地图的一部分。如果地图比视口大,我们一次只能显示一部分,而不会滚动的地图通常会完整渲染。
为了处理这些问题,我们需要稍微修改渲染算法。我们假设相机指向 (5,10)
。这意味着第一个瓦片将是 0x0
。在演示代码中,起始点存储在 startCol
和 startRow
中。方便的是,我们还可以预先计算要渲染的最后一个瓦片。
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)
像素。在我们的演示中,偏移量存储在 offsetX
和 offsetY
变量中。
const offsetX = -this.camera.x + startCol * map.tsize;
const offsetY = -this.camera.y + startRow * map.tsize;
有了这些值,渲染地图的循环与用于渲染静态瓦片地图的循环非常相似。主要区别在于我们将 offsetX
和 offsetY
值加到了目标 x
和 y
坐标上,并且这些值会被四舍五入,以避免相机指向浮点数位置时产生的伪影。
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
);
}
}
}
演示
我们的滚动瓦片地图实现演示将上述代码整合在一起,展示了这种地图的实现方式。您可以查看 在线演示,并查看 其源代码。
还有一个 可用的演示,它展示了如何让相机跟随角色。