2D izometrik: ekrandan döşemeye koordinatları


9

İzometrik bir 2D oyun yazıyorum ve imlecin hangi döşemede olduğunu bulmakta zorlanıyorum. İşte bir çizim:

burada xs ve ys ekran koordinatlarıdır (piksel), xt ve yt döşeme koordinatlarıdır, W ve H sırasıyla döşeme genişliği ve döşeme yüksekliği pikseldir. Koordinatlarla ilgili gösterimlerim (y, x) kafa karıştırıcı olabilir, bunun için üzgünüm.

Şimdiye kadar anlayabildiğim en iyi şey şudur:

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

Bu neredeyse doğru görünüyor ama bana çok kesin olmayan bir sonuç veriyor, belirli karoları seçmeyi zorlaştırıyor veya bazen tıklamaya çalıştığımın yanındaki bir kutucuğu seçiyor. Nedenini anlamıyorum ve birisi bunun arkasındaki mantığı anlamama yardımcı olabilir mi?

Teşekkürler!

Yanıtlar:


2

Doğru ölçüm için aşağıdakileri düşünebiliriz:

Öncelikle i ve j vektörleri tarafından belirlenen izometrik uzaydan (izometrik Harita [i, j] 'da olduğu gibi) veya ekrandaki yt ve xt olarak, ekranın x ve y ile belirlenen ekran alanına nasıl dönüştürüleceğini ele alalım. Basitçe hatırlamak için ekran alanınızın başlangıçta izometrik alanla hizalandığını varsayalım.

Dönüşümü yapmanın bir yolu, önce bir dönüş yapmak, ardından y veya x eksenini ölçeklendirmektir. Yt ve xt ile eşleştirmek için gerekli değerleri elde etmek için burada tam olarak gelemiyorum. Bunu yapmak ya da yapmak için bir matris oluşturabilir ve sonra ters matris kullanabilirsiniz, ancak ters işlem temelde istediğiniz şeydir.

Değeri tersine ölçeklendirin ve ardından değerleri almak ve aşağı doğru yuvarlamak için geriye doğru döndürün.

Sanırım bunun başka yolları da var, ama şu anda bu benim için çok uygun görünüyor.


argh. Ben bu yazıyı birçok kez gözden geçiriyorum ve ben oldukça neyse istediğim gibi düzgün benim noktasında alamıyorum düşünüyorum. Uykuya ihtiyacım var.
Toni

1
Teşekkürler, matrisler kesinlikle burada en iyi çözümdür. Neredeyse çalışan bir şeyim var!
Asik

4

Yazdığım bir oyun için de aynı sorunu yaşadım. Bu sorunun sizi tam olarak izometrik sisteminizi nasıl uyguladığınıza bağlı olarak değişeceğini hayal ediyorum, ancak sorunu nasıl çözdüğümü açıklayacağım.

İlk olarak tile_to_screen işlevimle başladım. (Döşemeleri ilk etapta doğru konuma yerleştirdiğinizi varsayalım.) Bu fonksiyonun screen_x ve screen_y hesaplamak için bir denklemi vardır. Benimki şöyle görünüyordu (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)

Bu iki denklemi aldım ve onları doğrusal denklemler sistemi haline getirdim. Bu denklem sistemini seçtiğiniz herhangi bir yöntemle çözün. (Bir rref yöntemi kullandım. Ayrıca, bazı grafik hesap makineleri bu sorunu çözebilir.)

Son denklemler şöyle görünüyordu:

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

Gördüğünüz gibi, ilk denklem gibi basit değil. Ama yarattığım oyun için iyi çalışıyor. Doğrusal cebir için çok şükür!

Güncelleme

Çeşitli operatörlerle basit bir Point sınıfı yazdıktan sonra, bu cevabı aşağıdakilere basitleştirdim:

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

Evet, iki doğrusal denklem sistemi de çalışmalıdır. Paralel olmayan iki vektörümüz olduğu düşünüldüğünde, yt ve xt birim vektörlerini kullanarak düzlemde herhangi bir nokta elde edebilmelisiniz. Uygulamanızın biraz daralmış olduğunu düşünmeme rağmen ve bunu doğrulamak için uğraşmayacağım.
Toni

2

İyi bir koordinat sistemi kullanıyorsunuz. Aşamalı sütunlar kullanırsanız işler çok daha karmaşık hale gelir.

Bu sorunu düşünmenin bir yolu (xt, yt) 'yi (xs, ys)' ye çevirecek bir işleve sahip olmanızdır. Thane'nin cevabını takip edip onu arayacağım map_to_screen.

Bu işlevin tersini istiyorsunuz . Biz diyebiliriz screen_to_map. Fonksiyon tersleri şu özelliklere sahiptir:

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

Bu iki işlev, her iki işlevi de yazdıktan sonra birim testi yapmak için iyi şeylerdir. Tersi nasıl yazıyorsunuz? Tüm işlevlerin tersi yoktur, ancak bu durumda:

  1. Bir rotasyon ve ardından bir çeviri olarak yazdıysanız, tersi ters çevirmedir (negatif dx, dy), ardından ters rotasyon (negatif açı).
  2. Bir matris çarpımı olarak yazdıysanız, tersi matris ters çarpımıdır.
  3. Eğer bunu (xt, yt) cinsinden (xs, ys) tanımlayan cebirsel denklemler olarak yazdıysanız, o zaman (xt, yt) için verilen denklemleri (xs, ys) çözerek ters bulunur.

Ters + orijinal işlevinin başladığınız yanıtı geri verdiğini test ettiğinizden emin olun. + TILE_HEIGHT/2Render ofsetini çıkarırsanız Thane her iki testi de geçer . Cebiri çözdüğümde:

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

ki Thane ile aynı olduğuna inanıyorum screen_to_map.

Fonksiyon fare koordinatlarını yüzer hale getirir; floortamsayı döşemesi koordinatlarına dönüştürmek için kullanın .


1
Teşekkürler! Ben bir dönüşüm matrisi kullanarak sona erdi, böylece ters yazma önemsiz, yani sadece Matrix.Invert (). Ayrıca daha bildirimsel bir kodlama stiline yol açar (bir grup denklem yerine Matrix.Translate () * Matrix.Scale () * Matrix.Rotate ()). Belki de biraz daha yavaş, ama bu bir sorun olmamalı.
Asik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.