网格边线

作者:GamerOreki
2023-06-20
21 18 5

编者按

本文来自游戏设计博客 Red Blob Games,由 indienova 取得授权并译制发表,原文链接见文末。

  • 原文作者:Amit Patel
  • 译者:GamerOreki

正文

带有网格的游戏通常会使用瓦片(Tiles)。但边线(Edges)和顶点(Vertices)也能用来做到一些很酷的事。对于建造游戏,(像墙壁、裂缝和窗户这样的)边线能用来阻断瓦片相连,(像门扉、管道和线路这样的)边线则能用来允许瓦片相连。

我们先从一个例子开始:创建墙壁。在瓦片上放置厚墙壁是实现墙壁的一种常见方法。替代方案则是在边线上放置薄墙壁。点击瓦片或边线以切换墙壁状态:

瓦片上的厚墙壁
边线上的薄墙壁

示例

有时我们会同时需要瓦片与边线。我会尝试将两者之一设为主项,再计算另一项。在这个展示网格照明的例子中,瓦片是主项。当任意瓦片亮起,我们都可以照亮其边线。点击瓦片:

瓦片是主项;边线是辅项(逻辑关系为或 OR)

来探讨一下这个相邻瓦片间管道/线路的例子。我们既想知道哪些瓦片连入了管道网络,也想知道哪些边线将瓦片彼此相连。如果我们将瓦片设为主项,我们就能够在相邻瓦片均被标记时放置管道。点击瓦片:

瓦片是主项;边线是辅项(逻辑关系为与 AND)

或者,我们也可以将边线设为主项,当任意边线被标记时,在其相邻瓦片上放置管道。点击边线:

边线是主项;瓦片是辅项(逻辑关系为或 OR)

本例中,当边线相邻的两个瓦片中,有且仅有一个具有厚墙壁时,为边线添加薄墙壁。点击瓦片:

边线是主项;瓦片是辅项(逻辑关系为异或 XOR)

只要条件允许,我就会将瓦片或边线之一设为主项,再使用一个函数来计算另一个。我发现相比将什么样的瓦片与边线组合允许出现作为约束条件,这样做更不容易出错。“管道”式的连接器与“墙壁”式的分隔器使用同样的底层逻辑。连接瓦片的中心(Centers)以绘制管道;连接瓦片的围角(Corners)以绘制墙壁。(译注:正方形瓦片的中心即为正方形的中心点;正方形瓦片的围角即为正方形的四个顶点)

坐标系统

方形瓦片的坐标系有两种不同的传统风格。在数学与 3D 绘图中,Y 轴指向上方增长;在许多 2D 图形系统中,Y 轴指向下方增长。本文我将展示 Y 轴指向下方增长的情况。

{{tile.q}}, {{tile.r}}

那么边线呢?我们可以为每个方形的四条边做标记:

{{tile.q}},{{tile.r}},N{{tile.q}},{{tile.r}},S{{tile.q}},{{tile.r}},W{{tile.q}},{{tile.r}},E

在许多情况下,我们希望将 (0, 0, S)(0, 1, N) 视作同一条边,类似还有 (0,0,E)(1,0,W)。这时,我们可以保留 N(北侧)和 W(西侧)的边,并舍弃 S(南侧)和 E(东侧)。反过来当然也没有问题,但这里以保留 N(北侧)和 W(西侧)为前提来展开讨论。

{{tile.q}},{{tile.r}},N{{tile.q}},{{tile.r}},W

给定瓦片(q, r),它的四条边线是(q, r, N); (q, r, W); (q, r+1, N); (q+1, r, W)

{{tile.q}},{{tile.r}}{{tile.q}},{{tile.r}},N{{tile.q}},{{tile.r+1}},N{{tile.q}},{{tile.r}},W{{tile.q+1}},{{tile.r}},W

边线的规则取决于它是北侧边还是西侧边。给定边线(q, r, N),它的两个相邻瓦片是(q, r-1)(q, r)。给定边线(q, r, W),它的两个相邻瓦片是(q-1, r)(q, r)

{{tile.q}},{{tile.r}}{{tile.q}},{{tile.r}},N{{tile.q}},{{tile.r}},W

有关围角坐标以及更多瓦片、边线、围角关系的详细信息,参看我的另一篇文章《网格指南》

像素查找

在某些游戏中,我们希望能确定鼠标位置离哪一个瓦片或哪一条边线最近。在处理瓦片时,我们可以通过查找方形来找到最近的瓦片。这些方形基本上和瓦片完全相同。这太简单了,我们都不用太思考!鼠标悬停在地图上,来查看像素所在区域,激活对应的瓦片。

在处理瓦片边线时,我们可以通过查找 45 度角方向的方形来找到距离最近的瓦片。每个这样的方形由两个瓦片中心和两个瓦片围角组成。鼠标悬停在地图上,来查看像素所在区域,激活对应的边线。

如果我们希望让鼠标同时处理瓦片和边线,我们可以构建能够同时处理这两者的多边形。瓦片用小正方形检测,而边线则用六边形检测。移动滑块来控制大小,将鼠标悬停在地图上,来查看被激活的内容。

偏向瓦片 ←
→ 偏向边线

或者,同时计算鼠标悬停的瓦片和边线,然后选择离鼠标指针更近的那个。

角点部分

如果你能够将方形瓦片、边线和围角都放在同一系统中处理,那么这里有一个很妙的技巧,用坐标 x 和 y 的低位比特信息(译注:此处应为取 x 和 y 二进制数的末位)来标识类型,即 00 = 围角,01 = 西侧边,10 = 北侧边,11 = 瓦片。 我们来看看是如何实现的。移动滑块来查看数据是如何存储在一个数组中的。

{{row-1}}{{col-1}}{{corner.q}},{{corner.r}},NW{{tile.q}},{{tile.r}}{{edge.q}},{{edge.r}},{{edge.s}}
像这样渲染 ←
→ 存储于数组
  • 在数组元素 [2*q][2*r] 中存储 围角 (q, r, NW)
  • 在数组元素 [2*q+1][2*r] 中存储 北侧边 (q, r, N)
  • 在数组元素 [2*q][2*r+1] 中存储 西侧边 (q, r, W)
  • 在数组元素 [2*q+1][2*r+1] 中存储 瓦片 (q, r)

这种实现方式能允许你将围角、边线和瓦片全部储存于同一数组。我还没有在自己的项目中用过这一方法,但我还是将它列入了本文,因为这是个很妙的想法,很可能会有人用得上。Anna Harren 还设计了能推广到六边形网格的方法

更多……

  • 我的《网格指南》涵盖了瓦片、边线和围角的坐标系统,不仅适用于方形网格,对六边形网格和三角形网格也同样适用。
  • 加权维诺图(Multiplicative Voronoi Diagram)可用于计算离边线和瓦片最近的像素区域。这里的简单情况并不需要用到它,但如果你在处理不规则网格或地图中物体大小不一的情况,那么它会非常有用。
  • 尽管许多 A* 算法的实现是为了在瓦片上工作而编写的,但 A* 算法本身同时关注瓦片和边线,因此它可以处理每条边线都被标记为可行走或不可行走的地图。
  • David Stark 发表过一篇关于游戏中网格的博客文章,指出《矮人要塞》(Dwarf Fortress)使用基于瓦片的墙壁而非基于边线的地板。
  • 我已经写了一篇关于如何使用几何学检测边线周围菱形外框的简短说明,但还没有在该页面中制作完整的交互版本。

有些游戏,比如《缺氧》(Oxygen Not Included)和《放置工厂》(Factory Idle),似乎使用了同时关注瓦片与边线连接的混合模式。有些游戏,像是《异星工厂》(Factorio)和《生产线》(Production Line),还考虑了边线指向的属性;请参阅我有关传送带的页面


原文链接:https://www.redblobgames.com/grids/edges/
*本文内容系作者独立观点,不代表 indienova 立场。未经授权允许,请勿转载。

近期点赞的会员

 分享这篇文章

GamerOreki 

一个仍在试着追梦的游戏玩家。 

您可能还会对这些文章感兴趣

参与此文章的讨论

  1. virmint 2023-06-20

    哇哦……

  2. 林荫 2023-06-20

    一个数组存点线边的想法有点意思哦。
    虽然使用的场景还没想到。

  3. mrup 2023-06-20

    可交互文章

  4. 二蕉 2023-06-21

    哇,好棒

  5. ReEd1327 2023-06-30

    存在一起的方法很有趣,也许会用到

您需要登录或者注册后才能发表评论

登录/注册