Mappa isometrica sfalsata: calcola le coordinate della mappa per il punto sullo schermo


12

So che ci sono già molte risorse a riguardo, ma non ne ho trovata una che corrisponda al mio sistema di coordinate e sto avendo grossi problemi ad adattare una di queste soluzioni alle mie esigenze. Quello che ho imparato è che il modo migliore per farlo è usare una matrice di trasformazione. L'implementazione non è un problema, ma non so in che modo devo trasformare lo spazio delle coordinate.

Ecco un'immagine che mostra il mio sistema di coordinate:

inserisci qui la descrizione dell'immagine

Come faccio a trasformare un punto sullo schermo in questo sistema di coordinate?



Non riesco a vedere come questo sia utile in alcun modo. Penso che tu non abbia capito bene cosa intendo.
Chris,

Esegue la trasformazione, al contrario, quindi è necessario invertirla.
Markus von Broady,

Risposte:


23

Innanzitutto, ecco il codice. Seguirà una spiegazione:

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

Nota che questo codice non funzionerà per la mappa mostrata nella tua domanda. Questo perché le tessere dispari sono spostate a sinistra, mentre la tessera dispari è più spesso spostata a destra (come nel caso dell'editor di mappe piastrellate ). Dovresti essere in grado di rimediare facilmente modificando il valore x restituito nel caso dispari-tile.

Spiegazione

Questo può sembrare un metodo leggermente più bruto per eseguire questo compito, ma almeno ha il vantaggio di essere pixel perfetto e leggermente più flessibile.

Il trucco sta nel visualizzare la mappa non come un'unica griglia sfalsata, ma come due griglie sovrapposte l'una sull'altra. C'è la griglia delle righe dispari e la griglia delle righe pari, ma chiamiamole invece rosse e verdi in modo da poter creare un bel diagramma ...

Due griglie, rosso e verde

Nota a destra di quell'immagine che ho segnato un punto con un punto viola. Questo è il punto di esempio che cercheremo di trovare nel nostro spazio delle piastrelle originale.

La cosa da notare su qualsiasi punto del mondo è che si troverà sempre esattamente in due regioni: una rossa e una verde (a meno che non sia su un bordo, ma probabilmente taglierai comunque entro il bordo frastagliato). Troviamo quelle regioni ...

Due regioni candidate

Ora seleziona quale delle due regioni è quella corretta. Ci sarà sempre esattamente una risposta.

Da qui potremmo fare un po 'di aritmetica più semplice e calcolare la distanza quadrata dal nostro punto di campionamento a ciascun punto centrale delle due regioni. Qualunque sia il più vicino sarà la nostra risposta.

C'è comunque un modo alternativo. Per ogni area di test, campioniamo una bitmap che corrisponde alla forma esatta delle nostre piastrelle. Lo campioniamo in un punto tradotto in coordinate locali per quella singola tessera. Per il nostro esempio sarebbe simile a questo:

Campioni puntuali

Uno a sinistra controlliamo la regione verde e otteniamo un colpo (pixel nero). Sulla destra testiamo la regione rossa e ci perdiamo (pixel bianco). Il secondo test è ovviamente ridondante poiché sarà sempre esattamente l'uno o l'altro, mai entrambi.

Arriviamo quindi alla conclusione che abbiamo un colpo nella tessera dispari a 1,1. Questa coordinata dovrebbe essere semplice da mappare alle coordinate del riquadro originale usando una trasformazione diversa per le righe pari e dispari.

Questo metodo consente anche di avere semplici proprietà per pixel sulle bitmap di test dei pixel. Ad esempio, il bianco è sfocato, il nero è un successo, il blu è acqua, il rosso è solido.


2
Questo è fantastico³!
HumanCatfood

Risposta fantastica e ben spiegata - ora basta implementarla nel codice. Poiché il poster originale non forniva alcuna informazione sul codice dietro di esso o sulla lingua diversa dal tag in Javascript, dico una risposta A *
Tom 'Blue' Piddock

1

Penso che il problema che stai riscontrando sia con il tuo spazio delle coordinate. Le coordinate che hai dato alle tessere non sono in realtà una proiezione isometrica - Devi pensare all'asse x come diagonale verso il basso a destra e all'asse y come diagonale verso il basso a sinistra (o qualche variante di questo)

in questo momento se ti sposti lungo gli assi delle coordinate illustrati viaggerai in direzione diagonale nello "spazio delle tessere" in modo che i quadrati finiscano come diamanti (e tutto sarà più complesso)

La matrice di trasformazione che stai cercando è quella costruita su quegli assi xey. È effettivamente lo stesso che fare il seguente calcolo.

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

Modifica: ho appena incontrato una domanda simile (ma con il sistema di coordinate di cui stavo parlando) e qualcuno ha dato una risposta molto più completa qui:

Come convertire le coordinate del mouse in indici isometrici?


0

Fondamentalmente si desidera ottenere la posizione del mouse nella finestra utilizzando un listener di eventi, è necessario rimuovere l'offset della posizione della tela nella finestra dalle posizioni del mouse, quindi la posizione del mouse è quindi relativa alla tela.

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

Presumo che tu sappia come ascoltare eventi su tela 'per mousemove, altrimenti potresti voler imparare di più JS prima di prendere in considerazione il design del gioco isometrico: D


Non è un po 'troppo semplice? Voglio che corrisponda perfettamente alla forma della piastrella iso. Stai solo controllando un'area rettangolare se lo capisco correttamente.
Chris,

cosa intendi per "forma" dell'isotilo ... ad esempio, dire che il tuo mouse è all'interno dei limiti a forma di diamante di 0: 1 il ritorno sarà 0: 1 fino a quando il mouse non lascia quel confine. Se le tue piastrelle hanno dimensioni variabili, il mio metodo non funzionerà. La funzione fornita è quella che uso e funziona bene per me.
Dave,

Mi chiedo, perché tutte le altre soluzioni sono molto più complicate. Naturalmente le piastrelle hanno le stesse dimensioni, quindi lo verificherò.
Chris,

Beh, armeggia con esso assicurandoti di ottenere l'offset corretto della tela a sinistra e in alto - e la larghezza della tela, quando lo fai la matematica funzionerà bene (anche la larghezza della dimensione isotile).
Dave,

Sei sicuro di utilizzare il mio stesso sistema di coordinate? È ancora completamente spento.
Chris,

0

Ho risolto questo cambiando lo spazio delle coordinate. Ora inizia senza un offset nella prima riga e per questo ho trovato un esempio funzionante che sono stato in grado di regolare un po '.

    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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.