Ordinamento della profondità isometrica per piattaforme mobili

Cosa starai creando

L'ordinamento della profondità può essere spiegato in termini semplici come un modo per capire quale elemento è più vicino alla fotocamera e che è più lontano, determinando in tal modo l'ordine in cui devono essere disposti in modo da trasmettere la giusta profondità nella scena.

In questo tutorial, scaveremo più in profondità nell'ordinamento per i livelli isometrici mentre proviamo ad aggiungere piattaforme mobili. Questo non è un tutorial per principianti sulla teoria isometrica e non riguarda il codice. L'obiettivo è capire la logica e la teoria piuttosto che analizzare il codice. Lo strumento di scelta per il tutorial è Unity, e quindi l'ordinamento in profondità essenzialmente sta cambiando il sortingOrder degli sprite coinvolti. Per altri framework, potrebbe essere una modifica dell'ordine z o della sequenza di un ordine di disegno.

Per iniziare la teoria isometrica, fai riferimento a questa serie di tutorial. Il codice e la struttura delle scene seguono il mio precedente tutorial isometrico. Si prega di fare riferimento a questi se si trova il tutorial difficile da seguire in quanto mi concentrerò solo sulla logica in questo tutorial.

1. Livelli senza movimento

Se il tuo livello isometrico non ha elementi in movimento o ha pochi personaggi che camminano sopra il livello, l'ordinamento in profondità è semplice. In questi casi, i personaggi che occupano le tessere isometriche sarebbero più piccoli delle tessere stesse e possono facilmente usare lo stesso ordine / profondità di disegno della tessera che occupano. 

Facciamo riferimento a tali livelli immobili come livelli statici. Ci sono alcuni modi in cui tali livelli possono essere disegnati in modo da trasmettere la giusta profondità. In genere, i dati di livello saranno un array bidimensionale in cui le righe e le colonne corrisponderanno alle righe e alle colonne del livello. 

Considera il seguente livello isometrico con solo due righe e sette colonne.

I numeri sulle tessere indicano il loro sortingOrder o profondità o ordine z, ovvero l'ordine in cui devono essere disegnati. In questo metodo, stiamo disegnando tutte le colonne nella prima riga, iniziando dalla prima colonna con a sortingOrder di 1. 

Una volta che tutte le colonne sono disegnate nella prima riga, la colonna più vicina alla fotocamera ha un sortingOrder di 7, e procediamo alla riga successiva. Quindi qualsiasi elemento nella seconda riga avrà un valore più alto sortingOrder di qualsiasi elemento della prima riga. 

Questo è esattamente il modo in cui le piastrelle devono essere disposte per trasmettere la profondità corretta come uno sprite con una maggiore sortingOrder si sovrapporrà a qualsiasi altro sprite con più basso sortingOrder.

Per quanto riguarda il codice, si tratta semplicemente di eseguire il looping tra le righe e le colonne dell'array di livelli e l'assegnazione sortingOrder sequenzialmente in ordine crescente. Non si romperà, anche se scambiamo righe e colonne, come si può vedere nell'immagine qui sotto.

Qui disegniamo una colonna completa prima di passare alla riga successiva. La percezione della profondità rimane intatta. Quindi la logica per un livello statico è disegnare una riga completa o una colonna completa e quindi passare alla successiva mentre si assegna sortingOrder sequenzialmente in ordine crescente.

Aggiunta di altezza

Se consideriamo il livello come un edificio, stiamo attualmente disegnando il piano terra. Se abbiamo bisogno di aggiungere un nuovo pavimento al nostro edificio, tutto quello che dobbiamo fare è aspettare prima di disegnare l'intero piano terra e seguire lo stesso metodo per il piano successivo. 

Per la giusta profondità, abbiamo aspettato che l'intera fila fosse completa prima di passare alla fila successiva, e allo stesso modo aspettiamo che tutte le righe siano complete prima di passare al piano successivo. Quindi per un livello con una sola riga e due piani, sembrerebbe l'immagine qui sotto.

In sostanza, qualsiasi tessera sul piano più alto avrà una maggiore sortingOrder di qualsiasi tessera al piano inferiore. Per quanto riguarda il codice per l'aggiunta di piani superiori, abbiamo solo bisogno di compensare il y valore delle coordinate dello schermo per il riquadro, a seconda del piano che occupa.

float floorHeight = tileSize / 2.2f; float currentFloorHeight = floorHeight * floorLevel; // tmpPos = GetScreenPointFromLevelIndices (i, j); tmpPos.y + = currentFloorHeight; tile.transform.position = tmpPos;

Il floorHeight valore indica l'altezza percepita dell'immagine della piastrella del blocco isometrico, mentre Piano terra indica quale piano occupa la piastrella.

2. Spostamento di piastrelle sull'asse X.

L'ordinamento della profondità su un livello isometrico statico non era complicato, giusto? Andando avanti, decidiamo di seguire il primo metodo di riga, dove assegniamo sortingOrder alla prima riga completamente e quindi procedere alla successiva. Consideriamo la nostra prima tessera o piattaforma mobile che si muove su un singolo asse, l'asse x. 

Quando dico che il movimento è sull'asse x, è necessario rendersi conto che stiamo parlando del sistema di coordinate cartesiane e non del sistema di coordinate isometriche. Consideriamo un livello con solo un piano terra di tre righe e sette colonne. Consideriamo anche che la seconda riga ha una sola tessera, che è la nostra tessera mobile. Il livello sarà simile all'immagine qui sotto.

La tessera scura è la nostra tessera mobile e la sortingOrder verrebbe assegnato sarà 8 come la prima riga ha 7 tessere. Se la tessera si sposta sull'asse x cartesiano, si sposterà lungo la trincea tra le due file. In tutte le posizioni che può occupare lungo quel percorso, le tessere della riga 1 avranno una minore sortingOrder

Allo stesso modo, tutte le tessere della riga 2 avranno un valore più alto sortingOrder, indipendentemente dalla posizione della piastrella scura lungo il percorso. Quindi, seguendo il primo metodo di assegnazione, seguiamo una riga sortingOrder, non abbiamo bisogno di fare nulla per il movimento sull'asse x. Ora, è stato facile.

3. Spostamento di piastrelle sull'asse Y.

I problemi cominciano a sorgere quando iniziamo a considerare l'asse y. Consideriamo un livello in cui la nostra tessera oscura si muove lungo una trincea rettangolare, come mostrato di seguito. Puoi vedere lo stesso nel MovingSortingProblem Scena unitaria nella fonte.

Usando il nostro primo approccio di fila, possiamo fornire a sortingOrder per la nostra tessera mobile in base alla fila che occupa attualmente. Quando la tessera si trova tra due file, verrà assegnata a sortingOrder in base alla riga da cui si sta muovendo. In tal caso, non può seguire il sequenziale sortingOrder nella fila in cui si sta muovendo. Questo essenzialmente rompe il nostro approccio di selezione della profondità.

Ordinamento in blocchi

Per risolvere questo, abbiamo bisogno di dividere il nostro livello in diversi blocchi, tra i quali uno è il blocco del problema, che si rompe sotto il nostro primo approccio di fila, e il resto sono blocchi che possono seguire il primo approccio senza interruzione. Considera l'immagine in basso per una migliore comprensione.

Il blocco tile 2x2 rappresentato dall'area blu è il nostro blocco problema. Tutti gli altri blocchi possono ancora seguire il primo approccio di fila. Si prega di non essere confusi dall'immagine in quanto mostra un livello che è già correttamente ordinato utilizzando il nostro approccio a blocchi. Il blocco blu consiste delle due tessere di colonna nelle file tra le quali la nostra tessera oscura è attualmente in movimento e le tessere immediatamente a sinistra di esse. 

Per risolvere il problema di profondità per il blocco del problema, possiamo utilizzare il primo approccio della colonna solo per questo blocco. Quindi per i blocchi verde, rosa e giallo, usiamo prima la riga, e per il blocco blu, usiamo il primo approccio della colonna. 

Si noti che è ancora necessario assegnare in modo sequenziale il file sortingOrder. Prima il blocco verde, poi il blocco rosa a sinistra, poi il blocco blu, ora arriva il blocco rosa a destra e infine il blocco giallo. Interrompiamo l'ordine solo per passare al primo approccio della colonna mentre siamo al blocco blu.

In alternativa, possiamo anche considerare il blocco 2x2 a destra della colonna del riquadro mobile. (La cosa interessante è che non hai nemmeno bisogno di cambiare approccio dato che irrompere nei blocchi ha già risolto il nostro problema in questo caso.) La soluzione può essere vista in azione nel BlockSort scena.

Questo si traduce in codice come di seguito.

private void DepthSort () Vector2 movingTilePos = GetLevelIndicesFromScreenPoint (movingGO.transform.position); int blockColStart = (int) movingTilePos.y; int blockRowStart = (int) movingTilePos.x; int depth = 1; // ordina le righe prima del blocco per (int i = 0; i < blockRowStart; i++)  for (int j = 0; j < cols; j++)  depth=AssignDepth(i,j,depth);   //sort columns in same row before the block for (int i = blockRowStart; i < blockRowStart+2; i++)  for (int j = 0; j < blockColStart; j++)  depth=AssignDepth(i,j,depth);   //sort block for (int i = blockRowStart; i < blockRowStart+2; i++)  for (int j = blockColStart; j < blockColStart+2; j++)  if(movingTilePos.x==i&&movingTilePos.y==j) SpriteRenderer sr=movingGO.GetComponent(); sr.sortingOrder = profondità; // assegna una nuova profondità di profondità ++; // incrementa la profondità else depth = AssignDepth (i, j, depth);  // ordina le colonne nella stessa riga dopo il blocco per (int i = blockRowStart; i < blockRowStart+2; i++)  for (int j = blockColStart+2; j < cols; j++)  depth=AssignDepth(i,j,depth);   //sort rows after block for (int i = blockRowStart+2; i < rows; i++)  for (int j = 0; j < cols; j++)  depth=AssignDepth(i,j,depth);   

4. Spostare le tessere sull'asse Z.

Un movimento nell'asse z è un movimento falso a livello isometrico. Essenzialmente è solo movimento sull'asse y dello schermo. Per un livello isometrico a piano singolo, non c'è altro da fare per aggiungere movimento sull'asse z se è già stato fatto il metodo di ordinamento dei blocchi sopra descritto. Puoi vedere questo in azione nel SingleLayerWave Scena unitaria, in cui ho aggiunto un movimento ondulatorio aggiuntivo sull'asse z insieme al movimento laterale della trincea.

Movimento Z su livelli con più piani

Aggiungere un piano aggiuntivo al tuo livello è solo questione di compensare la coordinata dello schermo y, come spiegato in precedenza. Se la tessera non si muove sull'asse z, non è necessario fare nulla di speciale per l'ordinamento in profondità. Possiamo bloccare l'ordinamento del piano terra con movimento e quindi applicare il primo ordinamento di fila a ciascun piano successivo. Puoi vedere questo in azione nel BlockSortWithHeight Scena dell'Unità.

Un problema di profondità molto simile si presenta quando la tessera inizia a spostarsi tra i piani. Può solo soddisfare l'ordine sequenziale di un piano usando il nostro approccio e rompere l'ordinamento in profondità dell'altro piano. Abbiamo bisogno di estendere o modificare il nostro ordinamento dei blocchi su tre dimensioni per affrontare questo problema di profondità con i pavimenti.

In sostanza, il problema saranno solo i due piani tra i quali la tessera è attualmente in movimento. Per tutti gli altri piani, possiamo attenerci al nostro attuale approccio di smistamento. Esigenze speciali si applicano solo a questi due piani, tra i quali possiamo prima determinare il piano inferiore come sotto dove tileZOffset è la quantità di movimento sull'asse z per la nostra tessera mobile.

float whichFloor = (tileZOffset / floorHeight); float lower = Mathf.Floor (cheFloor);

Ciò significa che inferiore e inferiore + 1 sono i piani che richiedono un approccio speciale. Il trucco è assegnare sortingOrder per entrambi questi piani insieme, come mostrato nel codice qui sotto. Questo risolve la sequenza in modo che i problemi di profondità siano risolti.

se (floor == lower) // abbiamo bisogno di ordinare il piano inferiore e il pavimento appena sopra insieme in una profondità di go = (floor * (rows * cols)) + 1; int nextFloor = floor + 1; if (nextFloor> = totalFloors) nextFloor = piano; // ordina le righe prima del blocco per (int i = 0; i < blockRowStart; i++)  for (int j = 0; j < cols; j++)  depth=AssignDepth(i,j,depth,floor); depth=AssignDepth(i,j,depth,nextFloor);   //sort columns in same row before the block for (int i = blockRowStart; i < blockRowStart+2; i++)  for (int j = 0; j < blockColStart; j++)  depth=AssignDepth(i,j,depth,floor); depth=AssignDepth(i,j,depth,nextFloor);   //sort block for (int i = blockRowStart; i < blockRowStart+2; i++)  for (int j = blockColStart; j < blockColStart+2; j++)  if(movingTilePos.x==i&&movingTilePos.y==j) SpriteRenderer sr=movingGO.GetComponent(); sr.sortingOrder = profondità; // assegna una nuova profondità di profondità ++; // incrementa la profondità else depth = AssignDepth (i, j, depth, floor); Profondità = AssignDepth (i, j, profondità, nextFloor);  // ordina le colonne nella stessa riga dopo il blocco per (int i = blockRowStart; i < blockRowStart+2; i++)  for (int j = blockColStart+2; j < cols; j++)  depth=AssignDepth(i,j,depth,floor); depth=AssignDepth(i,j,depth,nextFloor);   //sort rows after block for (int i = blockRowStart+2; i < rows; i++)  for (int j = 0; j < cols; j++)  depth=AssignDepth(i,j,depth,floor); depth=AssignDepth(i,j,depth,nextFloor);   

Essenzialmente, stiamo considerando due piani come un piano singolo e stiamo facendo un blocco in un unico piano. Controlla il codice e l'azione nella scena BlockSortWithHeightMovement. Con questo approccio, la nostra tessera è ora libera di muoversi su uno qualsiasi dei due assi senza rompere la profondità della scena, come mostrato di seguito.

Conclusione

L'idea di questo tutorial è stata quella di chiarire la logica degli approcci di approfondimento, e spero che tu l'abbia capito bene. È evidente che stiamo considerando livelli relativamente semplici con una sola tessera mobile. 

Non ci sono pendenze in quanto l'inclusione di piste avrebbe reso questo tutorial molto più lungo. Ma una volta che hai compreso la logica di ordinamento, puoi provare ad estendere la logica della pendenza bidimensionale alla vista isometrica.

L'unità ha un'economia attiva. Ci sono molti altri prodotti che ti aiutano a costruire il tuo progetto. La natura della piattaforma lo rende anche un'ottima opzione da cui puoi migliorare le tue abilità. In ogni caso, puoi vedere ciò che abbiamo a disposizione nel mercato Envato.