แผนที่ Isometric Staggered: คำนวณพิกัดของแผนที่สำหรับจุดบนหน้าจอ


12

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

นี่คือภาพที่แสดงระบบพิกัดของฉัน:

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

ฉันจะแปลงจุดบนหน้าจอเป็นระบบพิกัดนี้ได้อย่างไร


ลองดูสิ: jsfiddle.net/Sd4ZP/18
Markus von Broady

ฉันไม่เห็นว่าสิ่งนี้มีประโยชน์ในทางใด ฉันคิดว่าคุณไม่เข้าใจความหมายของฉัน
Chris

มันคือการแปลง, อีกทางหนึ่ง, ดังนั้นคุณต้องย้อนกลับ
Markus von Broady

คำตอบ:


23

ก่อนอื่นนี่คือรหัส คำอธิบายจะเป็นไปตาม:

/*
 * tw, th contain the tile width and height.
 *
 * hitTest contains a single channel taken from a tile-shaped hit-test
 * image. Data was extracted with getImageData()
 */

worldToTilePos = function(x, y) {

    var eventilex = Math.floor(x%tw);
    var eventiley = Math.floor(y%th);

    if (hitTest[eventilex + eventiley * tw] !== 255) {
        /* On even tile */

        return {
            x: Math.floor((x + tw) / tw) - 1,
            y: 2 * (Math.floor((y + th) / th) - 1)
        };
    } else {
        /* On odd tile */

        return {
            x: Math.floor((x + tw / 2) / tw) - 1,
            y: 2 * (Math.floor((y + th / 2) / th)) - 1
        };
    }
};

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

คำอธิบาย

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

เคล็ดลับคือในการดูแผนที่ไม่ใช่เป็นกริดเซเดียว แต่เป็นสองกริดที่ซ้อนทับกัน มีกริดแถวคี่และกริดคู่, แต่ลองเรียกมันว่าสีแดงและเขียวแทนเพื่อเราจะได้สร้างแผนภาพสวย ๆ ...

สองกริด, สีแดงและสีเขียว

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

สิ่งที่สังเกตได้เกี่ยวกับจุดใด ๆ ในโลกก็คือมันจะอยู่ในสองภูมิภาคเสมอ - หนึ่งสีแดงและสีเขียว (เว้นแต่จะอยู่บนขอบ มาหาพื้นที่เหล่านั้น ...

สองภูมิภาคผู้สมัคร

ทีนี้ให้เลือกว่าพื้นที่ใดในสองภูมิภาคที่ถูกต้อง จะมีคำตอบเดียวเสมอ

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

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

ตัวอย่างจุด

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

จากนั้นเราก็มาถึงข้อสรุปที่ว่าเราได้รับความนิยมในไพ่แปลก ๆ ที่ 1,1 พิกัดนี้ควรจะง่ายต่อการแมปไปยังพิกัดของไทล์ดั้งเดิมโดยใช้การแปลงที่แตกต่างกันสำหรับแถวคี่และแถว

วิธีนี้ยังช่วยให้คุณมีคุณสมบัติต่อพิกเซลอย่างง่าย ๆ บนบิตแมปทดสอบพิกเซล เช่นสีขาวเป็นกระเบื้อง, สีดำเป็นที่นิยม, สีฟ้าคือน้ำ, สีแดงเป็นของแข็ง


2
มันเยี่ยมมาก!
HumanCatfood

คำตอบที่ยอดเยี่ยมและอธิบายได้ดี - ตอนนี้ต้องใช้มันเป็นโค้ด เนื่องจากโปสเตอร์ดั้งเดิมไม่ได้ให้ข้อมูลใด ๆ เกี่ยวกับโค้ดด้านหลังหรือภาษาอื่นนอกเหนือจากแท็กใน Javascript ฉันพูดคำตอบ A *
Tom 'Blue' Piddock

1

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

ตอนนี้ถ้าคุณเคลื่อนที่ไปตามแกนพิกัดที่แสดงคุณจะต้องเดินทางไปในทิศทางทแยงมุมใน "ช่องว่างกระเบื้อง" ดังนั้นสี่เหลี่ยมจะจบลงด้วยเพชร (และทุกอย่างจะซับซ้อนกว่า)

เมทริกซ์การแปลงรูปแบบที่คุณต้องการนั้นเป็นหนึ่งในแกน x และ y เหล่านั้น มันมีประสิทธิภาพเหมือนกับการคำนวณต่อไปนี้

screenOffsetXY = screenPointXY - tileOriginXY;
tileX = dot(screenOffsetXY, xAxis) / tileWidth;
tileY = dot(screenOffsetXY, yAxis) / tileWidth;

แก้ไข: ฉันเพิ่งเจอคำถามที่คล้ายกัน (แต่ด้วยระบบพิกัดฉันพูดถึง) และมีคนให้คำตอบอย่างละเอียดมากขึ้นที่นี่:

วิธีแปลงพิกัดของเมาส์เป็นดัชนีภาพสามมิติ?


0

โดยทั่วไปคุณต้องการรับตำแหน่งของเมาส์ในหน้าต่างโดยใช้ฟังเหตุการณ์คุณจำเป็นต้องลบออฟเซ็ตตำแหน่งผ้าใบในหน้าต่างออกจากตำแหน่งเมาส์ดังนั้นตำแหน่งเมาส์จึงสัมพันธ์กับ Canvas

function mouseTwoGridPosition(e){

var mousex = e.pageX; //mouse position x
var mouseY = e.pageY;  //mouse position y
var canvas_width = 1000; //pixels
var offset_left = 100; //offset of canvas to window in pixels
var offset_top = 150; //offset of canvas to window in pixels

var isotile = 64; //if iso tile is 64 by 64

//get mouse position relative to canvas rather than window
var x = mousex - canvas_width/2 - offset_left;
var y = mousey - offset_top;


//convert to isometric grid
var tx = Math.round( (x + y * 2) / isotile) - 1;
var ty = Math.round((y * 2 - x) / isotile) - 1;

 //because your grid starts at 0/0 not 1/1 we subtract 1 
 // this is optional based on what grid number you want to start at



   return [tx,ty];
}

ฉันจะสมมติว่าคุณรู้วิธีการทำเหตุการณ์ฟังบนผืนผ้าใบ 'สำหรับ mousemove มิฉะนั้นคุณอาจต้องการเรียนรู้เพิ่มเติม JS ก่อนที่คุณจะพิจารณาการออกแบบเกมมีมิติเท่ากัน: D


นี่ไม่ใช่เรื่องง่ายเกินไปหรือ? ฉันต้องการให้ตรงกับรูปแบบของกระเบื้อง iso อย่างสมบูรณ์แบบ คุณแค่ตรวจสอบพื้นที่สี่เหลี่ยมถ้าฉันเข้าใจถูกต้อง
Chris

คุณหมายถึงอะไรโดย "รูปแบบ" ของไอโซไทล์ ... เช่นพูดว่าเมาส์ของคุณอยู่ในขอบเขตรูปเพชร 0: 1 ผลตอบแทนจะเป็น 0: 1 จนกระทั่งเมาส์ของคุณออกจากขอบเขตนั้น หากไทล์ของคุณมีขนาดต่างกัน - วิธีการของฉันจะไม่ทำงาน ฟังก์ชั่นที่ให้ไว้คือสิ่งที่ฉันใช้และใช้งานได้ดีสำหรับฉัน
เดฟ

เพียงแค่สงสัยเพราะโซลูชันอื่น ๆ ทั้งหมดนั้นมีความซับซ้อนมากขึ้น กระเบื้องมีขนาดเท่ากันดังนั้นฉันจะลองดู
Chris

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

คุณแน่ใจหรือว่าคุณกำลังใช้ระบบพิกัดเดียวกันกับฉัน มันยังคงปิดอยู่อย่างสมบูรณ์
Chris

0

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

    hWidth = this.tileset.tileSize.width / 2;
    hHeight = this.tileset.tileSize.height / 2;

    pX = point.x - halfWidth;
    pY = point.y - halfHeight;

    x = Math.floor((pX + (pY - hHeight) * 2) / this.tileset.tileSize.width);
    y = Math.floor((pY - (pX - hWidth) * 0.5) / this.tileset.tileSize.height);

    tx = Math.floor((x - y) / 2) + 1 + this.camera.x;
    ty = y + x + 2 + this.camera.y;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.