Versetzte isometrische Karte: Berechnen Sie die Kartenkoordinaten für den Punkt auf dem Bildschirm

11

Ich weiß, dass es bereits viele Ressourcen gibt, aber ich habe keine gefunden, die meinem Koordinatensystem entspricht, und ich habe massive Probleme, eine dieser Lösungen an meine Bedürfnisse anzupassen. Was ich gelernt habe ist, dass der beste Weg, dies zu tun, die Verwendung einer Transformationsmatrix ist. Das zu implementieren ist kein Problem, aber ich weiß nicht, auf welche Weise ich den Koordinatenraum transformieren muss.

Hier ist ein Bild, das mein Koordinatensystem zeigt:

Geben Sie hier die Bildbeschreibung ein

Wie transformiere ich einen Punkt auf dem Bildschirm in dieses Koordinatensystem?

Chris
quelle
Überprüfen Sie dies heraus: jsfiddle.net/Sd4ZP/18
Markus von Broady
Ich kann nicht sehen, wie hilfreich dies in irgendeiner Weise ist. Ich glaube, du hast nicht ganz verstanden, was ich meine.
Chris
Es macht die Transformation, genau umgekehrt, also müssen Sie es umkehren.
Markus von Broady

Antworten:

23

Hier ist zunächst der Code. Eine Erklärung folgt:

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

Beachten Sie, dass dieser Code für die in Ihrer Frage gezeigte Karte nicht sofort funktioniert. Dies liegt daran, dass die ungeraden Kacheln nach links versetzt sind, während die ungeraden Kacheln normalerweise nach rechts versetzt sind (wie dies im gekachelten Karteneditor der Fall ist ). Sie sollten in der Lage sein, dies einfach zu beheben, indem Sie den im Fall der ungeraden Kacheln zurückgegebenen x-Wert anpassen.

Erläuterung

Dies scheint eine etwas brutalere Methode zur Erfüllung dieser Aufgabe zu sein, hat jedoch zumindest den Vorteil, dass sie pixelgenau und etwas flexibler ist.

Der Trick besteht darin, die Karte nicht als einzelnes versetztes Gitter anzuzeigen, sondern als zwei übereinanderliegende Gitter. Es gibt das Raster mit ungeraden Zeilen und das Raster mit geraden Zeilen, aber nennen wir sie stattdessen rot und grün, damit wir ein hübsches Diagramm erstellen können ...

Zwei Gitter, rot und grün

Beachten Sie, dass ich rechts von diesem Bild einen Punkt mit einem violetten Punkt markiert habe. Dies ist der Beispielpunkt, den wir versuchen werden, in unserem ursprünglichen Kachelraum zu finden.

An jedem Punkt der Welt ist zu beachten, dass er immer in genau zwei Regionen liegt - einer roten und einer grünen (es sei denn, er befindet sich an einer Kante, aber Sie werden wahrscheinlich trotzdem innerhalb der gezackten Kantengrenze abschneiden). Lassen Sie uns diese Regionen finden ...

Zwei Kandidatenregionen

Wählen Sie nun aus, welche der beiden Regionen die richtige ist. Es wird immer genau eine Antwort geben.

Von hier aus könnten wir eine einfachere Arithmetik durchführen und den quadratischen Abstand von unserem Abtastpunkt zu jedem Mittelpunkt der beiden Regionen berechnen. Was am nächsten ist, wird unsere Antwort sein.

Es gibt jedoch einen alternativen Weg. Für jede Testregion probieren wir eine Bitmap aus, die der genauen Form unserer Kacheln entspricht. Wir probieren es an einem Punkt aus, der in lokale Koordinaten für diese einzelne Kachel übersetzt wurde. In unserem Beispiel würde es ungefähr so ​​aussehen:

Punktproben

Links überprüfen wir den grünen Bereich und erhalten einen Treffer (schwarzes Pixel). Rechts testen wir den roten Bereich und erhalten einen Fehler (weißes Pixel). Der zweite Test ist natürlich überflüssig, da es immer genau der eine oder andere sein wird, niemals beide.

Wir kommen dann zu dem Schluss, dass wir bei 1,1 einen Treffer in der ungeraden Kachel haben. Diese Koordinate sollte einfach auf die ursprünglichen Kachelkoordinaten abzubilden sein, wobei eine andere Transformation für ungerade und gerade Zeilen verwendet wird.

Mit dieser Methode können Sie auch einfache Eigenschaften pro Pixel für die Pixeltest-Bitmap (s) festlegen. ZB Weiß ist Off-Tile, Schwarz ist ein Hit, Blau ist Wasser, Rot ist fest.

izb
quelle
2
Das ist großartig³!
HumanCatfood
Fantastische und gut erklärte Antwort - jetzt muss sie nur noch in Code implementiert werden. Da das Originalplakat keine Informationen über den Code dahinter oder die andere Sprache als das Tag in Javascript enthielt, sage ich eine A * -Antwort
Tom 'Blue' Piddock
1

Ich denke, das Problem, das Sie haben, ist mit Ihrem Koordinatenraum. Die Koordinaten, die Sie den Kacheln gegeben haben, sind nicht wirklich eine isometrische Projektion. Sie müssen sich die xAxis als diagonal nach rechts unten und die yAxis als diagonal nach links unten (oder eine Variante davon) vorstellen.

Wenn Sie sich jetzt entlang der dargestellten Koordinatenachsen bewegen, bewegen Sie sich in diagonaler Richtung im "Kachelraum", sodass die Quadrate als Diamanten enden (und alles komplexer wird).

Die Transformationsmatrix, nach der Sie suchen, besteht aus diesen x- und y-Achsen. Dies entspricht praktisch der folgenden Berechnung.

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

Bearbeiten: Ich bin gerade auf eine ähnliche Frage gestoßen (aber mit dem Koordinatensystem, über das ich gesprochen habe) und jemand hat hier eine viel gründlichere Antwort gegeben:

Wie konvertiere ich Mauskoordinaten in isometrische Indizes?

Cholesky
quelle
0

Wenn Sie die Position der Maus im Fenster mithilfe eines Ereignis-Listeners ermitteln möchten, müssen Sie den Versatz der Canvas-Position im Fenster von den Mauspositionen entfernen, damit die Mausposition dann relativ zur Canvas-Position ist.

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

Ich gehe davon aus, dass Sie wissen, wie man Ereignisse auf Leinwand für Mausbewegungen hört, andernfalls möchten Sie vielleicht mehr über JS erfahren, bevor Sie sich mit isometrischem Spieldesign befassen: D.

Dave
quelle
Ist das nicht ein bisschen zu einfach? Ich möchte, dass es perfekt zur Form der ISO-Kachel passt. Sie überprüfen nur einen rechteckigen Bereich, wenn ich das richtig verstehe.
Chris
Was meinst du mit "Form" des Isotils? Angenommen, deine Maus befindet sich innerhalb der rautenförmigen Grenzen von 0: 1. Die Rückgabe beträgt 0: 1, bis deine Maus diese Grenze verlässt. Wenn Ihre Fliesen unterschiedlich groß sind, funktioniert meine Methode nicht. Die bereitgestellte Funktion ist das, was ich benutze und sie funktioniert gut für mich.
Dave
Ich frage mich nur, denn alle anderen Lösungen sind viel komplizierter. Die Fliesen haben natürlich die gleiche Größe, also werde ich das überprüfen.
Chris
Wenn Sie daran basteln, stellen Sie sicher, dass Sie den richtigen Versatz von Leinwand links und oben erhalten - und die Leinwandbreite, wenn Sie dies tun, funktioniert die Mathematik einwandfrei (auch die Breite der Isotilengröße).
Dave
Sind Sie sicher, dass Sie dasselbe Koordinatensystem wie ich verwenden? Es ist immer noch komplett aus.
Chris
0

Ich habe dies gelöst, indem ich den Koordinatenraum geändert habe. Es beginnt jetzt ohne Versatz in der ersten Zeile und dafür habe ich ein Arbeitsbeispiel gefunden, das ich etwas anpassen konnte.

    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
quelle