关于网格

Posted xuuold

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于网格相关的知识,希望对你有一定的参考价值。

 

网格通常用于游戏中,用于表示游戏区域,如地图(在“文明”和“魔兽”等游戏中),游戏界面(如游泳池,乒乓球和扑克等游戏),运动场(棒球和足球等游戏),棋盘(在象棋,Monopoly和Connect Four等游戏中,以及抽象空间(在俄罗斯方块之类的游戏中)。我试图在这些页面上收集我对网格的看法。我避免实现细节(例如源代码),而是专注于概念和算法。我主要使用网格来表示战略和模拟游戏中的地图。虽然这里的许多概念对各种网格都很有用,但我对这些游戏感兴趣。

网格由重复的简单形状构成。下文将覆盖以下内容:正方形、六边形和三角形顶点、边和面(平铺的瓦片),坐标系,以及使用网格的算法。

心急的读者可以直接跳到坐标系。还有一个六边形网格指南,那里有更多的特定于六边形的算法。

正方形

最常见的网格是方形网格。它简单易用,可以很好地映射到计算机屏幕上。

位置可以使用熟悉的笛卡尔坐标(x,y),轴是正交的。即使您的地图方块在等轴测图或轴测投影中在屏幕上成角度,方形坐标系也是相同的。

技术分享图片

六边形

六边形已经在一些电路板和计算机游戏中使用,因为它们提供的距离失真比方形网格少。这部分是因为每个六边形具有比正方形更多的非对角线邻居。(对角线扭曲了网格距离。)

六角形在自然中存在(蜂窝)并且具有令人愉悦的外观。在本文中,我将使用具有平顶和尖边的六边形,但如果你想要尖顶和平边,数学运算是相同的。

还有一个更全面的指南,涵盖偏移坐标,轴坐标,立方坐标,尖顶,平顶和更多算法。

技术分享图片

三角形

三角形在3d图形中很常见,但很少用于游戏地图。

它的一大缺点是周长大且面积小(与六边形相反)。小区域意味着将游戏块完全放置在地图上的单个空间内更加困难。

在三维图形中,三角形是唯一的平面形状;正方形和六边形有时可以“弯曲”,有时不可以。

在本文中,我将使用向上和向下指向的三角形,但如果您的三角形指向左右,数学运算也是相同的。

技术分享图片

网格部分

网格有三种类型的零件:面(平铺的瓦片)、边和顶点。

每个面都是由边缘包围的二维平面;每个边是一个以两个顶点结束的一维线段;每个顶点都是零维点。

游戏通常只关注这些类型的部分中的一种。

像Chess和Checkers这样的“西方”游戏似乎专注于面,像Go和中国跳棋这样的“东方”游戏似乎专注于顶点。像轮盘赌这样的游戏为所有三种类型的网格部分赋予了意义。

技术分享图片

 

面、边和顶点也显示在多边形地图中。

在不需要网格坐标的情况下处理面,边和顶点的算法将适用于这些多边形贴图:

技术分享图片

通过将每个面转换为节点,并将面之间的边转换为连接节点的边,可以转换为图的结构,可以使用图论的算法(例如最短路径)。

格图上使用图算法(例如最短路径)。

在游戏中使用

电脑游戏可以使用所有三种网格的零件,但面是最常见的。

建筑物,土地类型(草地,沙漠,砾石等)和领土所有可以使用面。

区域边界和“流动”算法(模拟相邻面之间的水流,人员,货物等)可以使用边。

高度(海拔高度,水深)使用顶点。

道路和铁路可以使用面(如在SimCity中)或边(如Locomotion,请参阅Java小程序演示此内容)。

零件计数

我们可以计算生成网格需要多少面、边和顶点。方法是考虑邻接和共享。

技术分享图片         技术分享图片

 (这段如果看不懂可以往下,推导和坐标系可以辅助理解)

考虑三角形网格。每个三角形面有3个边。因此,边的数量是面的3倍。

但每个边由2个面共享,因此每2个面有3个边。

每个三角面具有3个顶点。每个顶点由6个面共享。因此,我们每6个面有3个顶点,或者每2个面有1个顶点。

在设计坐标系时,这些关系很重要。

正方形具有相同数量的面和顶点。六边形有比面有更多的顶点。三角形有比顶点有更多的面。边的数量总是最多的。

技术分享图片

我将这些称为F,E,V计数。正方形的F,E,V是 1, 2, 1;六边形 1, 3, 2;三角形 2, 3, 1

 

请注意,六边形和三角形网格具有相似的计数,除了顶点和面数计数相反。这是因为六边形网格和三角形网格是成双:如果将顶点放在三角形网格的每个面的中心,您将获得六边形网格,反之亦然。

方形网格本身就是一致的。如果将顶点放在每个正方形的中心,则会生成另一个正方形网格,与第一个网格偏移。阅读更多维基百科上的密铺的凸多边形

六边形和三角形网格的推导

六边形和三角形网格可以从方格网格中导出。由于方块的坐标系统很简单,推导之可以指导我们设计六边形和三角形的坐标系。

正方形到六边形

将方形网格转换为六边形网格需要两个步骤。

首先,偏移列(或行)。

其次,取出方形一半的边,在中间弯曲它们进行变形。

 

技术分享图片                    技术分享图片                     技术分享图片

偏移列有两种简单的方法。最常见的是偏移偶数列。

一种更简单的方法是将每列偏移高于前一列的半个高度。使用这种方法的代码更加均匀,但地图形状不再是矩形,这可能不方便。

在这个页面上,我将重点介绍后一种方法;它更容易使用,也可以与三角形一起使用。

技术分享图片                    技术分享图片                    技术分享图片

 

无论采用哪种偏移方法,下一步是分割正方形的垂直的边并弯曲它们。

当弯曲从180度减小到120度时,您将拥有正常的六边形。

 

请注意,分割垂直边意味着我们将边数从4增加到6(每个面净增加1个边,因为2个新边由2个面共享)。

我们还将顶点数从4增加到6(但这些顶点是共享的,因此净增加为1),并且我们保持面的数量不变。

F,E,V计数从1,2,1变为1,3,2。

正方形到三角形

技术分享图片                    技术分享图片                    技术分享图片

将方形网格转换为三角形网格需要两个步骤。

首先,我们必须剪切正方形。这给了我们一个菱形网格。

为了制作三角形,我们将每个菱形面分成两个三角形。

拆分每个面意味着我们现在拥有的面数是以前的两倍,我们为每个面添加了1个边,我们没有添加任何顶点。

F,E,V计数从1,2,1变为2,3,1。

坐标系

网格有三个部分(面、边、点),我们需要一种方法来标识它们的每一个。

我将从与网格的轴匹配的简单数字坐标开始。

F,E,V计数告诉我们有多少网格部分共享相同的坐标。

如果多个部分具有相同的坐标,我将使用大写字母来消除歧义。

方形网格

技术分享图片                    技术分享图片                    技术分享图片

方格很容易。

F,E,V 计数为 1,2,1。这意味着只有边需要一个字母来消除歧义。

对于每个面,我们(任意)指定一个顶点来共享其坐标。我选择了面的西南角。

对于每个面,我们分配两个边来共享其坐标。我选择了南边和西边,并用字母 S和W 标注

有1个面、2条边和1个顶点共享(1,1)网格坐标。这符合我们的 F,E,V 计数 1,2,1。

六角网格

技术分享图片                    技术分享图片                    技术分享图片

我们用正方形网格创建六边形网格。

六边形面的坐标可以与转换前的正方形面的坐标相同。

 

对于每个面,我们选择三个边和两个顶点来共享相同的坐标。我选择了 NW,N 和 NE 边,并将它们标记为 W,N,E。

我选择了最左边和最右边的顶点,并将它们标记为 L,R。

类似于这样的设定都是可以的。

三角网格                   

技术分享图片    技术分享图片    技术分享图片

我们用正方形网格创建菱形网格,然后将每个剪切的正方形(菱形)分成两个三角形。

这意味着每个方形面坐标需要分为两个三角形面的坐标。我选择将它们标记为L和R。

边与正方形(W和S)的边相同,除了我们有一个额外的边缘将两个面分裂,将之标记为 E。

额外的三角形面不会创建任何额外的顶点,因此顶点标注与方形网格标注相同。

 

有记载的另一种方案,在这里,我需要学习。它似乎比我在这里更简单。

网格部件之间的关系

 我们可以定义 3 种网格零件之间的关系,从而给出网格之间的 9 种关系。

你可以定义更多的关系,但我会专注于这 9 种。我不知道这些关系的标准名称,所以我已经编写了一些名字。

技术分享图片

在您的游戏中,您可能想要使用上述变体。

例如,neighbors  adjacent 关系可能包括对角线。将 continues 关系可能已包括边缘未用原边共线。我选择了最简单的关系。

算法

 这9个关系中的每一个都可以表示为从A到B列表的算法;我写它们作为A→B1B2B3 ...

有3种形状,因此我们提供了27种可能的算法,这些算法以简单的形式表示,您可以将其转换为您喜欢的编程语言。

对于一些形状和算法,有超过一个A的变体,所以我会列出每个变体的规则。例如,三角形面有LR变体。

以下是所有算法:

技术分享图片

技术分享图片

技术分享图片

关系

(心情不好可跳过本节)

上面列出的关系本身就是相互关联的。例如,borders 从面到边,它是 joins 的颠倒,从边到面。

如果某些边位于某个面 borders 列表中,则 将位于边 joins 列表中。这9种关系可以归结为6种数学关系

 

如果您有数据库,则可以直接表达关系。例如,这是一个小的 1x2 方格网格中面和边之间的关系:

技术分享图片

 

 

 给定关系,您可以在任何列中查找。

例如,使用上表,查找Face==(0,0)4个边的结果,这是 borders 关系表达的内容。

Edge==(1,0,W)在2个面中查找结果,这是 joints 关系表达的内容。

关系允许您以多种方式查找事物;每个关系(和算法)都是一种特定的查找方式。

 

给定 6 种关系,那么应该有 12 种关系。

为什么我们只有9个?

这是因为面/面,边/边和顶点/顶点关系是对称的,所以以另一种方式查找事物会产生相同的答案。

因此,12个关系中的3个是多余的,我们剩下9个。

实现

所有算法都很简单。你怎么能实现它们?

您首先要为三个坐标系中的每一个选择数据结构。

我建议保持它非常简单和透明

我列出的所有坐标系都有一个整数u和一个整数v,其中一些有一个像L或的注释W

对于结构,在Ruby中使用带有公共attrs的类;在Lisp中使用一个列表; 在C中使用结构;在Java中使用具有公共字段的类;在Python中使用简单的对象或字典。

对于注释,在Ruby或Lisp中,使用符号(‘L:L); 在C中,使用字符(‘L‘)或枚举类型(L); 在Java中,使用字符; 在Python中,使用单字符字符串。

 

下一步是实现您需要的算法。

最简单的方法是编写带A的参数的函数(或方法)并返回B列表。

如果有多个A变体,请使用 switch / case 语句对注释进行分支。这是最简单的方法,但它并不是最快的。

为了加快速度,调用者可能会预先分配列表,或者您可能提供内联的回调(例如,C ++中的STL函数对象)。

在某些情况下,您需要同时查找多个A,因此您可以提供一个适用于A列表的算法,并生成B列表的列表。

 

我通常避免给出实现,因为它们对于每个游戏都太具体了,但我将举例说明 Ruby 中的 Triangle 形状。我选择使用列表作为我的基本数据结构,并使用 Ruby 符号(如:L)进行注释。

 技术分享图片

就这么简单。每个变体都成为case声明中的一种情况。

坐标转换

在2D和3D图形系统中,我们必须将“世界”坐标转换为“屏幕”坐标并返回。

对于网格,我们必须将“网格”坐标转换为“世界”坐标并返回。

转换将发生在点上

从网格到世界坐标,我们变换顶点并以所在面的中心示之。

从世界到网格坐标,我们可以选择是找到包围点的面,或最接近点的边,还是最接近点的顶点。

正方形

这对正方形网格来说很容易。

如果正方形的边长为 s 且边与 x 和 y 轴对齐,则可以将网格顶点坐标乘以 得到世界坐标。

 

另一方面,我们想要确定哪个顶点最接近世界空间中的目标点。

我们可以将世界坐标除以 并将浮点数舍入为 int 以获得最近的顶点。

如果您想要确定哪个面包围世界空间中的一个点,请使用平面而不是圆形。

六边形

使用六边形比使用正方形稍微复杂一些。

技术分享图片

 

计算面的中心很简单。

有一个i向量和一个j向量。从六边形坐标到世界坐标是一个(非常简单的)矩阵乘法:

技术分享图片

 展开来,即

技术分享图片

对于平顶六边形,i(hexagon_narrow_width, 0.5*hexagon_height)j(0, hexagon_height),所以给我们的值i.x, i.y, j.x, j.y。(尖顶六边形将与silghty不同)代码是:

技术分享图片

技术分享图片

计算顶点也很简单。

在文章的其余部分,我标记了六边形顶点 L 或 R。这两个顶点出现在六边形中心左侧或右侧的六边形宽度的一半,所以我们所要做的就是添加或减去 hexagon_wide_width * 0.5

技术分享图片

使用六边形时,将面中心视为主要,将顶点视为次要。

从六边形坐标(u, v)到世界坐标(x, y)是一个矩阵乘法。要从世界坐标返回到六边形,您可以求解方程式(u, v)

我会跳过代数,这是平顶六边形的结果:

 技术分享图片

如果你从坐标为(x, y)的面中心开始,这是有效的。如果(x, y)是随机的点那就需要更多的操作了。

最简单(但不是最有效)的方法是考虑计算(u, v)加上所有邻居并确定哪一个最接近给定的世界坐标。此方法适用于所有三种网格类型。

如果您使用它来选择鼠标,这足够快。通过更仔细地观察多边形,可以进一步优化它。

三角形

三角形通过正方形被剪切和分裂得到。

三角形顶点仅使用剪切步骤,这会产生菱形。

要将三角形的顶点从网格坐标转换为世界坐标,请乘以轴向量 i 和j

技术分享图片

展开后,得到:

技术分享图片

要将三角形面的坐标转换为世界坐标,需要进行调整。

对于面(u, v, L/R)首先计算左下顶点的世界坐标,(u, v)

然后对于一个 L面,添加(1/2 * i, 1/3 * j)到左下顶点位置。

对于 R面,添加(i, 2/3 * j)到左下顶点位置。

结果将得到面部的中心。

 

要从世界坐标转换为三角形顶点,首先(u, v)使用代数找到菱形的左下角,或者通过反转 i.x,j.x,i.y,j.y 矩阵。

菱形包含两个三角形面。

技术分享图片

要确定该点所在的面,请查看在每个菱形内划分两个三角形的边(边“E” )。

如果 frac(u) + frac(v) < 1.0,该点在线的左侧,因此在 L面;否则就是在 R 面上。

 

使用三角形时,将顶点视为主要,将面中心视为次要。这与我们如何处理六边形相反。

更多

方形网格上的距离??公式是众所周知的(曼哈顿,欧几里德,对角线距离)。

使用此坐标系的十六进制网格上的距离??使用两轴坐标到第三轴的扩展,我在我的十六进制网格页面上有公式在这里探讨三角形网格上的距离。

 

有一些我找不到的地方放的东西,但你可能有兴趣。数学家在2D空间发现了五种类型的网格:正方形,三角形,六边形,矩形和平行四边形。

但是,只有正方形,三角形和六边形是正多边形。

螺旋蜂窝马赛克是一种有趣的方法,可以在六边形网格中为六边形分配数字。它导致了一些奇怪的属性。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

以上是关于关于网格的主要内容,如果未能解决你的问题,请参考以下文章

Forge Viewer - 如何在场景中访问(或获取渲染/片段代理)克隆的网格?

在片段着色器中丢失纹理定义

片段内的网格适配器不起作用

css CSS网格片段

GLSL-片段着色器不同部分的精度不同

关于代码片段的时间复杂度