Làm cách nào tôi có thể triển khai chọn tilemap lục giác trong XNA?


13

Tôi có một bản đồ lát hình lục giác trong đó tôi cần kiểm tra khi nhấp vào hình lục giác. Các hình lục giác không thực sự chạm vào, thay vào đó chúng có một khoảng cách nhỏ ở giữa chúng.

Có ai biết làm thế nào tôi có thể kiểm tra xem một hình lục giác được nhấp mà không làm phức tạp quá mức không?

Câu trả lời:


13

Hãy nhìn vào bức tranh này

phân rã lục giác

Như bạn có thể thấy có một cách tương đối trực quan để ánh xạ hệ tọa độ hình chữ nhật x, y sang hình lục giác.

Chúng ta có thể nói về các hình lục giác không đều "trực tràng" tức là các hình lục giác được ghi bằng hình elip hoặc hình lục giác thu được từ các hình lục giác thông thường được chia tỷ lệ theo cả hai hướng không tương xứng (không có phép quay - cắt).

Một hình lục giác trực tiếp có thể được xác định bởi chiều cao và chiều rộng của hình chữ nhật bao quanh cộng với chiều rộng của hình chữ nhật. (W, w, h)

ghi / cắt hình chữ nhật

Cách dễ nhất để tìm ra chỉ số lục giác là phân vùng không gian như sau:

phân vùng không gian

Chiều rộng hình chữ nhật là w + (W - w) / 2 = (w + W) / 2, chiều cao của nó là h / 2; chiều rộng của hình chữ nhật màu xanh lá cây là (Ww) / 2. Rất dễ để tìm ra nơi hình chữ nhật rơi xuống:

nhập mô tả hình ảnh ở đây

u và v là tọa độ nhắc nhở cho biết điểm nằm ở đâu trong hình chữ nhật i, j: Sử dụng w chúng ta có thể nói nếu chúng ta ở trong khu vực màu xanh lá cây (u <(Ww) / 2) hay không.

nếu đó là trường hợp chúng ta ở trong khu vực màu xanh lá cây, chúng ta cần biết nếu chúng ta ở nửa trên hoặc dưới của hình lục giác: chúng ta ở nửa trên nếu i và j đều chẵn hoặc cả hai lẻ; chúng tôi ở nửa dưới khác.

Trong cả hai trường hợp, nó rất hữu ích để biến đổi u và v để chúng khác nhau giữa 0 và 1:

nhập mô tả hình ảnh ở đây

nếu chúng ta ở nửa dưới v <u

hoặc là

nếu chúng ta ở nửa trên (1-v)> u

sau đó chúng tôi giảm cho tôi một

Bây giờ chúng ta chỉ cần giảm j cho một nếu tôi là số lẻ để thấy rằng tôi là chỉ số hình lục giác ngang (cột) và phần nguyên của j / 2 là chỉ số hình lục giác dọc (hàng)

nhập mô tả hình ảnh ở đây


Cảm ơn! Thực sự hữu ích, đã có một vài rắc rối phát sinh với việc chia phần màu xanh lá cây (tôi có nên trừ 1 từ tôi hay không), nhưng tôi nghĩ đó là do sự định hướng của các hình lục giác của tôi. Tất cả đều hoạt động, cảm ơn!
Joel

14

Các hình lục giác thông thường có sáu trục đối xứng, nhưng tôi sẽ giả sử các hình lục giác của bạn chỉ có hai trục đối xứng ( nghĩa là tất cả các góc không chính xác là 60 độ). Không nhất thiết là vì bạn không có sự đối xứng hoàn toàn, nhưng bởi vì nó có thể hữu ích cho người khác.

Dưới đây là các tham số của một hình lục giác. Tâm của nó nằm ở O, chiều rộng lớn nhất là 2a, chiều cao 2bvà chiều dài của cạnh trên là2c .

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

Đây là cách bố trí hàng / cột, với điểm gốc ở giữa hình lục giác phía dưới bên trái. Nếu thiết lập của bạn khác, hãy dịch (x,y)tọa độ của bạn để quay lại trường hợp này hoặc sử dụng -ythay vìy :

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

Đoạn mã sau sẽ cung cấp cho bạn hàng và cột của hình lục giác chứa điểm (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;
}

Bạn có thể kiểm tra xem đoạn mã trên có vẽ các hình lục giác hoàn hảo trên lần chạy IdeOne này không .


Lần đầu tiên tôi nghe về ideOne, nhưng nó có vẻ thực sự hữu ích!
David Gouveia

@davidluzgouveia: vâng, nó thật tuyệt vời. Tôi không thích các dịch vụ web hoặc đám mây nhưng cái này rất hữu ích.
sam hocevar

1

Bạn có thể đặt 3 hình chữ nhật xoay trong khu vực của hình lục giác, và nếu được thực hiện đúng cách, nó sẽ lấp đầy khu vực chính xác. Sau đó, nó sẽ chỉ đơn giản là một vấn đề kiểm tra va chạm trên ba hình chữ nhật.


1

Bạn có thể không cần phải hủy đăng ký nhấp chuột giữa các ô. Điều đó có nghĩa là, nó sẽ không gây tổn thương và thậm chí có thể giúp người chơi nếu bạn cho phép các khoảng trống giữa các ô cũng có thể nhấp vào trừ khi bạn đang nói về một khoảng trống lớn giữa chúng chứa đầy thứ gì đó không hợp lý được bấm (Giả sử, các hình lục giác là các thành phố trên bản đồ lớn, ở giữa chúng là những thứ có thể nhấp khác như mọi người)

Để làm như trên, bạn có thể chỉ cần vẽ các tâm của tất cả các hình lục giác, sau đó tìm cái gần nhất với con chuột khi nhấp vào mặt phẳng của tất cả các hình lục giác. Trung tâm gần nhất trên một mặt phẳng của các hình lục giác tessellated sẽ luôn luôn giống như bạn đang di chuột qua.


1

Tôi đã trả lời một câu hỏi tương tự, với các mục tiêu giống hệt nhau, trên Stack Overflow Tôi sẽ đăng lại nó ở đây để đảm bảo: (NB - tất cả mã được viết và kiểm tra bằng Java)

Lưới lục giác với lưới vuông phủ

Hình ảnh này cho thấy góc trên cùng bên trái của lưới hình lục giác và được phủ lên là lưới hình vuông màu xanh. Thật dễ dàng để tìm ra điểm nào của hình vuông bên trong và điều này sẽ đưa ra một xấp xỉ thô về hình lục giác nào. Các phần màu trắng của hình lục giác hiển thị nơi lưới hình vuông và hình lục giác có cùng tọa độ và các phần màu xám của hình lục giác hiển thị ở nơi chúng không có.

Giải pháp bây giờ đơn giản như tìm một điểm nằm trong ô nào, sau đó kiểm tra xem điểm đó có nằm trong một trong ba hình tam giác hay không và sửa câu trả lời nếu cần.

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

Tại thời điểm này, chúng ta có hàng và cột của hộp, điểm tiếp theo của chúng ta, tiếp theo chúng ta cần kiểm tra điểm của chúng ta dựa vào hai cạnh trên của hình lục giác để xem điểm của chúng ta có nằm trong một trong các hình lục giác ở trên không:

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

Có tọa độ tương đối làm cho bước tiếp theo dễ dàng hơn.

Phương trình chung cho một đường thẳng

Giống như trong hình trên, nếu y của điểm của chúng tôi là > mx + c, chúng tôi biết điểm của chúng tôi nằm phía trên đường thẳng và trong trường hợp của chúng tôi, hình lục giác ở trên và bên trái của hàng và cột hiện tại. Lưu ý rằng hệ tọa độ trong java có y bắt đầu từ 0 ở phía trên bên trái màn hình chứ không phải phía dưới bên trái như thông thường trong toán học, do đó gradient âm được sử dụng cho cạnh trái và gradient dương được sử dụng cho bên phải.

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

Giải thích nhanh về các biến được sử dụng trong ví dụ trên:

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

m là độ dốc, vì vậy m = c / HalfWidth


Bổ sung của NeoShamam ở trên

Đây là phần phụ lục cho câu trả lời của SebastianTroy. Tôi sẽ để nó như một bình luận nhưng tôi chưa đủ danh tiếng.

Nếu bạn muốn triển khai một hệ tọa độ trục như được mô tả ở đây: http://www.redblobgames.com/grids/hexagons/

Bạn có thể thực hiện một sửa đổi nhỏ cho mã.

Thay vì

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

dùng cái này

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

Điều này sẽ làm cho tọa độ (0, 2) nằm trên cùng một cột đường chéo là (0, 0) và (0, 1) thay vì trực tiếp bên dưới (0, 0).


Tôi nhận ra rằng điều này không tính đến các khoảng trống giữa các hình lục giác nhưng nó sẽ giúp thu hẹp đáng kể vấn đề của bạn.
Troyseph

0

Nếu tất cả các hình lục giác của bạn được tạo bằng cách sử dụng cùng một tỷ lệ và cách đặt, bạn có thể sử dụng một số loại tài sản lớp phủ cho các va chạm, một cái gì đó dọc theo dòng: Lớp phủ va chạm cho một hình lục giác

Sau đó, tất cả những gì bạn phải làm là đặt hình ảnh va chạm nơi hình lục giác của bạn, đặt vị trí chuột so với góc bên trái và xem pixel của vị trí tương đối KHÔNG có màu trắng (có nghĩa là có va chạm).

Mã (không được kiểm tra):

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

Rõ ràng bạn có thể thực hiện kiểm tra va chạm hình chữ nhật trước đó (của toàn bộ hình lục giác của bạn) để cải thiện hiệu suất của toàn bộ quá trình.

Khái niệm này khá đơn giản để hiểu và thực hiện, nhưng chỉ hoạt động nếu các hình lục giác của bạn đều giống nhau. Nó cũng có thể hoạt động nếu bạn chỉ có một tập hợp các kích thước hình lục giác có thể, điều đó có nghĩa là bạn sẽ cần nhiều hơn một lớp phủ va chạm.

Nếu thấy nó là một giải pháp rất đơn giản cho những gì có thể hoàn thiện hơn và có thể tái sử dụng (sử dụng toán học để thực sự tìm thấy sự va chạm) nhưng theo tôi thì chắc chắn nó đáng để thử.


Theo cách của bạn, bạn có thể sử dụng bất kỳ tập hợp các hình dạng bất thường nào và cung cấp cho chúng tất cả một lớp phủ với một màu duy nhất, sau đó ánh xạ màu sắc đến đối tượng chúng đang phủ để bạn chọn một màu từ bộ đệm lớp phủ của bạn và sau đó sử dụng bản đồ của bạn để lấy đối tượng dưới chuột. Không phải là tôi đặc biệt tán thành kỹ thuật này, nghe có vẻ hơi phức tạp và một nỗ lực tối ưu hóa sớm IMHO.
Troyseph

0

Có một bài viết về Lập trình trò chơi Đá quý 7 có tên Dành cho Ong và Game thủ: Cách xử lý Gạch lục giác sẽ chính xác là những gì bạn cần.

Thật không may, hiện tại tôi không có bản sao của cuốn sách, nếu không tôi có thể mô tả nó một chút.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.