【问题标题】:On a hexagonal grid: selecting tiles within a given radius of a selection point.在六边形网格上:在选择点的给定半径内选择图块。
【发布时间】:2013-03-20 05:44:04
【问题描述】:

我正在使用六角瓷砖地图开发一个简单的 2D 棋盘游戏,我已经阅读了几篇关于如何在屏幕上绘制六边形的文章(包括 gamedev 的,每次出现关于六角瓷砖的问题时都会链接)如何管理运动(尽管我之前已经做过很多)。我的主要问题是根据给定的半径找到相邻的瓷砖。

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

(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) etc...

我正在努力解决的事实是,我不能使用 for(x-range;x+range;x++); for(y-range;y+range;y++); 来“选择”相邻的图块,因为它会选择不需要的图块(在我给出的示例中,选择 (1,1) 图块并给出1 的范围也会给我 (3,0) 瓷砖(我实际需要的是 (0,1)(0,2)(1,0)(1,2)(2,1)(2,2 ) ),它有点靠近磁贴(因为数组的结构方式),但它并不是我真正想要选择的。我可以暴力破解它,但这不会很漂亮,可能不会覆盖所有“选择半径事物”的方面。

有人可以在这里指出正确的方向吗?

【问题讨论】:

  • 但即使它不是六角瓷砖(即方形瓷砖),你也不能做那些“简单的半径选择”。因此,我认为这个问题实际上与 hex-tile 无关(但当然 hex-tile 会使问题变得更加困难)
  • 也许考虑如何处理每个图块以便更轻松地按半径进行选择。目前您使用的是 x/y 坐标系,但您想使用极坐标进行查找。它们不太匹配。
  • 我找到了算法,但它有一个重要错误
  • void select(int x, int y) { printf("X : %d, Y : %d\n",x,y); } void selectRange(int x, int y, int range) { int minX = x - range, maxX = x + range; for (int i = minX; i
  • 此问题与:stackoverflow.com/questions/4585135/… 完全相同。我没有足够的声誉来投票结束这个问题,因为已经存在一个问题。

标签: c++ tile hexagonal-tiles


【解决方案1】:

什么是六边形网格?

您在上面看到的是两个网格。这完全取决于您为瓷砖编号的方式以及您理解六边形网格的方式。在我看来,六边形网格只不过是一个变形的正交网格。

我用紫色圈出的两个六角图块理论上仍然与0,0 相邻。然而,由于我们从正交网格获得六边形网格所经历的变形,两者不再视觉上相邻。

变形

我们需要了解的是变形发生在某个方向上,在我的示例中沿着[(-1,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,而是两倍,即6

所以回顾一下。 (1, 1)(-2, 5) 之间的距离是(3, -3) 的长度加上(0, -1) 的长度。那是distance = 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;
}

现在,鉴于上述实现的ComputeDistanceHexGrid 函数,您现在可以拥有一个简单的、未优化的选择算法实现,该算法将忽略超出指定选择范围的任何图块:

int _tmain(int argc, _TCHAR* argv[])
{
  // your radius selection now becomes your usual orthogonal algorithm
  // except you eliminate hex tiles too far away from your selection center
  // for(x-range;x+range;x++); for(y-range;y+range;y++);
  Point selectionCenter = {1, 1};
  int range = 1;

  for ( int x = selectionCenter.x - range;
            x <= selectionCenter.x + range;
            ++x )
  {
    for ( int y = selectionCenter.y - range;
              y <= selectionCenter.y + range;
              ++y )
    {
      Point p = {x, y};
      if ( ComputeDistanceHexGrid(selectionCenter, p) <= range )
        cout << "(" << x << ", " << y << ")" << endl;
      else
      {
        // do nothing, skip this tile since it is out of selection range
      }
    }
  }

    return 0;
}

对于一个选择点(1, 1)1的范围,上面的代码会显示预期的结果:

(0, 0)
(0, 1)
(1, 0)
(1, 1)
(1, 2)
(2, 1)
(2, 2)

可能的优化

为了优化这一点,您可以将了解图块距离选择点多远的逻辑(在 ComputeDistanceHexGrid 中找到的逻辑)直接包含在选择循环中,这样您就可以以一种避免超出的方式迭代网格范围瓷砖。

【讨论】:

  • 谢谢你,如果你相信上帝,那么上帝保佑你^^
  • @user2189473 我已经用示例代码更新了我的答案。这应该会有所帮助。
  • 谢谢^^ 但是为什么结果有(0,0)??
  • 如果 selectionCenter 为 (1,1),range = 1 则结果为 (1,0) (2,0) (0,1) (1,1) (2,1) (1 ,2) (2,1)
  • @user2189473 查看我的答案中的网格图。选择点在 (1, 1),并且 (0, 0) 与其相邻。我们没有使用相同的坐标系。我的回答建议更容易管理。尝试阅读我的完整解释,您将了解背后的逻辑。如果您有任何问题,请告诉我。
【解决方案2】:

只需按如下方式布局您的网格:

   +-----+-----+-----+-----+
   | 0   |  1  | 2   |  3  |          even row
   +-----+-----+-----+--+-----+
      |  4  |  5  |  6  |  7  | ==>   odd row
   +--+-----+-----+-----+--+--+
   |  8  |  9  |  10 |  11 |          even row
   +-----+-----+-----+-----+

对于奇数行(N&gt;&gt;2)&amp;1 == 1,从概念上讲,必须将插槽移动插槽宽度的一半。
那么邻居是{ N-1, N+1, N-a, N-a+1, N+b, N+b+1 }, a=b=4.
对于偶数行,a=5,b=3。 (a=上面,b=下面)

有了这个想法,人们可以建立一个邻接列表并执行广度优先搜索(使用一些距离指标——欧几里得或任何曼哈顿距离在蜂蜜网格上被称为)。

【讨论】:

  • 谢谢你,如果你相信上帝,那么上帝保佑你^^
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-06
相关资源
最近更新 更多