Aşamalı İzometrik Harita: Ekrandaki nokta için harita koordinatlarını hesaplayın


12

Bu konuda zaten çok fazla kaynak olduğunu biliyorum, ancak koordinat sistemimle eşleşen bir kaynak bulamadım ve bu çözümlerden herhangi birini ihtiyaçlarıma göre ayarlamakta büyük sorun yaşıyorum. Öğrendiğim şey, bunu yapmanın en iyi yolunun bir dönüşüm matrisi kullanmaktır. Bunu uygulamak sorun değil, ama hangi şekilde koordinat alanını dönüştürmek zorunda olduğumu bilmiyorum.

İşte koordinat sistemimi gösteren bir resim:

resim açıklamasını buraya girin

Ekrandaki bir noktayı bu koordinat sistemine nasıl dönüştürebilirim?



Bunun herhangi bir şekilde nasıl yardımcı olduğunu göremiyorum. Ne demek istediğimi tam olarak anlamadığınızı düşünüyorum.
Chris

Dönüşümü, tam tersini yapar, bu yüzden tersine çevirmeniz gerekir.
Markus von Broady

Yanıtlar:


23

İlk olarak, kod İşte. Bir açıklama gelecektir:

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

Bu kodun sorunuzda gösterilen harita için kutudan çıkmayacağını unutmayın. Bunun nedeni, tek kiremitleri sola kaydırmış olmanızdır, oysa tek kiremit daha çok sağa kaydırılır ( Döşemeli harita düzenleyicide olduğu gibi ). Tek-çini durumunda döndürülen x değerini değiştirerek bunu kolayca çözebilmelisiniz.

açıklama

Bu, bu görevi yerine getirmek için biraz daha kaba kuvvetli bir yöntem gibi görünebilir, ancak en azından piksel mükemmel ve biraz daha esnek olma avantajına sahiptir.

Hile, haritayı tek bir aşamalı ızgara olarak değil, iki ızgara birbirinin üzerine bindirilmiş olarak görüntülemektedir. Tek sıra ızgara ve çift sıra ızgara var, ancak güzel bir diyagram oluşturabilmemiz için onlara kırmızı ve yeşil diyelim ...

İki ızgara, kırmızı ve yeşil

Bu görüntünün sağında mor noktayla bir nokta işaretledim. Orijinal döşeme alanımızda bulmaya çalışacağımız örnek nokta budur.

Dünyadaki herhangi bir nokta hakkında fark edilmesi gereken şey, her zaman tam olarak iki bölgede - kırmızı bir ve yeşil bir - (Bir kenarda değilse, ancak muhtemelen pürüzlü kenar sınırında kırpılacaksınız) yatmasıdır. Bu bölgeleri bulalım ...

İki aday bölge

Şimdi iki bölgeden hangisinin doğru olduğunu seçmek için. Her zaman tam olarak bir cevap olacaktır.

Buradan biraz daha basit bir aritmetik yapabilir ve örnek noktamızdan iki bölgenin her merkez noktasına olan kare mesafesini hesaplayabiliriz. En yakın olan hangisi cevabımız olacak.

Ancak alternatif bir yol var. Her test bölgesi için karolarımızın tam şekliyle eşleşen bir bitmap örnekliyoruz. Bunu tek bir karo için yerel koordinatlara çevrilmiş bir noktada örnekliyoruz. Örneğimiz için şöyle görünecektir:

Nokta örnekleri

Soldaki yeşil bölgeyi kontrol edip bir vuruş (Siyah piksel) alıyoruz. Sağda kırmızı bölgeyi test edip bir özledik (Beyaz piksel). İkinci test elbette gereksizdir çünkü her zaman tam olarak biri veya diğeri olacaktır, asla ikisi de olmayacaktır.

Daha sonra tek kiremitte 1,1'de bir isabet aldığımız sonucuna varıyoruz. Bu koordinat, tek ve çift satırlar için farklı bir dönüşüm kullanarak orijinal döşeme koordinatlarına eşlemek için basit olmalıdır.

Bu yöntem ayrıca piksel testi bitmap'lerinde basit piksel başına özelliklere sahip olmanızı sağlar. Örneğin, beyaz fayans dışı, siyah bir hit, mavi su, kırmızı katı.


2
Bu harika³!
HumanCatfood

Fantastik ve iyi açıklanmış cevap - şimdi sadece koda uygulamak gerekir. Orijinal poster, arkasındaki kod veya Javascript'teki etiket dışında bir dil hakkında herhangi bir bilgi sağlamadığından, A * yanıtı
Tom 'Blue' Piddock

1

Bence yaşadığınız problem koordinat alanınızla ilgili. Fayanslara verdiğiniz koordinatlar gerçekten izometrik bir projeksiyon değildir - xAxis'i aşağıya doğru diyagonal gitmek ve yAxis'i aşağıya sola gitmek (veya bunun bir varyantı) olarak düşünmeniz gerekir.

resimli koordinat eksenleri boyunca hareket ederseniz, kareler elmas olarak sonuçlanacak (ve her şey daha karmaşık olacak)

Aradığınız dönüşüm matrisi bu x ve y eksenlerinden oluşturulmuş bir matris. Aşağıdaki hesaplamayı yapmakla aynıdır.

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

Edit: Ben sadece benzer bir soru geldi (ama ben bahsettiğim koordinat sistemi ile) ve birisi burada çok daha kapsamlı bir cevap verdi:

Fare koordinatlarını izometrik dizinlere nasıl dönüştürebilirim?


0

Temel olarak, bir olay dinleyicisi kullanarak pencerenin penceresindeki konumunu almak istersiniz, penceredeki tuval konumunun ofsetini fare konumlarından kaldırmanız gerekir, böylece fare konumu daha sonra tuval ile ilgilidir.

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 için tuval üzerinde olay dinlemeyi nasıl yapacağınızı bileceğinizi varsayacağım, aksi takdirde izometrik oyun tasarımını düşünmeden önce daha fazla JS öğrenmek isteyebilirsiniz: D


Bu biraz fazla basit değil mi? İso kiremit şeklini mükemmel bir şekilde uymasını istiyorum. Bunu doğru anlarsam, sadece dikdörtgen bir alanı kontrol ediyorsunuz.
Chris

izotilin "formu" ile ne demek istediniz ... mesela fareniz 0: 1 elmas şeklindeki sınırlar içindedir, fareniz bu sınırdan çıkıncaya kadar dönüş 0: 1 olacaktır. Fayanslarınızın boyutu değişirse - yöntemim işe yaramaz. Sağlanan işlevi kullandığım şey ve benim için iyi çalışıyor.
Dave

Sadece merak ediyorum, çünkü diğer tüm çözümler çok daha karmaşık. Fayans elbette aynı boyuttadır, bu yüzden bunu kontrol edeceğim.
Chris

Onunla iyi tamircilik, tuvalin sol ve üst doğru ofsetini aldığınızdan emin olun - ve tuval genişliğini yaptığınızda, matematik iyi çalışacaktır (ayrıca izotil boyut genişliği).
Dave

Benimle aynı koordinat sistemini kullandığınızdan emin misiniz? Hala tamamen kapalı.
Chris

0

Bunu koordinat alanını değiştirerek çözdüm. Şimdi ilk satırda ofset olmadan başlar ve bunun için biraz ayarlayabildiğim bir çalışma örneği buldum.

    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;
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.