Bagaimana saya bisa menerapkan pengambilan tilemap hexagonal di XNA?

13

Saya memiliki peta ubin segi enam di mana saya perlu memeriksa kapan segi enam diklik. Segi enam sebenarnya tidak menyentuh, tetapi mereka memiliki sedikit celah di antara masing-masing.

Apakah ada yang tahu bagaimana saya bisa memeriksa apakah segi enam diklik tanpa terlalu menyulitkan semuanya?

Joel
sumber

Jawaban:

13

Lihatlah gambar ini

dekomposisi heksagonal

Seperti yang Anda lihat ada cara yang relatif intuitif untuk memetakan sistem koordinat persegi panjang x ke sistem heksagonal.

Kita dapat berbicara tentang heksagon tidak beraturan "segi empat" yaitu heksagon yang tertulis dalam elips atau heksagon yang diperoleh dari heksagon biasa yang diskalakan di kedua arah secara tidak proporsional (tidak ada rotasi-geser).

Segi enam segi empat dapat ditentukan oleh tinggi dan lebar persegi panjang yang membatasi ditambah lebar dari yang menuliskan. (W, w, h)

menggambar / membatasi empat persegi panjang

Cara termudah untuk mengetahui indeks heksagonal adalah dengan mempartisi ruang sebagai berikut:

partisi ruang

Lebar persegi panjang adalah w + (W - w) / 2 = (w + W) / 2, tingginya h / 2; lebar kotak hijau adalah (Ww) / 2. Mudah untuk menemukan di mana titik persegi jatuh:

masukkan deskripsi gambar di sini

u dan v adalah pengingat koordinat yang menunjukkan di mana titik berada di dalam persegi panjang i, j: Menggunakan w kita dapat mengatakan apakah kita berada di area hijau (u <(Ww) / 2) atau tidak.

jika demikian halnya kita berada di area hijau kita perlu tahu apakah kita berada di bagian atas atau bawah dari segi enam: kita berada di bagian atas jika i dan j sama-sama genap atau keduanya ganjil; kita berada di bagian bawah sebaliknya.

Dalam kedua kasus itu berguna untuk melakukan trasform u dan v sehingga bervariasi antara 0 dan 1:

masukkan deskripsi gambar di sini

jika kita berada di bagian bawah dan v <u

atau

jika kita berada di bagian atas dan (1-v)> u

maka kita mengurangi satu per satu

Sekarang kita hanya perlu mengurangi satu per satu jika saya aneh untuk melihat bahwa saya adalah indeks segi enam horizontal (kolom) dan bagian integer dari j / 2 adalah indeks segi enam vertikal (baris)

masukkan deskripsi gambar di sini

FxIII
sumber
Terima kasih! Sangat membantu, memiliki beberapa masalah yang timbul dengan pemisahan bagian hijau (haruskah saya mengurangi 1 dari saya atau tidak), tetapi saya pikir itu karena orientasi segi enam saya. Semua bekerja, terima kasih!
Joel
14

Heksagon biasa memiliki enam sumbu simetri, tetapi saya akan menganggap heksagon Anda hanya memiliki dua sumbu simetri ( yaitu. Semua sudut tidak persis 60 derajat). Tidak harus karena milik Anda tidak memiliki simetri penuh, tetapi karena mungkin bermanfaat bagi orang lain.

Berikut adalah parameter satu segi enam. Pusatnya adalah di O, lebar terbesar adalah 2a, tingginya 2b, dan panjang tepi atas adalah 2c.

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

Ini adalah tata letak baris / kolom, dengan asal di tengah hexagon kiri bawah. Jika pengaturan Anda berbeda, terjemahkan (x,y)koordinat Anda untuk kembali ke kasus ini, atau gunakan -ysebagai ycontoh:

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

Kode berikut akan memberi Anda baris dan kolom segi enam yang berisi titik (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;
}

Anda dapat memeriksa bahwa kode di atas menggambar hexagon sempurna pada proses IdeOne ini .

sam hocevar
sumber
Pertama kali saya mendengar tentang ideOne, tetapi sepertinya sangat berguna!
David Gouveia
@davidluzgouveia: ya, itu luar biasa. Saya tidak suka layanan web atau cloud tapi yang ini membantu.
sam hocevar
1

Anda bisa memasukkan 3 persegi panjang yang diputar di dalam area segi enam, dan jika dilakukan dengan benar akan mengisi area dengan tepat. Maka itu hanya masalah memeriksa tabrakan pada tiga persegi panjang.

jallan
sumber
1

Anda mungkin tidak perlu membatalkan pendaftaran klik di antara ubin. Dengan kata lain, itu tidak akan sakit dan bahkan mungkin membantu pemain jika Anda membiarkan ruang di antara ubin menjadi klik juga, kecuali jika Anda berbicara tentang ruang besar di antara mereka yang diisi dengan sesuatu yang secara logis tidak seharusnya dilakukan. diklik. (Katakanlah, heksa adalah kota di peta besar di mana di antara keduanya ada hal-hal yang dapat diklik lainnya seperti orang)

Untuk melakukan hal di atas, Anda cukup memplot pusat semua heks, dan kemudian menemukan yang terdekat ke mouse ketika mengklik bidang semua heks. Pusat terdekat pada bidang segi enam tessellated akan selalu sama dengan yang Anda tumpangi.

DampeS8N
sumber
1

Saya sudah menjawab pertanyaan serupa, dengan tujuan yang identik, di atas Stack Overflow saya akan memposting ulang di sini untuk kenyamanan: (NB - semua kode ditulis dan diuji di Jawa)

Kisi heksagonal dengan kisi persegi overlay

Gambar ini menunjukkan sudut kiri atas dari sebuah grid heksagonal dan overlay adalah grid persegi biru. Sangat mudah untuk menemukan yang mana dari kuadrat sebuah titik di dalam dan ini akan memberikan perkiraan kasar yang segi enam juga. Bagian putih dari segi enam menunjukkan di mana kotak persegi dan heksagonal berbagi koordinat yang sama dan bagian abu-abu dari segi enam menunjukkan di mana mereka tidak.

Solusinya sekarang sesederhana menemukan kotak mana titik dalam, kemudian memeriksa untuk melihat apakah titik tersebut di salah satu dari segitiga, dan mengoreksi jawabannya jika perlu.

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

Pada titik ini kita memiliki baris dan kolom kotak tempat kita berada, selanjutnya kita perlu menguji titik kita terhadap dua tepi atas segi enam untuk melihat apakah titik kita terletak pada salah satu dari segi enam di atas:

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

Memiliki koordinat relatif membuat langkah selanjutnya lebih mudah.

Persamaan generik untuk garis lurus

Seperti pada gambar di atas, jika y titik kita adalah > mx + c, kita tahu titik kita terletak di atas garis, dan dalam kasus kita, segi enam di atas dan di sebelah kiri baris dan kolom saat ini. Perhatikan bahwa sistem koordinat di java memiliki y mulai dari 0 di kiri atas layar dan bukan kiri bawah seperti biasa dalam matematika, maka gradien negatif digunakan untuk tepi kiri dan gradien positif digunakan untuk kanan.

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

Penjelasan singkat tentang variabel yang digunakan dalam contoh di atas:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

m adalah gradien, jadi m = c / halfWidth


Selain NeoShamam di atas

Ini merupakan tambahan untuk jawaban SebastianTroy. Saya akan meninggalkannya sebagai komentar tetapi reputasi saya belum cukup.

Jika Anda ingin menerapkan sistem koordinat aksial seperti dijelaskan di sini: http://www.redblobgames.com/grids/hexagons/

Anda dapat membuat sedikit modifikasi pada kode.

Dari pada

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

Gunakan ini

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

Ini akan membuat koordinat (0, 2) berada pada kolom diagonal yang sama dengan (0, 0) dan (0, 1) bukannya langsung di bawah (0, 0).

Troyseph
sumber
Saya menyadari bahwa ini tidak memperhitungkan kesenjangan antara segi enam tetapi harus membantu mempersempit masalah Anda.
Troyseph
0

Jika semua segi enam Anda dibuat menggunakan proporsi dan penempatan yang sama, Anda dapat menggunakan semacam aset overlay untuk tabrakan, sesuatu di sepanjang baris: Hamparan tabrakan untuk segi enam

Kemudian, yang harus Anda lakukan adalah menempatkan gambar tabrakan di tempat segi enam Anda, dapatkan posisi mouse relatif ke sudut kiri, dan lihat apakah piksel posisi relatif TIDAK putih (yang berarti ada tabrakan).

Kode (tidak diuji):

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

Anda jelas dapat melakukan pemeriksaan tabrakan persegi panjang sebelumnya (dari seluruh gambar segi enam Anda) untuk meningkatkan kinerja seluruh proses.

Konsep ini cukup sederhana untuk dipahami dan diimplementasikan, tetapi hanya berfungsi jika segi enam Anda sama. Ini juga bisa berfungsi jika Anda hanya memiliki satu set dimensi segi enam yang memungkinkan, yang kemudian berarti bahwa Anda akan membutuhkan lebih dari satu overlay tabrakan.

Jika menemukan itu menjadi solusi yang sangat sederhana untuk apa yang bisa menjadi jauh lebih lengkap dan dapat digunakan kembali (menggunakan matematika untuk benar-benar menemukan tabrakan) tetapi pasti patut dicoba menurut pendapat saya.

Jesse Emond
sumber
Dengan cara Anda, Anda bisa menggunakan serangkaian bentuk tidak beraturan dan memberi mereka semua overlay dengan warna yang unik, lalu memetakan warna ke objek yang mereka overlay sehingga Anda memilih warna dari buffer overlay Anda dan kemudian menggunakan peta Anda untuk mendapatkan objek di bawah mouse. Bukannya saya sangat mendukung teknik ini, kedengarannya sedikit terlalu rumit dan upaya IMHO optimasi dini.
Troyseph
0

Ada sebuah artikel tentang Pemrograman Game Permata 7 yang disebut Untuk Lebah dan Gamer: Cara Menangani Ubin Heksagonal yang akan persis seperti yang Anda butuhkan.

Sayangnya saya tidak membawa salinan buku itu saat ini, kalau tidak saya bisa menggambarkannya sedikit.

David Gouveia
sumber