ภาพสามมิติ 2D: พิกัดหน้าจอเป็นกระเบื้อง


9

ฉันกำลังเขียนเกม isometric 2D และฉันมีปัญหาในการหาตำแหน่งที่เคอร์เซอร์อยู่ นี่คือภาพวาด:

โดยที่ xs และ ys เป็นพิกัดหน้าจอ (พิกเซล), xt และ yt เป็นพิกัดของกระเบื้อง, W และ H คือความกว้างของกระเบื้องและความสูงของกระเบื้องเป็นพิกเซลตามลำดับ สัญกรณ์สำหรับพิกัดของฉันคือ (y, x) ซึ่งอาจทำให้เกิดความสับสนขออภัยเกี่ยวกับเรื่องนั้น

สิ่งที่ดีที่สุดที่ฉันสามารถคิดได้คือ:

int xtemp = xs / (W / 2);
int ytemp = ys / (H / 2);
int xt = (xs - ys) / 2;
int yt = ytemp + xt;

ดูเหมือนว่าจะเกือบจะถูกต้อง แต่ให้ผลลัพธ์ที่ไม่แน่ชัดแก่ฉันทำให้ยากที่จะเลือกไพ่บางรายการหรือบางครั้งเลือกไพ่ที่อยู่ถัดจากกระเบื้องที่ฉันพยายามคลิก ฉันไม่เข้าใจว่าทำไมและฉันต้องการถ้ามีคนสามารถช่วยให้ฉันเข้าใจตรรกะเบื้องหลังนี้

ขอบคุณ!

คำตอบ:


2

สำหรับการวัดที่แม่นยำเราสามารถพิจารณาสิ่งต่อไปนี้:

ให้พิจารณาวิธีการแปลงพิกัดจากพื้นที่ isometric ก่อนกำหนดโดย i และ j vector (เช่นใน isometricMap [i, j]) หรือเป็น yt และ xt บนหน้าจอเป็นพื้นที่หน้าจอกำหนดโดย x และ y ของหน้าจอ สมมติว่าพื้นที่หน้าจอของคุณถูกจัดตำแหน่งที่จุดกำเนิดพร้อมพื้นที่ว่างภาพสามมิติเพื่อประโยชน์ของความเรียบง่าย

วิธีหนึ่งในการแปลงคือทำการหมุนก่อนจากนั้นปรับสเกลแกน y หรือแกน x เพื่อให้ได้ค่าที่จำเป็นเพื่อให้ตรงกับ yt และ xt ของคุณฉันไม่สามารถมาที่นี่ได้ คุณอาจสร้างเมทริกซ์เพื่อทำสิ่งนี้หรือไม่แล้วใช้เมทริกซ์ย้อนกลับ แต่การดำเนินการย้อนกลับนั้นเป็นสิ่งที่คุณต้องการ

ปรับขนาดค่าในสิ่งที่ตรงกันข้ามแล้วหมุนไปด้านหลังเพื่อรับค่าและปัดเศษลง

มีวิธีอื่นในการคาดเดานี้ แต่ดูเหมือนว่าเหมาะสมที่สุดสำหรับฉันในตอนนี้


โอ๊ะ ฉันได้แก้ไขโพสต์นี้หลายครั้งแล้วและฉันคิดว่าฉันไม่สามารถเข้าใจประเด็นนี้ได้อย่างเรียบร้อยเหมือนที่ฉันต้องการ ฉันต้องการนอน.
Toni

1
ขอบคุณเมทริกซ์เป็นทางออกที่ดีที่สุดอย่างแน่นอนที่นี่ ฉันมีอะไรบางอย่างเกือบจะทำงานแล้ว!
Asik

4

ฉันมีปัญหาเดียวกันนี้สำหรับเกมที่ฉันกำลังเขียน ฉันคิดว่าปัญหานี้จะแตกต่างกันไปตามวิธีการที่คุณนำระบบ isometric ของคุณไปใช้จริง แต่ฉันจะอธิบายว่าฉันแก้ปัญหาได้อย่างไร

ฉันเริ่มแรกด้วยฟังก์ชั่น tile_to_screen ของฉัน (ฉันคิดว่านั่นเป็นวิธีที่คุณวางกระเบื้องในตำแหน่งที่ถูกต้องตั้งแต่แรก) ฟังก์ชั่นนี้มีสมการในการคำนวณ screen_x และ screen_y ฉันดูเหมือนว่านี้ (หลาม):

def map_to_screen(self, point):
    x = (SCREEN_WIDTH + (point.y - point.x) * TILE_WIDTH) / 2
    y = (SCREEN_HEIGHT + (point.y + point.x) * TILE_HEIGHT) / 2
    return (x, y)

ฉันเอาสมการสองตัวนั้นมาทำให้เป็นระบบของสมการเชิงเส้น แก้ระบบสมการนี้ในวิธีใดก็ได้ที่คุณเลือก (ฉันใช้วิธี rref นอกจากนี้เครื่องคิดเลขกราฟบางตัวสามารถแก้ปัญหานี้ได้)

สมการสุดท้ายดูเหมือนว่า:

# constants for quick calculating (only process once)
DOUBLED_TILE_AREA = 2 * TILE_HEIGHT * TILE_WIDTH
S2M_CONST_X = -SCREEN_HEIGHT * TILE_WIDTH + SCREEN_WIDTH * TILE_HEIGHT
S2M_CONST_Y = -SCREEN_HEIGHT * TILE_WIDTH - SCREEN_WIDTH * TILE_HEIGHT

def screen_to_map(self, point):
    # the "+ TILE_HEIGHT/2" adjusts for the render offset since I
    # anchor my sprites from the center of the tile
    point = (point.x * TILE_HEIGHT, (point.y + TILE_HEIGHT/2) * TILE_WIDTH)
    x = (2 * (point.y - point.x) + self.S2M_CONST_X) / self.DOUBLED_TILE_AREA
    y = (2 * (point.x + point.y) + self.S2M_CONST_Y) / self.DOUBLED_TILE_AREA
    return (x, y)

อย่างที่คุณเห็นมันไม่ง่ายเหมือนสมการเริ่มต้น แต่มันทำงานได้ดีสำหรับเกมที่ฉันสร้างขึ้น ขอบคุณพระเจ้าสำหรับพีชคณิตเชิงเส้น!

ปรับปรุง

หลังจากเขียนคลาสพอยต์ง่าย ๆ กับโอเปอเรเตอร์ที่หลากหลายฉันทำให้คำตอบนี้ง่ายขึ้น:

# constants for quickly calculating screen_to_iso
TILE_AREA = TILE_HEIGHT * TILE_WIDTH
S2I_CONST_X = -SCREEN_CENTER.y * TILE_WIDTH + SCREEN_CENTER.x * TILE_HEIGHT
S2I_CONST_Y = -SCREEN_CENTER.y * TILE_WIDTH - SCREEN_CENTER.x * TILE_HEIGHT

def screen_to_iso(p):
    ''' Converts a screen point (px) into a level point (tile) '''
    # the "y + TILE_HEIGHT/2" is because we anchor tiles by center, not bottom
    p = Point(p.x * TILE_HEIGHT, (p.y + TILE_HEIGHT/2) * TILE_WIDTH)
    return Point(int((p.y - p.x + S2I_CONST_X) / TILE_AREA),
                 int((p.y + p.x + S2I_CONST_Y) / TILE_AREA))

def iso_to_screen(p):
    ''' Converts a level point (tile) into a screen point (px) '''
    return SCREEN_CENTER + Point((p.y - p.x) * TILE_WIDTH / 2,
                                 (p.y + p.x) * TILE_HEIGHT / 2)

ใช่ระบบสมการเชิงเส้นสองแบบควรทำงานเช่นกัน เมื่อพิจารณาว่าเรามีเวกเตอร์สองตัวที่ไม่ขนานกันคุณควรจะสามารถหาจุดใด ๆ บนเครื่องบินโดยใช้เวกเตอร์หน่วยของ yt และ xt แม้ว่าฉันคิดว่าการใช้งานของคุณจะดูแคบลงและฉันจะไม่รบกวนการตรวจสอบ
Toni

2

คุณกำลังใช้ระบบพิกัดที่ดี สิ่งต่าง ๆ มีความซับซ้อนมากขึ้นถ้าคุณใช้คอลัมน์ที่ถูกเซ

วิธีหนึ่งที่จะคิดเกี่ยวกับปัญหานี้คือคุณมีฟังก์ชั่นที่จะเปลี่ยน (xt, yt) เป็น (xs, ys) map_to_screenฉันจะทำตามคำตอบโขดหินและเรียกมันว่า

คุณต้องการผกผันของฟังก์ชันนี้ screen_to_mapเราสามารถเรียกมันว่า ฟังก์ชั่นผกผันมีคุณสมบัติเหล่านี้:

map_to_screen(screen_to_map(xs, ys)) == (xs, ys)
screen_to_map(map_to_screen(xt, yt)) == (xt, yt)

ทั้งสองเป็นสิ่งที่ดีในการทดสอบหน่วยเมื่อคุณเขียนทั้งสองฟังก์ชั่น คุณจะเขียนอินเวอร์สได้อย่างไร? ไม่ใช่ทุกฟังก์ชั่นที่มี inverses แต่ในกรณีนี้:

  1. หากคุณเขียนเป็นการหมุนตามด้วยการแปลแล้วการผกผันคือการแปลแบบผกผัน (ลบ dx, dy) ตามด้วยการหมุนผกผัน (มุมลบ)
  2. ถ้าคุณเขียนมันเป็นเมทริกซ์คูณแล้วอินเวอร์สคือเมทริกซ์อินเวอร์สคูณ
  3. หากคุณเขียนเป็นสมการพีชคณิตที่กำหนด (xs, ys) ในรูปของ (xt, yt) แล้วอินเวอร์สจะถูกพบโดยการแก้สมการเหล่านั้นสำหรับ (xt, yt) ที่ให้ (xs, ys)

ตรวจสอบให้แน่ใจว่าได้ทดสอบฟังก์ชั่นอินเวอร์สดั้งเดิม + กลับคำตอบที่คุณเริ่มต้นด้วย โขดหินของทั้งสองผ่านการทดสอบถ้าคุณทำ+ TILE_HEIGHT/2ออฟเซตเรนเดอร์ เมื่อฉันแก้ไขพีชคณิตฉันก็มาด้วย:

x = (2*xs - SCREEN_WIDTH) / TILE_WIDTH
y = (2*ys - SCREEN_HEIGHT) / TILE_HEIGHT
yt =  (y + x) / 2
xt =  (y - x) / 2

screen_to_mapซึ่งผมเชื่อว่าเป็นเช่นเดียวกับโขดหินของ

ฟังก์ชั่นนี้จะเปลี่ยนพิกัดของเมาส์ให้เป็นแบบลอย ใช้floorในการแปลงให้เป็นพิกัดกระเบื้องแบบเต็ม


1
ขอบคุณ! ฉันลงเอยด้วยการใช้เมทริกซ์การแปลงดังนั้นการเขียนค่าอินเวอร์สจึงเป็นเรื่องเล็กน้อยนั่นคือมันเป็นแค่เมทริกซ์อินเวอร์ส () ยิ่งไปกว่านั้นมันจะนำไปสู่รูปแบบการประกาศที่มากขึ้นของการเข้ารหัส (Matrix.Translate () * Matrix.Scale () * Matrix.Rotate () มากกว่าการเขียนสมการ) อาจจะช้ากว่าเล็กน้อย แต่ก็ไม่ควรเป็นปัญหา
Asik
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.