Come faccio a ordinare gli sprite isometrici nell'ordine corretto?


19

In un normale gioco 2D dall'alto verso il basso si può usare l'asse y dello schermo per ordinare le immagini. In questo esempio gli alberi sono ordinati correttamente ma i muri isometrici non sono:

Immagine di esempio: ordinata per schermo y

Il muro 2 è un pixel sotto il muro 1, quindi viene disegnato dopo il muro 1 e finisce in cima.

Se ordino per l'asse y isometrico le pareti appaiono nell'ordine corretto ma gli alberi non:

Immagine di esempio: ordinata per isometrica y

Come posso farlo correttamente?


3
Sembra che tu abbia colto i problemi comuni in Painter's Algorithm. La soluzione "comune" è usare z-buffer =). Alcune soluzioni alternative includono l'utilizzo del centro oggetti come chiave di ordinamento anziché in alcuni angoli.
Jari Komppa,

Risposte:


12

I giochi isometrici sono funzionalmente 3D, quindi internamente, dovresti memorizzare le coordinate 3D per ogni entità nel gioco. Le coordinate effettive che scegli sono arbitrarie, ma supponiamo che X e Y siano i due assi sul terreno e Z sia sollevato da terra in aria.

Il renderer deve quindi proiettarlo in 2D per disegnare elementi sullo schermo. "Isometrica" ​​è una di queste proiezioni. Proiettare dal 3D al 2D in modo isometrico è piuttosto semplice. Diciamo che l'asse X va da in alto a sinistra a in basso a destra e in basso di un pixel per ogni due pixel orizzontali. Allo stesso modo, l'asse Y va da in alto a destra in basso a sinistra. L'asse Z sale verso l'alto. Per convertire da 3D a 2D, allora è solo:

function projectIso(x, y, z) {
    return {
        x: x - y,
        y: (x / 2) + (y / 2) - z
    };
}

Ora alla tua domanda originale, l'ordinamento. Ora che stiamo lavorando con i nostri oggetti direttamente in 3D, l'ordinamento diventa molto più semplice. Nel nostro spazio di coordinate qui, lo sprite più lontano ha le coordinate x, y e z più basse (cioè tutti e tre gli assi puntano fuori dallo schermo). Quindi basta ordinarli per la somma di quelli:

function nearness(obj) {
    return obj.x + obj.y + obj.z;
}

function closer(a, b) {
    if (nearness(a) > nearness(b)) {
        return "a";
    } else {
        return "b";
    }
}

Per evitare di riordinare le entità in ogni fotogramma, utilizzare un ordinamento per piccione, qui dettagliato .


1
So che questo è vecchio, un buon inizio, ma sai come potrebbe anche incorporare i limiti 3d di un oggetto non solo la sua traduzione. Quando si sovrappongono, l'ordinamento in profondità diventa più complicato.
Rob,

4

Supponendo che i tuoi sprite occupino insiemi di tessere rettangolari (se occupano insiemi arbitrari, quindi non puoi affatto disegnare correttamente nel caso generale), il problema è che non esiste una relazione di ordine totale tra gli elementi, quindi non puoi ordinare usando un ordinamento che comporterebbe confronti O (nlogn).

Si noti che per ogni due oggetti A e B, A deve essere disegnato prima di B (A <- B), B deve essere disegnato prima di A (B <- A) oppure possono essere disegnati in qualsiasi ordine. Formano un ordine parziale. Se ti fai alcuni esempi con 3 oggetti sovrapposti, potresti notare che anche se il 1o e il 3o oggetto non possono sovrapporsi, quindi non avendo una dipendenza diretta, il loro ordine di disegno dipende dal 2o oggetto che è tra loro - a seconda di come lo metti, otterrai diversi ordini di disegno. Bottomline - i tipi tradizionali non funzionano qui.

Una soluzione consiste nell'utilizzare il confronto (menzionato da Dani) e confrontare ciascun oggetto con l'altro per determinare le loro dipendenze e formare un grafico delle dipendenze (che sarà un DAG). Quindi eseguire un ordinamento topologico sul grafico per determinare l'ordine di disegno. Se non ci sono troppi oggetti, questo potrebbe essere abbastanza veloce (è O(n^2)).

Un'altra soluzione è quella di utilizzare un (per bilanciamento - pseudo ) albero quad e memorizzare i rettangoli di tutti gli oggetti al suo interno.

Quindi scorrere tutti gli oggetti X e utilizzare l'albero dei quadratini per verificare se ci sono oggetti Y nella striscia sopra l'oggetto X che inizia con l'estrema sinistra e termina con l'angolo più a destra dell'oggetto X - per tutti questi Y, Y < - X. In questo modo, dovrai comunque formare un grafico e ordinare topologicamente.

Ma puoi evitarlo. Si utilizza un elenco di oggetti Q e una tabella di oggetti T. Si ripetono tutti gli slot visibili da valori più piccoli a più grandi sull'asse x (una riga), procedendo riga per riga sull'asse y. Se esiste un angolo inferiore di un oggetto in quello slot, eseguire la procedura sopra per determinare le dipendenze. Se un oggetto X dipende da qualche altro oggetto Y che è parzialmente sopra di esso (Y <- X), e ogni Y simile è già in Q, aggiungi X a Q. Se c'è qualche Y che non è in Q, aggiungi X a T e denota che Y <- X. Ogni volta che aggiungi un oggetto a Q, rimuovi le dipendenze degli oggetti in sospeso in T. Se tutte le dipendenze vengono rimosse, un oggetto da T viene spostato in Q.

Partiamo dal presupposto che gli sprite degli oggetti non sbirciano dalle loro fessure in basso, a sinistra oa destra (solo in alto, come gli alberi nella tua foto). Ciò dovrebbe migliorare le prestazioni per un gran numero di oggetti. Questo approccio sarà di nuovo O(n^2), ma solo nel peggiore dei casi che include oggetti di strane dimensioni e / o strane configurazioni di oggetti. Nella maggior parte dei casi, lo è O(n * logn * sqrt(n)). Conoscere l'altezza dei tuoi sprite può eliminare il sqrt(n), perché non devi controllare l'intera striscia sopra. A seconda del numero di oggetti sullo schermo, è possibile provare a sostituire il quad tree con un array che indica quali slot sono presi (ha senso se ci sono molti oggetti).

Infine, sentiti libero di ispezionare questo codice sorgente per alcune idee: https://github.com/axel22/sages/blob/master/src/gui/scala/name/brijest/sages/gui/Canvas.scala


2

Non penso che ci sia una soluzione matematica. Probabilmente non hai abbastanza dati nel mondo 2D in cui vivono i tuoi oggetti. Se i tuoi muri fossero specchiati su X sarebbero nell'ordine "corretto". Poi di nuovo potresti essere in grado di fare test di sovrapposizione con il riquadro di delimitazione dell'immagine in qualche modo, ma questa è un'area che non conosco.

Probabilmente dovresti ordinare per schermo Y per riquadro e dire che qualcosa di più complicato è un "problema di progettazione". Ad esempio, se stai creando il contenuto, basta dire ai tuoi progettisti l'algoritmo di ordinamento e spostare wall2 2 pixel in su per risolvere il problema. È così che abbiamo dovuto risolverlo nel gioco isometrico su cui ho lavorato. Ciò potrebbe includere il prendere oggetti "lunghi" e spezzarli in pezzi di dimensioni di una piastrella.

Se si consente agli utenti di modificare il contenuto, la cosa completamente sicura da fare è rendere tutto basato su un riquadro e al massimo un riquadro di grandi dimensioni. In questo modo eviti il ​​problema. Potresti riuscire a cavartela facendo tutto più grande di una tessera, ma forse solo se è quadrata. Non ci ho giocato.


2

L'ordinamento isometrico perfetto è difficile. Ma per un primo approccio, per ordinare correttamente i tuoi oggetti, dovresti usare una funzione di confronto più complessa con ogni oggetto sovrapposto. Questa funzione deve verificare questa condizione: Un oggetto sovrapposto "a" è dietro a "b" se:

(a.posX + a.sizeX <= b.posX) o (a.posY + a.sizeY <= b.posY) o (a.posZ + a.sizeZ <= b.posZ)

Naturalmente, questa è una prima idea per un'implementazione isometrica ingenua. Per uno scenario più complesso (se si desidera la rotazione della vista, le posizioni per pixel sull'asse z, ecc.) È necessario controllare più condizioni.


+1, se hai tessere con larghezze / altezze diverse, guarda la funzione di confronto in questa risposta.
mucaho,

2

Uso una formula molto semplice che funziona bene con il mio piccolo motore isometrico in OpenGL:

Ognuno di voi oggetti (alberi, piastrelle, personaggi, ...) hanno una posizione X e Y sullo schermo. È necessario abilitare TEST DI PROFONDITÀ e trovare il buon valore Z per ciascun valore. Puoi semplicemente:

z = x + y + objectIndex

Uso un indice diverso per il pavimento e gli oggetti che saranno sopra il pavimento (0 per il pavimento e 1 per tutti gli oggetti che hanno un'altezza). Questo dovrebbe funzionare bene.



1

Se si confrontano i valori y nella stessa posizione x, funzionerà ogni volta. Quindi, non confrontare il centro con il centro. Confronta invece il centro di uno sprite con la stessa posizione x sull'altro sprite.

inserisci qui la descrizione dell'immagine

Ma ciò richiede alcuni dati geometrici per ogni sprite. Potrebbe essere facile come due punti dai lati sinistro a destro che descrivono il limite inferiore per lo sprite. In alternativa, è possibile analizzare i dati dell'immagine degli sprite e trovare il primo pixel non trasparente.

Un approccio più semplice è quello di dividere tutti gli sprite in tre gruppi: diagonale lungo l'asse x, diagonale lungo l'asse y e piatta. Se due oggetti sono entrambi diagonali lungo lo stesso asse, ordinarli in base all'altro asse.


0

È necessario assegnare ID univoci a tutti gli oggetti dello stesso tipo. Quindi ordina tutti gli oggetti in base alla loro posizione e disegna gruppi di oggetti in ordine di ID. Quindi quell'oggetto 1 nel gruppo A non supererà mai l'oggetto 2 nel gruppo A ecc.


0

Questa non è davvero una risposta, ma solo un commento e un voto positivo che vorrei dare alla risposta di questo axel22 qui /gamedev//a/8181/112940

Non ho abbastanza reputazione per votare o commentare altre risposte, ma il secondo paragrafo della sua risposta è probabilmente la cosa più importante di cui dovresti essere a conoscenza quando cerchi di ordinare le entità in un gioco isometrico senza fare affidamento sul 3D "moderno" tecniche come un buffer Z.

Nel mio motore volevo fare cose "vecchia scuola", in puro 2D. E ho trascorso molto tempo a farmi impazzire il cervello cercando di capire perché la mia chiamata a "ordinare" (nel mio caso era c ++ std :: sort) non funzionava correttamente su alcune configurazioni di mappe.

Solo quando ho capito che si trattava di una situazione di "ordine parziale" ero in grado di risolverlo.

Finora tutte le soluzioni funzionanti che ho trovato sul web hanno usato una sorta di ordinamento topologico per affrontare correttamente il problema. L'ordinamento topologico sembra essere la strada da percorrere.

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.