在六角形网格上找到最短路径


14

我正在写一个带有一些模拟元素的回合制游戏。我目前挂着的一项任务是寻找路径。我想做的是利用AI当前的x,y和目标x,y将AI冒险者每转一圈,使其离目标越来越近。

在自己弄清楚这个问题的过程中,我可以确定使用4个方向没问题

dx = currentX - targetY
dy = currentY - targetY

但我不确定如何确定6个方向中的哪个实际上是“最佳”或“最短”路线。

例如,按照当前的设置方式,我使用东,西,NE,NW,SE,SW,但要转到NE磁贴,我先向东移动然后向西北移动,而不是仅向西北移动。

我希望这不是全部。即使只是一两个链接就可以让我入门也很好。我发现的大多数信息都是关于绘制网格并增加所需的怪异坐标系。


5
A *可为您提供最短路径,无论图形的形状如何(网格,十六进制,自由形式..)
Jari Komppa 2012年

Answers:


21

一些答案!

对于基于十六进制的遍历,我最常看到的坐标系是玩家可以在每个正常NSEW方向以及NW和SE上移动的坐标系。然后,只需渲染每行半个正方形的偏移量即可。例如,位置(2,7)被认为与(1,7),(3,7),(2,6),(2,8)和(1,6)和(3,8)。同时,如果我们假设(2,7)渲染在屏幕中心,则(2,6)将渲染到右上方,(2,8)将渲染到右下方-the-left,(1,7)和(3,7)会将其分别放在左右括号中,而(1,6)和(3,8)会将它们分别放在左上方和右下方。

我的意思的图表:

在此处输入图片说明

如果您采用这种方式,找到最短的直接路径并不困难-沿最大NW / SE距离行进,而不会沿基轴超调目标,然后直接沿该轴行进至目标。

但这当然会使您高兴地直奔山脉或其他不可逾越的地形。要回答您尚未提出的问题:A *搜索算法是一种常见且相当不错的寻路方法。它不仅可以处理怪异的非网格布局,而且可以愉快地处理障碍,甚至阻碍/阻碍地面。


感谢您链接到A *搜索算法。我能想象的能够遍历nsew和nw / se的唯一方法是倾斜的十六进制。在我的脑海中看起来很奇怪。你能把我链接到一个例子吗?
蒂莫西·梅斯

4
我是说您渲染的图像不必与内部结构非常相似。我建议您在内部使用NSEW和NW / SE,但将其显示为用户就像是网格一样。在原始回复中附加说明图:)
ZorbaTHut 2012年

2
十六进制网格的有趣表示。我通常制作一个锯齿状的图案,所以偶数行和偶数行的邻接关系是不同的。这引入了在路径搜索的附加最小复杂,但更有效地使用一个二维阵列(假设整个播放区域为矩形。
熊猫睡衣

2
@PandaPajama:锯齿状可以更好地有效存储矩形地图;您可以使用此技巧
amitp

2
@PandaPajama,您可以使用另一个有趣的技巧-您可以将非锯齿状的表示形式用于坐标,然后在使用“锯齿状”方法的东西后面抽象出数据存储的支持。我发现非锯齿状的坐标系要容易处理得多,但是当然,一旦将其抽象化,后端就可以执行其想做的任何事情以提高效率:)
ZorbaTHut 2012年

5

我刚刚在CodePlex.com上发布了一个十六进制网格实用程序库,网址为https ://hexgridutilities.codeplex.com/ 该库包含路径查找(使用A- * la Eric Lippert),并且包括用于在两个程序之间进行自动转换的实用程序锯齿(称为用户)坐标和非锯齿(称为规范)坐标。路径查找算法允许每个节点的步长成本随入口十六进制和遍历十六进制侧的不同而变化(尽管提供的示例更简单)。此外,还提供了使用阴影投射的高视野,[编辑:已删除单词]。

这是一个可以在三个十六进制网格坐标系统之间轻松转换的代码示例:

static readonly IntMatrix2D MatrixUserToCanon = new IntMatrix2D(2,1, 0,2, 0,0, 2);
IntVector2D VectorCanon {
  get { return !isCanonNull ? vectorCanon : VectorUser * MatrixUserToCanon / 2; }
  set { vectorCanon = value;  isUserNull = isCustomNull = true; }
} IntVector2D vectorCanon;
bool isCanonNull;

static readonly IntMatrix2D MatrixCanonToUser  = new IntMatrix2D(2,-1, 0,2, 0,1, 2);    
IntVector2D VectorUser {
  get { return !isUserNull  ? vectorUser 
             : !isCanonNull ? VectorCanon  * MatrixCanonToUser / 2
                            : VectorCustom * MatrixCustomToUser / 2; }
  set { vectorUser  = value;  isCustomNull = isCanonNull = true; }
} IntVector2D vectorUser;
bool isUserNull;

static IntMatrix2D MatrixCustomToUser = new IntMatrix2D(2,0, 0,-2, 0,(2*Height)-1, 2);
static IntMatrix2D MatrixUserToCustom = new IntMatrix2D(2,0, 0,-2, 0,(2*Height)-1, 2);
IntVector2D VectorCustom {
  get { return !isCustomNull ? vectorCustom : VectorUser * MatrixUserToCustom / 2; }
  set { vectorCustom  = value;  isCanonNull = isUserNull = true; }
} IntVector2D vectorCustom;
bool isCustomNull;

IntMatrix2D和IntVector2D是affine2D图形向量和矩阵的[整数]整数实现。向量应用程序上的最后除以2是重新标准化向量。可以将其埋在IntMatrix2D实现中,但是对于IntMatrix2D构造函数使用第7个参数的原因并不那么明显。注意非当前配方的组合缓存和延迟评估。

这些矩阵是的情况下:

  • 六角纹垂直;
  • 标准坐标和用户坐标的左上角原点,自定义坐标的左下角;
  • Y轴垂直向下;
  • 水平矩形X轴;和
  • 东北的标准X轴(即,与Y轴成逆时针方向120度)。

代码库上述提供了一种类似的优雅机制己拾取(即识别用鼠标点击选择的十六进制)。

在规范坐标中,6个基本方向矢量分别是(1,0),(0,1),(1,1)及其所有六边形的逆,而没有锯齿状坐标的不对称性。


哇!一张赞成票,用于发布工作代码库以及示例和文档,该库回答了OP提出的问题。
Pieter Geerkens

5
虽然我不是拒绝投票的人(礼节通常建议留下评论来解释反对投票的人),但我怀疑拒绝投票的原因是(a)帖子听起来像是在打广告,而(b)大部分答案都放在了另一个链接的一侧通常会皱眉,因为链接容易腐烂,SE站点会变得自成体系。此处提供的信息很有趣,但是它不能回答用户的问题,唯一可以回答问题的信息是在链接的另一侧。
Steven Stadnicki

好点;谢谢。我用摘录扩展了帖子,以解决如何有效维护多个十六进制网格坐标的问题。发布的代码库是免费软件
Pieter Geerkens 2013年

糟糕!2 分频仅适用于正整数。(再次感谢您,K&R。)应该替换为对IntVector2D中的Normalize()方法的调用:
Pieter Geerkens

public IntVector2D Normalize() { if (Z==1) return this; else { var x = (X >= 0) ? X : X - Z; var y = (Y >= 0) ? Y : Y - Z; return new IntVector2D(x/Z, y/Z); } }
Pieter Geerkens

0

这是一个已解决的问题,有大量文献支持。我知道的最好的资源是Red Blob Games:https : //www.redblobgames.com/grids/hexagons/

简而言之,最可能的原因是您从错误的坐标系开始。使用实现A *算法的多维数据集坐标系非常简单。参见上面链接的现场演示。

如果您确实要使用其他系统,请在需要时与其他系统进行相互转换。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.