Przesunięta mapa izometryczna: Oblicz współrzędne mapy dla punktu na ekranie

12

Wiem, że jest już dużo zasobów na ten temat, ale nie znalazłem takiego, który pasowałby do mojego układu współrzędnych i mam ogromne problemy z dostosowaniem któregokolwiek z tych rozwiązań do moich potrzeb. Nauczyłem się, że najlepszym sposobem na to jest użycie matrycy transformacji. Wdrażanie nie stanowi problemu, ale nie wiem, w jaki sposób muszę przekształcić przestrzeń współrzędnych.

Oto obraz, który pokazuje mój układ współrzędnych:

wprowadź opis zdjęcia tutaj

Jak przekształcić punkt na ekranie w ten układ współrzędnych?

Chris
źródło
Sprawdź to: jsfiddle.net/Sd4ZP/18
Markus von Broady,
Nie widzę, w jaki sposób jest to pomocne w jakikolwiek sposób. Myślę, że nie do końca rozumiesz, co mam na myśli.
Chris
Dokonuje transformacji, odwrotnie, więc musisz ją odwrócić.
Markus von Broady,

Odpowiedzi:

23

Po pierwsze, oto kod. Wyjaśnienie nastąpi:

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

Pamiętaj, że ten kod nie będzie działać po wyjęciu z pudełka mapy pokazanej w pytaniu. Dzieje się tak, ponieważ nieparzyste kafelki są przesunięte w lewo, podczas gdy nieparzyste kafelki są zwykle przesunięte w prawo (tak jak ma to miejsce w edytorze map kafelkowych ). Powinieneś być w stanie łatwo temu zaradzić, modyfikując wartość x zwróconą w przypadku nieparzystych kafelków.

Wyjaśnienie

Może się to wydawać nieco bardziej brutalną metodą realizacji tego zadania, ale ma tę zaletę, że jest idealna pod względem piksela i nieco bardziej elastyczna.

Sztuczka polega na tym, aby oglądać mapę nie jako pojedynczą naprzemienną siatkę, ale jako dwie siatki nakładające się na siebie. Istnieje siatka nieparzystych wierszy i siatka nieparzystych wierszy, ale nazwijmy je czerwonymi i zielonymi, abyśmy mogli stworzyć ładny diagram ...

Dwie siatki, czerwona i zielona

Zauważ, że po prawej stronie tego obrazu zaznaczyłem punkt fioletową kropką. Jest to przykładowy punkt, który spróbujemy znaleźć w naszym oryginalnym polu kafelków.

Należy zwrócić uwagę na każdy punkt na świecie, że zawsze będzie on leżeć dokładnie w dwóch regionach - czerwonym i zielonym (chyba że jest na krawędzi, ale prawdopodobnie i tak przycinamy w granicach poszarpanej krawędzi). Znajdźmy te regiony ...

Dwa regiony kandydujące

Teraz wybierz, który z dwóch regionów jest właściwy. Zawsze będzie dokładnie jedna odpowiedź.

Stąd możemy wykonać prostszą arytmetykę i obliczyć kwadratową odległość od naszego punktu próbnego do każdego punktu środkowego dwóch regionów. Cokolwiek będzie najbliższe, będzie nasza odpowiedź.

Istnieje jednak alternatywny sposób. Dla każdego regionu testowego próbkujemy mapę bitową, która dokładnie odpowiada kształtowi naszych płytek. Próbkujemy to w punkcie przetłumaczonym na lokalne współrzędne dla tego pojedynczego kafelka. W naszym przykładzie wyglądałoby to mniej więcej tak:

Próbki punktowe

Po lewej stronie sprawdzamy zielony region i otrzymujemy trafienie (Czarny piksel). Po prawej testujemy czerwony region i otrzymujemy brak (biały piksel). Drugi test jest oczywiście zbędny, ponieważ zawsze będzie dokładnie jeden lub drugi, nigdy oba.

Następnie dochodzimy do wniosku, że trafiliśmy w nieparzystą płytkę przy 1,1. Ta współrzędna powinna być łatwa do odwzorowania na oryginalne współrzędne kafelków przy użyciu innej transformacji dla nieparzystych i parzystych wierszy.

Ta metoda pozwala również mieć proste właściwości poszczególnych pikseli na bitmapach testowych pikseli. Np. Biały jest pozbawiony kafelków, czarny jest hitem, niebieski to woda, czerwony jest jednolity.

izb
źródło
2
To jest niesamowite³!
HumanCatfood
Fantastyczna i dobrze wyjaśniona odpowiedź - teraz wystarczy zaimplementować ją w kodzie. Ponieważ oryginalny plakat nie zawierał żadnych informacji o kodzie ani języku innym niż tag w JavaScript, odpowiadam A *
Tom „Blue” Piddock
1

Myślę, że masz problem z przestrzenią współrzędnych. Współrzędne, które podałeś kafelkom, nie są tak naprawdę rzutem izometrycznym - musisz myśleć o xAxis jako o przekątnej w dół w prawo, a yAxis jako o przekątnej w dół w lewo (lub o innym wariancie)

teraz, jeśli poruszasz się wzdłuż ilustrowanych osi współrzędnych, będziesz podróżować po przekątnej w „przestrzeni kafelkowej”, aby kwadraty stały się diamentami (i wszystko będzie bardziej złożone)

Matryca transformacji, której szukasz, jest zbudowana z tych osi xiy. Jest to w rzeczywistości to samo, co wykonanie następujących obliczeń.

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

Edycja: Właśnie natrafiłem na podobne pytanie (ale z układem współrzędnych, o którym mówiłem) i ktoś udzielił tutaj o wiele dokładniejszej odpowiedzi:

Jak przekonwertować współrzędne myszy na indeksy izometryczne?

Cholesky
źródło
0

Zasadniczo chcesz uzyskać pozycję myszy w oknie za pomocą detektora zdarzeń, musisz usunąć przesunięcie pozycji kanwy w oknie z pozycji myszy, aby pozycja myszy była wówczas względna względem kanwy.

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

Zakładam, że wiesz, jak wykonać zdarzenie nasłuchujące na płótnie dla ruchu myszy, w przeciwnym razie możesz dowiedzieć się więcej o JS, zanim weźmiesz pod uwagę izometryczny projekt gry: D

Dave
źródło
Czy to nie jest trochę zbyt proste? Chcę, żeby idealnie pasowała do formy płytki ISO. Sprawdzasz tylko prostokątny obszar, jeśli dobrze to rozumiem.
Chris
co rozumiesz przez „formę” izotylu ... na przykład powiedz, że twoja mysz znajduje się w granicach rombu w kształcie 0: 1, powrót wyniesie 0: 1, dopóki mysz nie opuści tej granicy. Jeśli twoje płytki różnią się rozmiarem - moja metoda nie zadziała. Podana funkcja jest tym, z czego korzystam i działa dobrze dla mnie.
Dave
Zastanawiam się, ponieważ wszystkie inne rozwiązania są znacznie bardziej skomplikowane. Płytki są oczywiście tego samego rozmiaru, więc to sprawdzę.
Chris
Majstruj przy nim, upewnij się, że uzyskasz prawidłowe przesunięcie płótna po lewej i na górze - a szerokość płótna, kiedy to zrobisz, matematyka będzie działać dobrze (również szerokość rozmiaru izotylu).
Dave
Czy na pewno używasz tego samego układu współrzędnych co ja? Nadal jest całkowicie wyłączony.
Chris
0

Rozwiązałem to, zmieniając przestrzeń współrzędnych. Teraz zaczyna się bez przesunięcia w pierwszym rzędzie i do tego znalazłem działający przykład, który mogłem trochę dostosować.

    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
źródło