ฉันจะใช้การหยิบไพ่แบบหกเหลี่ยมใน XNA ได้อย่างไร


13

ฉันมีแผนที่เรียงต่อหกเหลี่ยมซึ่งฉันต้องตรวจสอบเมื่อมีการคลิกรูปหกเหลี่ยม รูปหกเหลี่ยมไม่ได้สัมผัสจริง ๆ แต่มีช่องว่างเล็กน้อยระหว่างแต่ละจุด

ไม่มีใครรู้ว่าฉันจะไปเกี่ยวกับการตรวจสอบว่ามีการคลิกหกเหลี่ยมโดยไม่ซับซ้อนเกินสิ่งทั้งหมดหรือไม่

คำตอบ:


13

ลองดูภาพนี้

การสลายตัวหกเหลี่ยม

ดังที่คุณเห็นว่ามีวิธีที่ใช้งานง่ายในการทำแผนที่ x ระบบพิกัดรูปสี่เหลี่ยมผืนผ้า y กับรูปหกเหลี่ยม

เราอาจพูดถึง "rect" hexagons ที่ผิดปกติเช่น hexagons ที่ถูกจารึกไว้ในรูปไข่หรือ hexagons ที่ได้จาก hexagons ปกติที่ปรับสเกลในทั้งสองทิศทางไม่สมส่วน

สี่เหลี่ยมหกเหลี่ยมสามารถกำหนดได้โดยความสูงและความกว้างของสี่เหลี่ยมที่มีเส้นรอบวงรวมกับความกว้างของสี่เหลี่ยมที่จารึก (W, W, เอช)

จารึก / เส้นรอบวงสี่เหลี่ยม

วิธีที่ง่ายที่สุดในการค้นหาดัชนีหกเหลี่ยมคือการแบ่งพื้นที่ดังต่อไปนี้:

พาร์ทิชันพื้นที่

ความกว้างของรูปสี่เหลี่ยมผืนผ้าคือ w + (W - w) / 2 = (w + W) / 2 ความสูงของมันคือ h / 2; ความกว้างของสี่เหลี่ยมสีเขียวคือ (Ww) / 2 ง่ายต่อการค้นหาจุดที่สี่เหลี่ยมตรงจุด:

ป้อนคำอธิบายรูปภาพที่นี่

u และ v เป็นพิกัดเตือนความจำที่ระบุว่าจุดนั้นอยู่ที่ไหน i, j rectangle: การใช้ w เราสามารถพูดได้ว่าถ้าเราอยู่ในพื้นที่สีเขียว (u <(Ww) / 2) หรือไม่

หากเป็นกรณีที่เราอยู่ในพื้นที่สีเขียวเราจำเป็นต้องรู้ว่าเราอยู่ในครึ่งบนหรือล่างของรูปหกเหลี่ยม: เราอยู่ในครึ่งบนถ้า i และ j มีทั้งคู่หรือทั้งคู่ เราอยู่ในครึ่งล่างเป็นอย่างอื่น

ในทั้งสองกรณีมันมีประโยชน์ในการ trasform u และ v ดังนั้นจึงมีความแตกต่างระหว่าง 0 และ 1:

ป้อนคำอธิบายรูปภาพที่นี่

ถ้าเราอยู่ในครึ่งล่างและ v <u

หรือ

ถ้าเราอยู่ในครึ่งบนและ (1-v)> u

ถ้าอย่างนั้นเราก็ลดทอน i ทีละอัน

ทีนี้เราก็ต้องลด j ไปทีละทีถ้าผมแปลกที่เห็นว่า i คือดัชนีหกเหลี่ยมแนวนอน (คอลัมน์) และส่วนจำนวนเต็มของ j / 2 คือดัชนีหกเหลี่ยมแนวตั้ง (แถว)

ป้อนคำอธิบายรูปภาพที่นี่


ขอบคุณ! มีประโยชน์จริง ๆ มีปัญหาเล็กน้อยที่เกิดขึ้นจากการแยกส่วนสีเขียว (ฉันควรลบ 1 จากฉันหรือไม่) แต่ฉันคิดว่านั่นเป็นเพราะการวางแนวของรูปหกเหลี่ยมของฉัน ทำงานได้ดีขอบคุณ!
Joel

14

หกเหลี่ยมปกติมีหกแกนสมมาตร แต่ผมจะสมมติหกเหลี่ยมของคุณมีเพียงสองแกนสมมาตร ( ie.ทุกมุมจะไม่ตรง 60 องศา) ไม่จำเป็นต้องเป็นเพราะคุณไม่มีความสมมาตรเต็มรูปแบบ แต่เนื่องจากอาจเป็นประโยชน์กับคนอื่น

นี่คือพารามิเตอร์ของรูปหกเหลี่ยมหนึ่งอัน ศูนย์ที่อยู่ในOความกว้างที่ใหญ่ที่สุดคือ2aความสูงเป็นและความยาวของขอบด้านบนเป็น2b2c

         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: ใช่มันยอดเยี่ยม ฉันไม่ชอบเว็บหรือบริการคลาวด์ แต่อันนี้มีประโยชน์
sam hocevar

1

คุณสามารถใส่สี่เหลี่ยมหมุนได้ 3 อันภายในพื้นที่ของรูปหกเหลี่ยมและถ้าทำอย่างถูกต้องมันจะเติมเต็มพื้นที่อย่างแน่นอน จากนั้นมันจะเป็นเพียงเรื่องของการตรวจสอบการชนกันของรูปสามเหลี่ยมทั้งสาม


1

คุณอาจไม่จำเป็นต้องยกเลิกการลงทะเบียนการคลิกระหว่างไทล์ กล่าวคือมันจะไม่เจ็บและอาจช่วยผู้เล่นได้หากคุณอนุญาตให้ช่องว่างระหว่างไทล์สามารถคลิกได้เช่นกันเว้นแต่ว่าคุณกำลังพูดถึงช่องว่างขนาดใหญ่ระหว่างกระเบื้องที่เต็มไปด้วยบางสิ่งที่ไม่ควรมีเหตุผล ถูกคลิก (บอกว่าฐานสิบหกเป็นเมืองบนแผนที่ขนาดใหญ่ซึ่งอยู่ระหว่างกลางเป็นสิ่งที่สามารถคลิกได้เช่นเดียวกับผู้คน)

ในการทำสิ่งข้างต้นคุณสามารถพล็อตจุดศูนย์กลางของ hexes ทั้งหมดแล้วหาจุดที่ใกล้ที่สุดกับเมาส์เมื่อคลิกบนระนาบของ hexes ทั้งหมด จุดศูนย์กลางที่ใกล้ที่สุดบนระนาบของรูปหกเหลี่ยม tessellated จะเป็นรูปแบบเดียวกับที่คุณลอยอยู่เหนือ


1

ฉันได้ตอบคำถามที่คล้ายกันโดยมีเป้าหมายที่เหมือนกันแล้วใน Stack Overflowฉันจะโพสต์ที่นี่อีกครั้งเพื่อความมั่นใจ: (NB - รหัสทั้งหมดเขียนและทดสอบใน 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 's นอกเหนือจากข้างต้น

นี่คือภาคผนวกของคำตอบของ 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

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

มีบทความเกี่ยวกับGame Programming Gems 7 ที่เรียกว่าFor Bees and Gamers: วิธีจัดการกับไพ่หกเหลี่ยมซึ่งจะเป็นสิ่งที่คุณต้องการ

น่าเสียดายที่ฉันไม่มีสำเนาของหนังสือเล่มนี้กับฉันในตอนนี้ไม่เช่นนั้นฉันอาจอธิบายได้เล็กน้อย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.