2D isometrik: koordinat layar ke ubin

9

Saya sedang menulis sebuah game 2D isometrik dan saya mengalami kesulitan mencari di mana ubin kursornya. Ini gambarnya:

di mana xs dan ys adalah koordinat layar (piksel), xt dan yt adalah koordinat ubin, W dan H masing-masing adalah lebar ubin dan tinggi ubin dalam piksel. Notasi saya untuk koordinat adalah (y, x) yang mungkin membingungkan, maaf soal itu.

Sejauh ini yang terbaik yang bisa saya pikirkan adalah:

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

Ini tampaknya hampir benar tetapi memberi saya hasil yang sangat tidak tepat, membuatnya sulit untuk memilih ubin tertentu, atau kadang-kadang memilih ubin di sebelah yang saya coba klik. Saya tidak mengerti mengapa dan saya ingin jika seseorang dapat membantu saya memahami logika di balik ini.

Terima kasih!

Asik
sumber

Jawaban:

2

Untuk ukuran yang akurat, kami dapat mempertimbangkan hal berikut:

Pertama-tama mari kita pertimbangkan bagaimana mengubah koordinat dari ruang isometrik, yang ditentukan oleh vektor i dan j (seperti dalam isometrikMap [i, j]) atau sebagai yt dan xt pada layar, ke ruang layar, ditentukan oleh x dan y layar. Mari kita asumsikan ruang layar Anda sejajar pada awalnya dengan ruang isometrik demi kesederhanaan.

Salah satu cara untuk melakukan transformasi adalah dengan melakukan rotasi terlebih dahulu, kemudian skala sumbu y atau x. Untuk mendapatkan nilai-nilai yang diperlukan untuk mencocokkan yt dan xt Anda, saya tidak bisa mengemukakannya langsung di sini. Anda dapat membuat matriks untuk melakukan ini atau tidak dan kemudian menggunakan matriks terbalik, tetapi operasi sebaliknya pada dasarnya adalah apa yang Anda inginkan.

Skala nilai secara terbalik dan kemudian putar ke belakang untuk mendapatkan nilai dan bulatkan ke bawah.

Saya kira ada beberapa cara lain untuk hal ini, tetapi ini tampaknya paling tepat untuk saya saat ini.

Toni
sumber
argh. Saya telah merevisi posting ini berkali-kali dan saya pikir saya tidak bisa menyampaikan maksud saya dengan rapi seperti yang saya inginkan. Aku butuh tidur.
Toni
1
Terima kasih, matriks jelas merupakan solusi terbaik di sini. Saya memiliki sesuatu yang hampir berfungsi sekarang!
Asik
4

Saya memiliki masalah yang sama untuk permainan yang saya tulis. Saya membayangkan bahwa masalah ini akan berbeda berdasarkan pada bagaimana tepatnya Anda menerapkan sistem isometrik Anda, tetapi saya akan menjelaskan bagaimana saya memecahkan masalah tersebut.

Saya pertama kali mulai dengan fungsi tile_to_screen saya. (Saya berasumsi itulah bagaimana Anda menempatkan ubin di lokasi yang tepat di tempat pertama.) Fungsi ini memiliki persamaan untuk menghitung screen_x dan screen_y. Milik saya terlihat seperti ini (python):

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)

Saya mengambil dua persamaan itu dan membuatnya menjadi sistem persamaan linear. Selesaikan sistem persamaan ini dalam metode apa pun yang Anda pilih. (Saya menggunakan metode rref. Juga, beberapa kalkulator grafik dapat menyelesaikan masalah ini.)

Persamaan akhir terlihat seperti ini:

# 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)

Seperti yang Anda lihat, ini tidak sederhana seperti persamaan awal. Tapi itu bekerja dengan baik untuk game yang saya buat. Syukurlah untuk aljabar linier!

Memperbarui

Setelah menulis kelas Point sederhana dengan berbagai operator, saya menyederhanakan jawaban ini sebagai berikut:

# 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)
Thane Brimhall
sumber
Ya, sistem dua persamaan linear juga harus berfungsi. Mengingat kita memiliki dua vektor yang tidak paralel, Anda harus bisa mendapatkan titik di pesawat dengan menggunakan vektor satuan yt dan xt. Meskipun saya pikir implementasi Anda agak terbatas dan saya tidak akan repot memvalidasinya.
Toni
2

Anda menggunakan sistem koordinat yang baik. Hal-hal menjadi jauh lebih rumit jika Anda menggunakan kolom terhuyung.

Salah satu cara untuk memikirkan masalah ini adalah Anda memiliki fungsi untuk mengubah (xt, yt) menjadi (xs, ys). Saya akan mengikuti jawaban Thane dan menyebutnya map_to_screen.

Anda ingin kebalikan dari fungsi ini. Kita bisa menyebutnya screen_to_map. Fungsi inverses memiliki properti ini:

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

Keduanya adalah hal yang baik untuk unit test setelah Anda memiliki kedua fungsi tertulis Bagaimana Anda menulis kebalikannya? Tidak semua fungsi memiliki invers tetapi dalam kasus ini:

  1. Jika Anda menulisnya sebagai rotasi diikuti oleh terjemahan, maka invers adalah terjemahan terbalik (negatif dx, dy) diikuti oleh rotasi terbalik (sudut negatif).
  2. Jika Anda menulisnya sebagai matriks perkalian, maka kebalikannya adalah pembalikan matriks.
  3. Jika Anda menulisnya sebagai persamaan aljabar yang mendefinisikan (xs, ys) dalam istilah (xt, yt), maka inversnya ditemukan dengan menyelesaikan persamaan tersebut untuk (xt, yt) diberikan (xs, ys).

Pastikan untuk menguji bahwa fungsi terbalik + asli memberikan jawaban yang Anda mulai. Thane's melewati kedua tes, jika Anda mengambil + TILE_HEIGHT/2render offset. Ketika saya memecahkan aljabar, saya menemukan:

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

yang saya yakini sama dengan milik Thane screen_to_map.

Fungsi ini akan mengubah koordinat mouse menjadi float; gunakan flooruntuk mengubahnya menjadi koordinat ubin integer.

amitp
sumber
1
Terima kasih! Saya akhirnya menggunakan matriks transformasi, sehingga menulis kebalikannya sepele, yaitu hanya Matrix.Invert (). Plus itu mengarah ke gaya pengkodean yang lebih deklaratif (Matrix.Translate () * Matrix.Scale () * Matrix.Rotate () daripada sekelompok persamaan). Mungkin sedikit lebih lambat, tapi itu seharusnya tidak menjadi masalah.
Asik