Staggered Isometric Map: Hitung koordinat peta untuk titik di layar

12

Saya tahu sudah ada banyak sumber daya tentang ini, tetapi saya belum menemukan satu yang cocok dengan sistem koordinat saya dan saya mengalami kesulitan besar menyesuaikan salah satu solusi tersebut dengan kebutuhan saya. Apa yang saya pelajari adalah bahwa cara terbaik untuk melakukan ini adalah dengan menggunakan matriks transformasi. Menerapkan itu bukan masalah, tapi saya tidak tahu bagaimana saya harus mengubah ruang koordinat.

Berikut gambar yang menunjukkan sistem koordinat saya:

masukkan deskripsi gambar di sini

Bagaimana cara mengubah titik di layar ke sistem koordinat ini?

Chris
sumber
Lihat ini: jsfiddle.net/Sd4ZP/18
Markus von Broady
Saya tidak bisa melihat bagaimana ini membantu dalam hal apa pun. Saya pikir Anda tidak mengerti apa yang saya maksud.
Chris
Itu melakukan transformasi, hanya sebaliknya, jadi Anda perlu membalikkannya.
Markus von Broady

Jawaban:

23

Pertama, ini kodenya. Penjelasan akan mengikuti:

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

Perhatikan bahwa kode ini tidak akan berfungsi di luar kotak untuk peta yang ditunjukkan dalam pertanyaan Anda. Ini karena Anda memiliki ubin aneh diimbangi ke kiri, sedangkan ubin aneh lebih sering diimbangi ke kanan (Seperti halnya di editor peta ubin ). Anda harus dapat dengan mudah memperbaiki ini dengan mengubah nilai x yang dikembalikan dalam kasus odd-tile.

Penjelasan

Ini mungkin tampaknya menjadi metode yang sedikit lebih kasar dalam menyelesaikan tugas ini, tetapi setidaknya memiliki keuntungan menjadi pixel sempurna dan sedikit lebih fleksibel.

Kuncinya adalah dalam melihat peta bukan sebagai satu grid terhuyung-huyung, tetapi sebagai dua grid overlay satu sama lain. Ada grid baris ganjil dan grid genap, tapi mari kita sebut itu merah dan hijau sehingga kita bisa membuat diagram yang cantik ...

Dua kisi, merah dan hijau

Perhatikan di sebelah kanan gambar itu saya telah menandai titik dengan titik ungu. Ini adalah titik contoh yang akan kami coba temukan di ruang ubin asli kami.

Hal yang perlu diperhatikan tentang titik mana pun di dunia adalah bahwa ia akan selalu terletak tepat di dua wilayah - yang merah dan yang hijau (Kecuali jika berada di tepi, tetapi Anda mungkin akan tetap berada di dalam batas tepi bergerigi pula). Ayo cari daerah itu ...

Dua kandidat daerah

Sekarang untuk memilih yang mana dari dua daerah yang benar. Akan selalu ada satu jawaban.

Dari sini kita bisa melakukan aritmatika yang lebih sederhana dan menghitung jarak kuadrat dari titik sampel kami ke setiap titik pusat dari dua wilayah. Apapun yang terdekat adalah jawaban kami.

Namun ada cara alternatif. Untuk setiap wilayah pengujian, kami mencicipi bitmap yang cocok dengan bentuk persis ubin kami. Kami mencicipi pada titik yang diterjemahkan ke dalam koordinat lokal untuk ubin tunggal itu. Sebagai contoh kita akan terlihat seperti ini:

Sampel titik

Di sebelah kiri kita periksa wilayah hijau dan mendapatkan hit (Black pixel). Di sebelah kanan kami menguji wilayah merah dan mendapatkan miss (White pixel). Tes kedua tentu saja berlebihan karena akan selalu tepat satu atau yang lain, tidak pernah keduanya.

Kami kemudian sampai pada kesimpulan bahwa kami memiliki hit di ubin aneh di 1,1. Koordinat ini harus sederhana untuk memetakan ke koordinat ubin asli menggunakan transformasi berbeda untuk baris ganjil dan genap.

Metode ini juga memungkinkan Anda untuk memiliki properti per-piksel sederhana pada bitmap pengujian piksel. Misal putih off-tile, hitam hit, biru air, merah solid.

izb
sumber
2
Ini luar biasa³!
HumanCatfood
Jawaban yang fantastis dan dijelaskan dengan baik - sekarang hanya perlu mengimplementasikannya ke dalam kode. Karena poster asli tidak memberikan informasi tentang kode di belakangnya atau bahasa selain tag dalam Javascript, saya mengatakan jawaban A *
Tom 'Blue' Piddock
1

Saya pikir masalah yang Anda hadapi adalah dengan ruang koordinat Anda. Koordinat yang Anda berikan pada ubin sebenarnya bukan proyeksi isometrik - Anda perlu menganggap xAx sebagai diagonal ke kanan bawah dan yAxis sebagai diagonal ke kiri bawah (atau beberapa varian dari itu)

sekarang jika Anda bergerak di sepanjang sumbu koordinat bergambar Anda akan bepergian ke arah diagonal dalam "ruang ubin" sehingga kotak berakhir sebagai berlian (dan semuanya akan lebih kompleks)

Matriks transformasi yang Anda cari adalah yang dibangun dari sumbu x dan y. Sama efektifnya dengan melakukan perhitungan berikut.

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

Sunting: Saya baru saja menemukan pertanyaan serupa (tetapi dengan sistem koordinat yang saya bicarakan) dan seseorang memberikan jawaban yang jauh lebih menyeluruh di sini:

Bagaimana cara mengubah koordinat mouse ke indeks isometrik?

Cholesky
sumber
0

Pada dasarnya Anda ingin mendapatkan posisi mouse di jendela menggunakan pendengar acara, Anda perlu menghapus offset posisi kanvas di jendela dari posisi mouse, sehingga posisi mouse kemudian relatif ke kanvas.

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

Saya akan menganggap Anda tahu bagaimana melakukan acara mendengarkan di atas kanvas 'untuk ibu, jika tidak, Anda mungkin ingin belajar lebih banyak JS sebelum Anda mempertimbangkan desain game isometrik: D

Dave
sumber
Bukankah ini agak terlalu sederhana? Saya ingin mencocokkan bentuk ubin iso dengan sempurna. Anda hanya memeriksa area persegi panjang jika saya mengerti ini dengan benar.
Chris
apa yang Anda maksud dengan "bentuk" dari isotile ... misalnya katakanlah mouse Anda berada dalam batas-batas berbentuk berlian 0: 1 pengembaliannya akan 0: 1 sampai mouse Anda meninggalkan batas itu. Jika ukuran ubin Anda bervariasi - maka metode saya tidak akan berfungsi. Fungsi yang disediakan adalah apa yang saya gunakan dan berfungsi dengan baik untuk saya.
Dave
Hanya ingin tahu, karena semua solusi lain jauh lebih rumit. Ubin dengan ukuran yang sama tentu saja jadi saya akan memeriksanya.
Chris
Yah bermain-main dengan itu pastikan Anda mendapatkan offset yang benar dari kanvas kiri dan atas - dan lebar kanvas, ketika Anda melakukan matematika akan bekerja dengan baik (juga lebar ukuran isotile).
Dave
Anda yakin menggunakan sistem koordinat yang sama dengan saya? Ini masih benar-benar mati.
Chris
0

Saya memecahkan masalah ini dengan mengubah ruang koordinat. Sekarang mulai tanpa offset di baris pertama dan untuk itu saya menemukan contoh kerja yang saya bisa sesuaikan sedikit.

    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;
Chris
sumber