4

我正在尝试创建一个基于网格的游戏。到目前为止,我有一个基于六边形瓷砖的网格,其坐标方案如下所示:

col 0
 | col 1
 |   | col 2
 |   |  |
 __  | __    __    __    __   
/00\__/02\__/04\__/06\__/08\__
\__/01\__/03\__/05\__/07\__/09\--- row 0
/10\__/12\__/14\__/16\__/18\__/
\__/11\__/13\__/15\__/17\__/19\--- row 1
/20\__/22\__/24\__/26\__/28\__/
\__/21\__/23\__/25\__/27\__/29\--- row 2
/30\__/32\__/34\__/36\__/38\__/
\__/  \__/  \__/  \__/  \__/   --- row 3

在现实生活中看起来像这样,每个六边形都有随机颜色:

我正在努力弄清楚的是,当用户点击一个六边形时,我如何确定他们点击了哪个六边形?

到目前为止我尝试过的代码如下:

private: System::Void MyForm_MouseDown(System::Object^  sender,
    System::Windows::Forms::MouseEventArgs^  e) {

    int CloseI=0,CloseJ=0;
    CloseJ = FindNearesetX(e->X);
    CloseI = FindNearesetY(e->Y);
    //Grid[down(y)][along(x)]
    P1.X = Grid[CloseI][CloseJ].GetX();
    P1.Y = Grid[CloseI][CloseJ].GetY();
} // END MOUSE DOWN EVENT

int FindNearesetX(int ActualX){
    int ClosestJPos;
    ClosestJPos = ((ActualX-Grid[0][0].GetX())/(1.5*HexSideLength));
    return ClosestJPos;
}//END FIND NEAREST X

int FindNearesetY(int ActualY){
    int ClosestIPos;
    ClosestIPos = ((ActualY-Grid[0][0].getY())/(HexHeight));
    return ClosestIPos;
}//END FIND NEAREST Y

private: System::Void MyForm_MouseMove(System::Object^  sender,
    System::Windows::Forms::MouseEventArgs^  e) {
    this->Invalidate();

    P2.X = e->X;
    P2.Y = e->Y; 
} // END MOUSE MOVE EVENT       

然而,这并没有按我的意愿工作,这是因为当用户单击六边形中心点的左侧时,它会捕捉到他们单击的左侧的六边形,并且如果他们单击中心点上方所有奇数列都捕捉到他们点击的那一列上方的六边形。

我已经在这个问题上停留了 2 天,真的很想弄清楚。谢谢

4

4 回答 4

5

单击的点将始终最靠近发生单击的六边形中心,除非该点恰好位于两个六边形之间,在这种情况下,它将与两个中心等距。两点之间距离的方程是 SQRT( (x1-x2)^2 + (y1-y2)^2 )。

您不必测试到每个六边形的距离。通过创建 x/y 阈值,您可以将测试限制在附近的六边形。例如,如果六边形的宽度为 10 并且点位于 (51, 73) 处,则您不必测试 x 坐标小于 40 或 > 70 的六边形。

于 2014-03-04T19:05:18.940 回答
3

应该能够找到具有 O(1) 复杂度的最接近的六边形:

    odd     even    odd    even
 0 +----+  |    |  +----+  |
   | 00 |\ |    |  | 02 |  |
   |    | \+----+  |    |  +
   |    | /| 01 |  |    |  |
 H +----+/ |    |  +----+  |
   | 10 |\ |    |  | 12 |  |
   |    | \+----+  |    |  +
   |    | /| 11 |  |    |  |
2H +----+/ |    |  +----+  |
   0....X..W.......2W......3W

角“+”也是六边形的角。当 'x' mod W < X 时,'x' DIV 2*W 和 y DIV H 确定正确的平方。当 W <= x mod 2W <= W+X 时,该点位于偶数列上。在奇数列上,行号是y DIV H,在偶数列上,它是 ( y + H / 2) DIV H

锯齿形图案难以说明的灰色区域需要求解两个线性方程(或点积)以确定该点落在对角线的哪一侧。在任何情况下,最多有两个候选人可供选择。

于 2014-03-04T20:34:02.780 回答
3

事实上,这可以很容易地在数学上完成,而无需求助于迭代大量潜在值的烦人的、规模限制的方法。我想出了以下代码与以下网站上的优秀信息相勾结。秘诀是想象你的六边形网格实际上是一个三维立方体的平面。

http://www.redblobgames.com/grids/hexagons/

NB SS2DCoordinates 和 SS3DCoordinates 是简单的结构,具有两个或三个整数变量,分别表示 2D 和 3D 网格上的坐标(x/y 表示 2D,x/y/z 表示 3D)另外请注意,我的十六进制网格从 1/1 开始,而不是0/0。

SS2DCoordinates coordinatesForHexAtPoint(float a, float b)
{
    // Get basic hex information - pseudocode
    float radius = <radius of one hexagon>

    // Estimate the most likely hex and round to nearest values
    float x = 2.0/3.0*a/radius;
    float z = (1.0/3.0*sqrt(3.0)*b-1.0/3.0*a)/radius;
    float y = -x-z;

    int ix = (int)round((floor(x-y)-floor(z-x))/3.0);
    int iy = (int)round((floor(y-z)-floor(x-y))/3.0);
    int iz = (int)round((floor(z-x)-floor(y-z))/3.0);

    // Adjust to flat coordinates on the offset numbering system
    SS2DCoordinates corrected = hexToFlatCoordinates(SS3DCoordinatesMake(ix, iy, iz));
    corrected.x --;
    return axialToOffsetCoordinates(corrected);
}


SS2DCoordinates hexToFlatCoordinates(SS3DCoordinates hex)
{
    SS2DCoordinates coordinates;
    coordinates.x = hex.x;
    coordinates.y = hex.z;
    return coordinates;
}


SS2DCoordinates axialToOffsetCoordinates(SS2DCoordinates axial)
{
    SS2DCoordinates offset;
    offset.x = axial.x;
    offset.y = axial.y + (NSInteger)ceilf((float)axial.x/2.0);
    return offset;
}
于 2014-04-04T11:26:42.133 回答
0

实际上,由于六边形的规则形状(所有边的长度相同),这就像循环浏览六边形瓷砖列表并找出哪个瓷砖的中心最靠近鼠标点击一样简单。

C++ 伪代码:

//assuming "map" is an array of "Tile" pointers

Tile *closest = nullptr;
int fromClosestCenterToClick = INT_MAX;

for (int row = 0; row < map.numRows(); row++)
{
   for (int col = 0; col < map.numCols(); col++)
   {
      int distance = std::sqrt(std::pow(map[row][column]->center.x - mouseClickX, 2) + std::pow(map[row][column]->center.y - mouseClickY, 2) < fromClosestCenterToClick);
      if (distance < fromClosestCenterToClick)
      {
         closest = map[row][column];
         fromClosestCenterToClick = distance;
      }
   }
}
//closest now holds the correct tile
于 2014-03-04T19:08:15.060 回答