2D isometrico: schermo per affiancare le coordinate


9

Sto scrivendo un gioco isometrico 2D e ho difficoltà a capire con precisione su quale tessera si trova il cursore. Ecco un disegno:

dove xs e ys sono coordinate dello schermo (pixel), xt e yt sono coordinate della piastrella, W e H sono rispettivamente la larghezza e l'altezza della piastrella in pixel. La mia notazione per le coordinate è (y, x) che può essere fonte di confusione, mi dispiace per quello.

Il meglio che ho potuto capire finora è questo:

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

Questo sembra quasi corretto, ma mi sta dando un risultato molto impreciso, rendendo difficile selezionare determinate tessere, o talvolta seleziona una tessera accanto a quella su cui sto cercando di fare clic. Non capisco perché e mi piacerebbe se qualcuno potesse aiutarmi a capire la logica dietro questo.

Grazie!

Risposte:


2

Per una misura accurata, potremmo considerare quanto segue:

Consideriamo innanzitutto come trasformare le coordinate dallo spazio isometrico, determinate dai vettori ie (come in Mappa isometrica [i, j]) o come yt e xt sullo schermo, nello spazio dello schermo, determinato da xey sullo schermo. Supponiamo che lo spazio dello schermo sia allineato all'origine con lo spazio isometrico per semplicità.

Un modo per eseguire la trasformazione è innanzitutto eseguire una rotazione, quindi ridimensionare l'asse y o x. Per ottenere i valori necessari per corrispondere a yt e xt, non riesco proprio a trovare sul posto qui. È possibile creare una matrice per eseguire questa operazione o meno e quindi utilizzare la matrice inversa, ma l'operazione inversa è sostanzialmente ciò che si desidera.

Ridimensionare il valore al contrario, quindi ruotare all'indietro per ottenere i valori e arrotondare verso il basso.

Ci sono altri modi per immaginarlo, ma questo mi sembra più appropriato in questo momento.


Argh. Ho rivisto questo post così tante volte e penso di non riuscire a capire esattamente come vorrei. Ho bisogno di dormire.
Toni,

1
Grazie, le matrici sono sicuramente la migliore soluzione qui. Ho qualcosa che quasi funziona ora!
Asik,

4

Ho avuto lo stesso problema per un gioco che stavo scrivendo. Immagino che questo problema differirà in base a come esattamente hai implementato il tuo sistema isometrico, ma ti spiego come ho risolto il problema.

Ho iniziato con la mia funzione tile_to_screen. (Suppongo sia il modo in cui stai posizionando le tessere nella posizione giusta in primo luogo.) Questa funzione ha un'equazione per calcolare screen_x e screen_y. Il mio sembrava così (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)

Ho preso quelle due equazioni e le ho trasformate in un sistema di equazioni lineari. Risolvi questo sistema di equazioni con qualsiasi metodo tu scelga. (Ho usato un metodo rref. Inoltre, alcuni calcolatori grafici possono risolvere questo problema.)

Le equazioni finali sembravano così:

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

Come puoi vedere, non è semplice come l'equazione iniziale. Ma funziona bene per il gioco che ho creato. Grazie al cielo per l'algebra lineare!

Aggiornare

Dopo aver scritto una semplice classe Point con vari operatori, ho semplificato questa risposta a quanto segue:

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

Sì, dovrebbe funzionare anche un sistema di due equazioni lineari. Considerando che abbiamo due vettori che non sono paralleli, dovresti essere in grado di ottenere qualsiasi punto sul piano usando i vettori unità di yt e xt. Anche se penso che la tua implementazione sia un po 'ristretta e non mi preoccuperò di convalidarla.
Toni,

2

Stai usando un buon sistema di coordinate. Le cose diventano molto più complicate se usi colonne sfalsate.

Un modo di pensare a questo problema è che hai una funzione per trasformare (xt, yt) in (xs, ys). Seguirò la risposta di Thane e la chiamerò map_to_screen.

Vuoi l' inverso di questa funzione. Possiamo chiamarloscreen_to_map . Le funzioni inverse hanno queste proprietà:

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

Queste due cose sono buone per il test unitario dopo aver scritto entrambe le funzioni. Come scrivi l'inverso? Non tutte le funzioni sono inverse, ma in questo caso:

  1. Se lo hai scritto come una rotazione seguita da una traduzione, l'inverso è la traduzione inversa (negativo dx, dy) seguita dalla rotazione inversa (angolo negativo).
  2. Se lo hai scritto come moltiplicatore di matrice, l'inverso è il moltiplicatore inverso di matrice.
  3. Se lo hai scritto come equazioni algebriche che definiscono (xs, ys) in termini di (xt, yt), allora si trova l'inverso risolvendo quelle equazioni per (xt, yt) dato (xs, ys).

Assicurati di verificare che la funzione inversa + originale restituisca la risposta con cui hai iniziato. Thane supera entrambi i test, se si elimina l' + TILE_HEIGHT/2offset di rendering. Quando ho risolto l'algebra, mi sono inventato:

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

che credo sia lo stesso di Thane screen_to_map.

La funzione trasformerà le coordinate del mouse in float; usare floorper convertirli in coordinate di numero intero.


1
Grazie! Ho finito per usare una matrice di trasformazione, quindi scrivere l'inverso è banale, cioè è solo Matrix.Invert (). Inoltre porta a uno stile di codifica più dichiarativo (Matrix.Translate () * Matrix.Scale () * Matrix.Rotate () anziché un gruppo di equazioni). Forse è leggermente più lento, ma non dovrebbe essere un problema.
Asik,
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.