如何在XNA中实现六角形的贴图拾取?


13

我有一个六边形平铺的地图,需要检查单击六边形的时间。六边形实际上并没有接触,而是在它们之间略有间隙。

有谁知道我该如何去检查是否单击了六角形而不会使整个事情复杂化?

Answers:


13

看看这张照片

六角分解

如您所见,有一种相对直观的方法将x,y直角坐标系映射到六角形坐标系。

我们可以谈论“矩形”不规则六边形,即刻在椭圆上的六边形或从在两个方向上成比例缩放的正六边形获得的六边形(无旋转剪切)。

矩形可以由外接矩形的高度和宽度加上外接矩形的宽度来定义。(W,W,H)

内切/外接矩形

找出六角形索引的最简单方法是按以下方式划分空间:

空间划分

矩形宽度为w +(W-w)/ 2 =(w + W)/ 2,其高度为h / 2;绿色矩形的宽度为(Ww)/ 2。很容易找出该点落在哪个矩形中:

在此处输入图片说明

u和v是提示点,它指示点在i,j矩形中的位置:使用w可以说我们是否在绿色区域(u <(Ww)/ 2)中。

如果是这种情况,我们在绿色区域,我们需要知道我们是在六边形的上半部还是下半部:如果i和j均为偶数或均为奇数,则我们位于上半部。否则,我们处于下半部分。

在这两种情况下,对u和v进行变换都是有用的,因此它们在0到1之间变化:

在此处输入图片说明

如果我们在下半部分并且 v <u

要么

如果我们在上半部分并且(1-v)> u

然后我减一

现在,如果我很奇怪,我们仅需将j减1即可看到i是水平六边形索引(列),而j / 2的整数部分是垂直六边形索引(行)

在此处输入图片说明


谢谢!确实有帮助,因为分割绿色部分时遇到了一些麻烦(我是否应该从i中减去1),但是我认为这是由于六边形的方向所致。一切顺利,谢谢!
乔尔

14

规则的六边形具有六个对称轴,但是我将假设您的六边形只有两个对称轴(即,所有角度都不完全是60度)。不一定是因为您的不完全对称,而是因为它可能对其他人有用。

这是一个六边形的参数。其中心在O,最大宽度在2a,高度在2b,上边缘的长度在2c

         Y ^
           |
       ____|____
      /  b |   |\
     /     |   | \
    /      |   |  \
---(-------+---+---)------>
    \     O|   c  / a      X
     \     |     /
      \____|____/
           |

这是行/列的布局,其原点位于左下六角形的中心。如果您的设置不同,请平移(x,y)坐标以退回这种情况,或者使用-y代替y例如:

col 0
 | col 1
 |   | col 2
 |   |  |
 __  | __    __    __    __   
/  \__/  \__/  \__/  \__/  \__
\__/  \__/  \__/  \__/  \__/  \
/  \__/  \__/  \__/  \__/  \__/
\__/  \__/  \__/  \__/  \__/  \
/  \__/  \__/  \__/  \__/  \__/_ _ line 2
\__/  \__/  \__/  \__/  \__/  \ _ _ _ line 1
/ .\__/  \__/  \__/  \__/  \__/_ _ line 0
\__/  \__/  \__/  \__/  \__/

下面的代码将为您提供包含点的六边形的行和列(x,y)

static void GetHex(float x, float y, out int row, out int column)
{
  // Find out which major row and column we are on:
  row = (int)(y / b);
  column = (int)(x / (a + c));

  // Compute the offset into these row and column:
  float dy = y - (float)row * b;
  float dx = x - (float)column * (a + c);

  // Are we on the left of the hexagon edge, or on the right?
  if (((row ^ column) & 1) == 0)
      dy = b - dy;
  int right = dy * (a - c) < b * (dx - c) ? 1 : 0;

  // Now we have all the information we need, just fine-tune row and column.
  row += (column ^ row ^ right) & 1;
  column += right;
}

您可以检查上面的代码在此IdeOne运行中是否绘制了完美的六边形。


第一次听说ideOne,但这似乎真的很有用!
David Gouveia

@davidluzgouveia:是的,太棒了。我不喜欢Web或云服务,但是这一功能很有帮助。
sam hocevar 2011年

1

您可以在六角形区域内放入3个旋转的矩形,如果操作正确,它将完全填充该区域。然后,只需检查三个矩形上的碰撞即可。


1

您可能不需要注销磁贴之间的点击。也就是说,如果您也允许平铺瓷砖之间的间隔也是可点击的,那不会有伤害甚至可能对玩家有帮助,除非您谈论的是平铺瓷砖之间的大空间,而在逻辑上不应填充被点击。(例如,十六进制是大地图上的城市,它们之间是其他可点击的东西,例如人)

为此,您可以简单地绘制所有六边形的中心,然后在单击所有六边形的平面时找到距离鼠标最近的一个。棋盘格六边形平面上最近的中心将始终与您悬停在同一平面上。


1

我已经在Stack Overflow上回答了一个具有相同目标的类似问题,为了方便起见,我将其重新张贴在这里:(注意-所有代码都是用Java编写和测试的)

六角形网格和覆盖的方形网格

此图显示了六角形网格的左上角,并且覆盖了一个蓝色方形网格。很容易找到一个点在哪个正方形内,这也将大致得出一个六角形。六边形的白色部分显示正方形和六边形网格共享相同坐标的位置,而六边形的灰色部分则显示它们不共享坐标的位置。

现在,解决方案非常简单,只需找到一个点位于哪个盒子中,然后检查该点是否在两个三角形中,然后在必要时更正答案即可。

private final Hexagon getSelectedHexagon(int x, int y)
{
    // Find the row and column of the box that the point falls in.
    int row = (int) (y / gridHeight);
    int column;

    boolean rowIsOdd = row % 2 == 1;

    // Is the row an odd number?
    if (rowIsOdd)// Yes: Offset x to match the indent of the row
        column = (int) ((x - halfWidth) / gridWidth);
    else// No: Calculate normally
        column = (int) (x / gridWidth);

在这一点上,我们有了点所在的框的行和列,接下来我们需要针对六边形的两个上边缘测试我们的点,以查看我们的点是否位于上面的六边形中的任意一个中:

    // Work out the position of the point relative to the box it is in
    double relY = y - (row * gridHeight);
    double relX;

    if (rowIsOdd)
        relX = (x - (column * gridWidth)) - halfWidth;
    else
        relX = x - (column * gridWidth);

具有相对坐标使下一步变得更容易。

直线的通用方程式

就像上图中一样,如果我们的点的y > mx + c,我们知道我们的点位于直线上方,在我们的情况下,该点位于当前行和列的上方和左侧的六边形。请注意,java中的坐标系的y从屏幕左上角的0开始,而不是数学上通常的左下角,因此,左边缘使用负梯度,右边缘使用正梯度。

    // Work out if the point is above either of the hexagon's top edges
    if (relY < (-m * relX) + c) // LEFT edge
        {
            row--;
            if (!rowIsOdd)
                column--;
        }
    else if (relY < (m * relX) - c) // RIGHT edge
        {
            row--;
            if (rowIsOdd)
                column++;
        }

    return hexagons[column][row];
}

上例中使用的变量的简要说明:

在此处输入图片说明 在此处输入图片说明

m是渐变,所以m = c / halfWidth


NeoShamam对上述内容的补充

这是SebastianTroy的答复的附录。我将其留为评论,但我的声誉还不够。

如果要按此处所述实现轴坐标系:http : //www.redblobgames.com/grids/hexagons/

您可以对代码进行一些修改。

代替

// Is the row an odd number?
if (rowIsOdd)// Yes: Offset x to match the indent of the row
    column = (int) ((x - halfWidth) / gridWidth);
else// No: Calculate normally
    column = (int) (x / gridWidth);

用这个

float columnOffset = row * halfWidth;
column = (int)(x + columnOffset)/gridWidth; //switch + to - to align the grid the other way

这将使坐标(0,2)与(0,0)和(0,1)在同一对角线上,而不是直接在(0,0)下方。


我意识到这并没有考虑到六边形之间的间隙,但可以帮助您缩小问题的范围。
Troyseph 2015年

0

如果所有六边形都是使用相同的比例和位置制成的,则可以对碰撞使用某种叠加资源,类似于: 六边形的碰撞覆盖

然后,您所要做的就是将碰撞图像放置在六边形所在的位置,获取鼠标相对于左角的位置,并查看相对位置的像素是否不是白色(这意味着发生碰撞)。

代码(未经测试):

bool IsMouseTouchingHexagon(Vector2 mousePosition, Vector2 hexagonPosition,
    Rectangle hexagonRectangle, Texture2D hexagonImage)
{
    Vector2 mousePositionToTopLeft = mousePosition - hexagonPosition;

    // We make sure that the mouse is over the hexagon's rectangle.
    if (mousePositionToTopLeft.X >= 0 && mousePositionToTopLeft.X < hexagonRectangle.Width && 
        mousePositionToTopLeft.Y >= 0 && mousePositionToTopLeft.Y < hexagonRectangle.Height)
    {
        // Where "PixelColorAt" returns the color of a pixel of an image at a certain position.
        if (PixelColorAt(hexagonImage, mousePositionToTopLeft) == Color.White)
        {
            // If the color is not white, we are colliding with the hexagon
            return true;
        }
    }

    // if we get here, it means that we did not find a collision.
    return false;
}

您显然可以事先(对整个六边形图像)执行矩形碰撞检查,以提高整个过程的性能。

这个概念很容易理解和实施,但是仅在六角形都相同的情况下才有效。如果您只有一组可能的六边形尺寸,这也可能会起作用,这意味着您将需要多个碰撞覆盖。

如果发现它是一个非常简单的解决方案,可以解决更完整,更可重用的问题(使用数学方法真正找到冲突),但是在我看来,绝对值得尝试。


通过这种方式,您可以使用任何一组不规则形状,并为它们提供具有唯一颜色的叠加层,然后将颜色映射到它们叠加的对象,以便从叠加缓冲区中选择一种颜色,然后使用地图获取鼠标下的对象。并不是说我特别赞同这种技术,听起来有点过于复杂,并且尝试过早优化恕我直言。
Troyseph 2015年

0

在《Game Programming Gems 7》上有一篇名为《蜜蜂与游戏玩家:如何处理六角形瓷砖》的文章,这正是您所需要的。

不幸的是,我目前没有这本书的副本,否则我可以稍微描述一下。

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.