请注意:这不是教程或最佳实践文章! 我只是在描述自己的经历。
代表世界的图块

在了解了该项目的发展方向之后,我不得不决定如何实现游戏世界。 之前,我已经对此进行了一些阅读,并且我非常确定tilemap对于我的游戏已经足够了。
什么是图块图? 基本上,所有游戏世界(地图)都分为单元格(图块)。 就像真实地图一样,通常会绘制一些经度和纬度,以便更轻松地查找某些对象。 另一个很好的类比是棋盘。

无论如何,tilemap是一个二维网格,就是这样。 程序员的网格是什么? 好吧,这很容易表示为数组。 下棋时,您可以描述任何人物的位置:例如,在黑王上面的图像上的E8。 如果我们将棋盘表示为二维数组,则可以使用chessboard[4][7]
指向黑人国王-本质上是相同的。
因此,我们可以有一个二维数组,其中每个元素为1或0并将其绘制在画布上-使1用红色单元格表示,0用绿色表示。 或者,我们可以输入更多数字并添加更多颜色。 或者,我们不仅可以绘制彩色矩形,还可以绘制国际象棋图形。 还是世界物体:石头,树木,房屋……MDN的瓷砖地图介绍非常不错,如果您感兴趣,请检查一下。
tilemap有什么好处? 它们很容易实现! 它们还可以简化游戏机制的许多方面,例如碰撞检测。 tilemap有什么坏处? 好吧,这个世界看起来不那么自然,也不是很现实,它缺乏深度。 为了解决这些问题,可以使用另一种几何网格-等距。 我们之前讨论的网格在几何上是正交的,它的像元是正方形。 等距网格具有菱形单元。 辐射(Fallout)(不是Bethesda辐射,旧辐射(Fallouts)1和2)等游戏都使用等距投影-它创造了一个感觉更加自然的世界(当然,不如3D引擎游戏的世界那么自然)。

另一种网格是六角形的。 它可以更公平地计算距离,这就是为什么它经常用于军队在地图上移动的策略中的原因。

宝珠守护者中的图块地图
好,让我回到我的游戏。 我真的关心它看起来现实吗? 不。 我的手工制作的精灵无论如何都会使它看起来不现实! 为了使等距游戏看起来很酷,它需要一些专业艺术家的好作品,显然这不是我的情况。 另外,当我在脑海中思考迷宫时,我会以正交投影的方式想象它是自上而下的。 当具有严格的矩形网格时,更容易预测敌人的路径并计划放置障碍物。 因此,最简单的网格似乎实际上最适合我的TD游戏。
然后,我不得不考虑如何实施它。 坦白说,数字数组对我来说不太方便。 我可以让每个数字代表一个地形类型-例如,对于山谷的草0(炭黑可以穿过草砖),对于山谷的那些山则为1(炭黑不能穿过山区的砖瓦)。 但是,如果我想添加静态物体(如石头)怎么办? 然后,我是否应该有两个数组,一个用于地形,另一个用于对象? 可能可行。 但是,我决定将每个图块表示为一个对象。
一个对象可以存储所需数量的数据。 对我来说,更容易理解通过细胞-物体描述的世界。 例如,可以说单元Nr100具有地形类型的草,有石头的对象,被遮挡并且具有某些索引的邻居。 另外,我决定随意使用一维数组来保存所有单元格(实际上并不那么重要,可以使用二维)。
最后,我像这样实现了我的tilemap……我使它成为一个对象本身。 它的宽度和高度将取决于窗口的尺寸,它将占用屏幕的所有可用空间。 将提供图块大小(是正方形,因此宽度和高度相同)。 应用启动时,将创建tilemap。 它计算需要多少个瓷砖来填充屏幕,创建此类瓷砖(使用new Tile
)并将它们存储在数组中。 然后可以遍历tile数组(或使用索引为其指定特定tile)并更改其地形,添加对象,使其是否受阻等。
是的,目前游戏世界的大小,因此难度将取决于屏幕大小-我稍后会担心。 当然,我可以使用一定数量的图块制作一个图块,并添加相机以仅向玩家显示世界的一部分(这很可能是在许多2D游戏中完成的)。 但是,在塔防游戏中必须滚动地图对我来说总是错的。 我想一次看到我所有的迷宫,我想看到所有敌人。 因此,我决定使游戏世界变得很小,不大于窗口(可能使其在大屏幕上变小)。 这也意味着我不必担心性能。
康威的人生游戏(使用Tilemap)

上面描述的图块地图可以很容易地修改。 实际上,我也将其用于一个“免费代码营”项目以及“我的生活游戏”。
康威(Conway)的人生游戏是一种模拟,其中我们拥有一个细胞网格,每个细胞都根据其存活邻居的数量而出生,生活或死亡。 因此,我最初的tilemap已经几乎满足了我的所有需求! 它甚至每个瓦片都可以“知道”它的邻居,但是我不得不稍作改动,因为在Conway的人生游戏实施中,最好考虑将所有边缘缝合在一起。 然后,我必须向每个图块添加一个alive
字段,显然每个单元格可能是true
还是false
。
现在,我的tilemap包含一个由其组成的数组,因此创建一个要遍历该数组,为每个单元格求出活跃邻居并决定单元格命运的函数没有问题。 一个小问题是,如果我即时更改单元格的“活动性”,那么,该单元格是某人的邻居,对他们而言,计算将不可用。 我以一种非常直接的方式解决了这个问题-添加了另一个字段来保留更改信息,然后应用所有更改。 当然,这个游戏世界不是很复杂,可以用简单数字数组实现,0表示死细胞,1表示活着。
我并不真正在意性能,但是我的实现仍然没有那么糟糕(当您将tile大小设置为1px时滞后,但是很好),比某些React更快。 当然,如果使用div表示单元格,则它会使应用程序在每次迭代时都处理DOM。 canvas的requestAnimationFrame
方法在渲染这些单元格方面做得更好。