Dictionary 六边形瓷砖和寻找他们的邻居

Dictionary 六边形瓷砖和寻找他们的邻居,dictionary,grid,hexagonal-tiles,Dictionary,Grid,Hexagonal Tiles,我正在开发一个简单的二维棋盘游戏,使用六边形贴图,我已经阅读了几篇文章,包括gamedev one的文章,每次有关于六边形贴图的问题时,都会链接到如何在屏幕上绘制六边形以及如何管理移动,尽管我以前已经做了很多。我的主要问题是根据给定的半径查找相邻的瓷砖 这就是我的地图系统的工作原理: (0,0) (0,1) (0,2) (0,3) (0,4) (1,0) (1,1) (1,2) (1,3) (1,4) (2,0) (2,1) (2,2) (2,3) (2,4) (3,0) (3,1)

我正在开发一个简单的二维棋盘游戏,使用六边形贴图,我已经阅读了几篇文章,包括gamedev one的文章,每次有关于六边形贴图的问题时,都会链接到如何在屏幕上绘制六边形以及如何管理移动,尽管我以前已经做了很多。我的主要问题是根据给定的半径查找相邻的瓷砖

这就是我的地图系统的工作原理:

(0,0) (0,1) (0,2) (0,3) (0,4)
   (1,0) (1,1) (1,2) (1,3) (1,4)
(2,0) (2,1) (2,2) (2,3) (2,4)
   (3,0) (3,1) (3,2) (3,3) (3,4)
等等

我正在努力解决的是,我不能仅仅使用forx range“选择”相邻的瓷砖;x+范围;x++;福里山脉;y+范围;y++;因为它在我给出的示例中选择了不需要的瓷砖,所以选择1,1瓷砖并给出1的范围也会给我3,0瓷砖,我实际需要的是0,10,21,01,22,12,2,这有点接近瓷砖,因为数组的结构方式,但这不是我真正想要选择的。我可以用蛮力强迫它,但这并不漂亮,也可能无法涵盖“选择半径”的每一个方面


有人能给我指出正确的方向吗?

我能想到的最简单的方法

minX = x-range; maxX = x+range
select (minX,y) to (maxX, y), excluding (x,y) if that's what you want to do
for each i from 1 to range:
    if y+i is odd then maxX -= 1, otherwise minX += 1
    select (minX, y+i) to (maxX, y+i)
    select (minX, y-i) to (maxX, y-i)
可能有点不对劲;我只是在脑子里想了想。但至少,这是你需要做什么的想法

用西语:

void select(int x, int y) { /* todo: implement this */ }

void selectRange(int x, int y, int range)
{
    int minX = x - range, maxX = x + range;
    for (int i = minX; i <= maxX; ++i)
        if (i != x) select(i, y);
    for (int yOff = 1; yOff <= range; ++yOff)
    {
        if (y+yOff % 2 == 1) --maxX; else ++minX;
        for (int i=minX; i<=maxX; ++i)
        {
            select(i, y+yOff);  select(i, y-yOff);
        }
    }  
}
什么是六边形网格? 您可以看到上面的两个网格。这一切都取决于你给瓷砖编号的方式,以及你理解六边形网格是什么的方式。在我看来,六边形网格只不过是一个变形的正交网格

我用紫色圈出的两个六边形瓷砖理论上仍然与0,0相邻。然而,由于我们通过变形从正交网格获得六角瓷砖网格,这两个网格在视觉上不再相邻

变形 我们需要理解的是变形发生在某个方向,在我的例子中沿着[-1,1,-1]想象线。更准确地说,它就像网格沿着该线被拉长,并沿着垂直于该线的线被挤压。因此,很自然地,这条线上的两块瓷砖展开,在视觉上不再相邻。相反,与0,0成对角线的瓷砖1,1和-1,-1现在非常接近0,0,事实上非常接近,它们现在在视觉上与0,0相邻。然而,从数学上讲,它们仍然是对角线,在代码中这样处理它们会有所帮助

选择 我显示的图像显示半径为1。对于半径为2的情况,您会注意到2,-2和-2,2是不应包含在选择中的平铺。等等因此,对于半径r的任何选择,不应选择点r,-r和-r,r。除此之外,您的选择算法应该与方形平铺网格相同

只需确保在六边形网格上正确设置轴,并相应地为瓷砖编号

实施 让我们对此进行一点扩展。我们现在知道,沿网格中的任何方向移动都会花费我们1。沿拉伸方向的运动花费我们2美元。例如,请参见0,0到-1,1

知道了这一点,我们可以通过将距离分解为两个分量来计算这样一个网格上任意两个瓷砖之间的最短距离:对角线运动和沿其中一个轴的直线运动。 例如,对于法线栅格上1,1和-2,5之间的距离,我们有:

Normal distance = (1, 1) - (-2, 5) = (3, -4)
这将是两块瓷砖之间的距离向量,如果它们位于正方形网格上。但是,我们需要补偿网格变形,因此我们按如下方式分解:

(3, -4) = (3, -3) + (0, -1)
如你所见,我们已经将向量分解成一个对角线,一个3,-3,一个沿着轴0,-1的直线

我们现在检查对角线是否沿着变形轴,即任意点n,-n,其中n是一个整数,可以是正的,也可以是负的。 3,-3确实满足这个条件,所以这个对角向量是沿着变形的。这意味着这个向量的长度或成本不是3,而是2倍,即6

总而言之。1,1和-2,5之间的距离是3,-3加上0,-1的长度。这就是距离=3*2+1=7

C++实现

以下是我在上面解释过的算法的C++实现:

int ComputeDistanceHexGrid(const Point & A, const Point & B)
{
  // compute distance as we would on a normal grid
  Point distance;
  distance.x = A.x - B.x;
  distance.y = A.y - B.y;

  // compensate for grid deformation
  // grid is stretched along (-n, n) line so points along that line have
  // a distance of 2 between them instead of 1

  // to calculate the shortest path, we decompose it into one diagonal movement(shortcut)
  // and one straight movement along an axis
  Point diagonalMovement;
  int lesserCoord = abs(distance.x) < abs(distance.y) ? abs(distance.x) : abs(distance.y);
  diagonalMovement.x = (distance.x < 0) ? -lesserCoord : lesserCoord; // keep the sign 
  diagonalMovement.y = (distance.y < 0) ? -lesserCoord : lesserCoord; // keep the sign

  Point straightMovement;

  // one of x or y should always be 0 because we are calculating a straight
  // line along one of the axis
  straightMovement.x = distance.x - diagonalMovement.x;
  straightMovement.y = distance.y - diagonalMovement.y;

  // calculate distance
  size_t straightDistance = abs(straightMovement.x) + abs(straightMovement.y);
  size_t diagonalDistance = abs(diagonalMovement.x);

  // if we are traveling diagonally along the stretch deformation we double
  // the diagonal distance
  if ( (diagonalMovement.x < 0 && diagonalMovement.y > 0) || 
       (diagonalMovement.x > 0 && diagonalMovement.y < 0) )
  {
    diagonalDistance *= 2;
  }

  return straightDistance + diagonalDistance;
}
对于选择点1、1和范围1,上述代码将显示预期结果:

(0, 0)
(0, 1)
(1, 0)
(1, 1)
(1, 2)
(2, 1)
(2, 2)
可能的优化
为了优化这一点,您可以将知道磁贴距离ComputeDistanceHexGrid中的选择点逻辑有多远的逻辑直接包含到您的选择循环中,这样您就可以以一种完全避免超出范围磁贴的方式迭代网格。

它完全按照我的需要工作,我真的很感谢你的帮助,感谢你抽出时间:有人发了一个关于这个问题的复制粘贴问题。因为这是最初的问题,我也在这里发布我的答案。看下面。非常漂亮的图纸和一个非常长和彻底的答案。可能来自哈文
g以前自己解决过这个问题。事实上,我以前必须自己解决。谢谢
(0, 0)
(0, 1)
(1, 0)
(1, 1)
(1, 2)
(2, 1)
(2, 2)