XNA에서 육각형 타일 맵 선택을 어떻게 구현할 수 있습니까?


13

육각형을 클릭했을 때 확인 해야하는 육각형 타일 맵이 있습니다. 육각형은 실제로 닿지 않고 각 육각형 사이에 약간의 간격이 있습니다.

누구나 전체를 복잡하게하지 않고 육각형을 클릭했는지 확인하는 방법을 알고 있습니까?

답변:


13

이 사진을 한번보세요

육각형 분해

보시다시피, x, y 직사각형 좌표계를 6 각형 좌표계에 매핑하는 비교적 직관적 인 방법이 있습니다.

우리는 "정확한"불규칙한 육각형, 즉 타원으로 새겨진 육각형 또는 불규칙하게 양 방향으로 스케일링 된 정규 육각형으로부터 얻은 육각형 (회전-전단 없음)에 대해 이야기 할 수 있습니다.

정사각형 육각형은 둘레 사각형의 높이와 너비에 곱한 사각형의 너비를 더한 값으로 정의 할 수 있습니다. (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

우리는 i 씩 하나씩 줄입니다

이제 i가 수평 육각형 인덱스 (열)이고 j / 2의 정수 부분이 수직 육각형 인덱스 (행)임을 알기 위해 j를 1 씩 감소시켜야합니다.

여기에 이미지 설명을 입력하십시오


감사! 정말 도움이되었고 녹색 섹션의 분할로 인해 몇 가지 문제가 발생했습니다 (i에서 1을 빼야하는지 여부). 나는 그것이 육각형의 방향 때문이라고 생각합니다. 모든 작업, 감사합니다!
Joel

14

정규 육각형에는 6 개의 대칭 축이 있지만 육각형에는 2 개의 대칭 축만 있다고 가정합니다 ( 예 : 모든 각도가 정확히 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 : 예, 굉장합니다. 나는 웹이나 클라우드 서비스를 좋아하지 않지만 이것은 도움이된다.
샘 호세 바

1

육각형 영역 안에 3 개의 회전 된 사각형을 맞출 수 있으며 올바르게 완료되면 영역을 정확하게 채 웁니다. 그런 다음 단순히 세 사각형에서 충돌을 확인하는 것입니다.


1

타일 ​​사이에서 클릭을 등록 해제 할 필요는 없습니다. 다시 말해, 논리적으로해서는 안되는 무언가로 채워진 타일 사이의 넓은 공간에 대해 이야기하지 않는 한 타일 사이의 공간을 클릭 할 수 있도록 허용하면 아프지 않으며 플레이어를 도울 수도 있습니다. 클릭하십시오. (즉, 육각형은 사람들과 같은 클릭 가능한 다른 것들이있는 큰지도상의 도시입니다)

위의 작업을 수행하려면 모든 헥스의 중심을 플로팅 한 다음 모든 헥스의 평면을 클릭하면 가장 가까운 마우스를 찾을 수 있습니다. 테셀레이션 된 육각형 평면에서 가장 가까운 중심은 항상 마우스를 올려 놓은 것과 동일합니다.


1

스택 오버플로에서 동일한 목표로 비슷한 질문에 이미 답변했습니다 . 편의를 위해 여기에 다시 게시 할 것입니다. (NB-모든 코드는 Java로 작성되고 테스트되었습니다)

정사각형 격자가 겹쳐진 6 각 격자

이 이미지는 6 각형 격자의 왼쪽 상단을 보여 주며 오버레이는 파란색 사각형 격자입니다. 점이 어느 사각형 안에 있는지 쉽게 찾을 수 있으며 이것은 어느 육각형에 대한 대략적인 근사치를 제공합니다. 육각형의 흰색 부분은 정사각형과 육각형 격자가 동일한 좌표를 공유하는 위치를 나타내고 육각형의 회색 부분은 그렇지 않은 위치를 나타냅니다.

해결책은 이제 어떤 상자에 어떤 상자가 있는지 찾아서 삼각형 중 하나에 점이 있는지 확인하고 필요한 경우 답을 수정하는 것만 큼 간단합니다.

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의 좌표계는 화면에서 왼쪽 상단이 0에서 시작하여 y가 왼쪽 하단이 아니라 수학에서 왼쪽으로 사용되므로 왼쪽 가장자리에 사용 된 음의 기울기와 오른쪽에 사용되는 양의 기울기가 있습니다.

    // 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


네오 샤맘 의 위의 추가

이것은 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, 0) 및 (0, 1)과 동일한 대각선 열에있게됩니다.


나는 이것이 육각형 사이의 간격을 고려하지는 않지만 문제를 상당히 좁히는 데 도움이된다는 것을 알고 있습니다.
Troyseph

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

전체 프로세스의 성능을 향상시키기 위해 (육각형 이미지 전체에서) 사각형 충돌 검사를 미리 수행 할 수 있습니다.

개념은 이해하고 구현하기가 매우 간단하지만 육각형이 모두 같은 경우에만 작동합니다. 가능한 육각 치수 세트 만있는 경우에도 작동 할 수 있으며, 이는 충돌 오버레이가 둘 이상 필요하다는 것을 의미합니다.

그것이 훨씬 더 완전하고 재사용 가능한 것 (수학을 사용하여 실제로 충돌을 찾음)에 대한 매우 단순한 해결책이라고 생각한다면 분명히 내 의견으로는 시도해 볼 가치가 있습니다.


원하는 방식으로 불규칙한 모양을 사용하고 고유 한 색상으로 오버레이를 모두 제공 한 다음 오버레이중인 객체에 색상을 매핑하여 오버레이 버퍼에서 색상을 선택한 다음지도를 사용하여 마우스 아래에있는 개체. 내가이 기술을 특별히지지하고있는 것은 아니지만, 약간 복잡하고, 조기 최적화 IMHO 시도가 들린다.
Troyseph

0

에 대한 기사가있어 게임 프로그래밍 보석 (7) 라고 꿀벌과 게이머가 : 육각형 타일을 처리하는 방법을 정확하게 당신이 필요로 할 것이다.

불행히도 나는 지금 나와 함께 책의 사본을 가지고 있지 않다. 그렇지 않으면 나는 그것을 조금 설명 할 수 있었다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.