【问题标题】:snap to closest hexagon centre in hex based grid捕捉到基于十六进制的网格中最近的六边形中心
【发布时间】:2014-03-04 18:44:44
【问题描述】:

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

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 天,真的很想弄清楚。 谢谢

【问题讨论】:

  • 我发布这个问题已经 2 年了,但我确实记得看过那个帖子,我相信它并没有解决我的问题(虽然我确信它有帮助),但是它太长了让我记住为什么它没有回答我的问题的细节。很奇怪在这么长时间后看到这个帖子的活动!啊哈

标签: .net algorithm c++-cli rounding hexagonal-tiles


【解决方案1】:

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

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

【讨论】:

  • 此外,由于您只对找到最近的六边形感兴趣,您应该能够省略平方根检查,因为实际长度不会用于任何事情。如果您要检查很多多边形,这可能会为您节省一些周期。 Tyler 的基于宽度的剔除点也是一个非常快速的优化,您应该考虑实施并且绝对应该用作预测试。
  • 对,我包括 sqrt 只是为了完整性。在实际实现中你不需要做sqrt,只需要差的平方和,也就是点之间的方差。
【解决方案2】:

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

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

角“+”也是六边形的角。当 'x' mod W y DIV H,在偶数列上,它是 (y + H / 2) DIV H

锯齿形图案的灰色区域显示效果不佳,需要求解两个线性方程(或点积)才能确定该点落在对角线的哪一侧。无论如何,最多有两个候选人可供选择。

【讨论】:

    【解决方案3】:

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

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

    注意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;
    }
    

    【讨论】:

      【解决方案4】:

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

      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
      

      【讨论】:

      • 呸,你复制了我的答案
      • @TylerDurden xD 我可能先开始打字,但是是的。具有讽刺意味的是,你的更完整。阈值是个好主意。 +1
      猜你喜欢
      • 1970-01-01
      • 2019-02-08
      • 2011-06-01
      • 1970-01-01
      • 2020-08-06
      • 2011-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多