ฉันมีแผนที่เรียงต่อหกเหลี่ยมซึ่งฉันต้องตรวจสอบเมื่อมีการคลิกรูปหกเหลี่ยม รูปหกเหลี่ยมไม่ได้สัมผัสจริง ๆ แต่มีช่องว่างเล็กน้อยระหว่างแต่ละจุด
ไม่มีใครรู้ว่าฉันจะไปเกี่ยวกับการตรวจสอบว่ามีการคลิกหกเหลี่ยมโดยไม่ซับซ้อนเกินสิ่งทั้งหมดหรือไม่
ฉันมีแผนที่เรียงต่อหกเหลี่ยมซึ่งฉันต้องตรวจสอบเมื่อมีการคลิกรูปหกเหลี่ยม รูปหกเหลี่ยมไม่ได้สัมผัสจริง ๆ แต่มีช่องว่างเล็กน้อยระหว่างแต่ละจุด
ไม่มีใครรู้ว่าฉันจะไปเกี่ยวกับการตรวจสอบว่ามีการคลิกหกเหลี่ยมโดยไม่ซับซ้อนเกินสิ่งทั้งหมดหรือไม่
คำตอบ:
ลองดูภาพนี้
ดังที่คุณเห็นว่ามีวิธีที่ใช้งานง่ายในการทำแผนที่ 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 คือดัชนีหกเหลี่ยมแนวตั้ง (แถว)
หกเหลี่ยมปกติมีหกแกนสมมาตร แต่ผมจะสมมติหกเหลี่ยมของคุณมีเพียงสองแกนสมมาตร ( ie.ทุกมุมจะไม่ตรง 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นี้
คุณสามารถใส่สี่เหลี่ยมหมุนได้ 3 อันภายในพื้นที่ของรูปหกเหลี่ยมและถ้าทำอย่างถูกต้องมันจะเติมเต็มพื้นที่อย่างแน่นอน จากนั้นมันจะเป็นเพียงเรื่องของการตรวจสอบการชนกันของรูปสามเหลี่ยมทั้งสาม
คุณอาจไม่จำเป็นต้องยกเลิกการลงทะเบียนการคลิกระหว่างไทล์ กล่าวคือมันจะไม่เจ็บและอาจช่วยผู้เล่นได้หากคุณอนุญาตให้ช่องว่างระหว่างไทล์สามารถคลิกได้เช่นกันเว้นแต่ว่าคุณกำลังพูดถึงช่องว่างขนาดใหญ่ระหว่างกระเบื้องที่เต็มไปด้วยบางสิ่งที่ไม่ควรมีเหตุผล ถูกคลิก (บอกว่าฐานสิบหกเป็นเมืองบนแผนที่ขนาดใหญ่ซึ่งอยู่ระหว่างกลางเป็นสิ่งที่สามารถคลิกได้เช่นเดียวกับผู้คน)
ในการทำสิ่งข้างต้นคุณสามารถพล็อตจุดศูนย์กลางของ hexes ทั้งหมดแล้วหาจุดที่ใกล้ที่สุดกับเมาส์เมื่อคลิกบนระนาบของ hexes ทั้งหมด จุดศูนย์กลางที่ใกล้ที่สุดบนระนาบของรูปหกเหลี่ยม tessellated จะเป็นรูปแบบเดียวกับที่คุณลอยอยู่เหนือ
ฉันได้ตอบคำถามที่คล้ายกันโดยมีเป้าหมายที่เหมือนกันแล้วใน 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
นี่คือภาคผนวกของคำตอบของ 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)
หากรูปหกเหลี่ยมทั้งหมดของคุณทำโดยใช้สัดส่วนและตำแหน่งที่เท่ากันคุณสามารถใช้เนื้อหาซ้อนทับสำหรับการชนได้บ้างตามแนวของ:
จากนั้นสิ่งที่คุณต้องทำคือวางภาพการชนที่ตำแหน่งหกเหลี่ยมของคุณรับตำแหน่งเมาส์ที่สัมพันธ์กับมุมซ้ายและดูว่าพิกเซลของตำแหน่งสัมพัทธ์ไม่ใช่สีขาว (ซึ่งหมายความว่ามีการชนกัน)
รหัส (ไม่ผ่านการทดสอบ):
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;
}
เห็นได้ชัดว่าคุณสามารถทำการตรวจสอบการชนกันของสี่เหลี่ยมผืนผ้าล่วงหน้า (จากภาพหกเหลี่ยมทั้งหมด) เพื่อปรับปรุงประสิทธิภาพของกระบวนการทั้งหมด
แนวคิดนี้ค่อนข้างง่ายต่อการเข้าใจและนำไปใช้ แต่ใช้ได้ผลถ้ารูปหกเหลี่ยมของคุณเหมือนกันทั้งหมด นอกจากนี้ยังสามารถใช้งานได้หากคุณมีมิติหกเหลี่ยมที่เป็นไปได้ซึ่งหมายความว่าคุณต้องมีการซ้อนทับมากกว่าหนึ่งครั้ง
หากพบว่ามันเป็นทางออกที่ง่ายมากสำหรับสิ่งที่อาจจะสมบูรณ์มากขึ้นและนำกลับมาใช้ใหม่ได้ (ใช้คณิตศาสตร์เพื่อหาการชนจริง ๆ ) แต่มันคุ้มค่าที่จะลองใช้ในความคิดของฉัน
มีบทความเกี่ยวกับGame Programming Gems 7 ที่เรียกว่าFor Bees and Gamers: วิธีจัดการกับไพ่หกเหลี่ยมซึ่งจะเป็นสิ่งที่คุณต้องการ
น่าเสียดายที่ฉันไม่มีสำเนาของหนังสือเล่มนี้กับฉันในตอนนี้ไม่เช่นนั้นฉันอาจอธิบายได้เล็กน้อย