Modelarea adâncimii izometrice pentru platforme mobile

Ce veți crea

Sortarea în adâncime poate fi explicată în termeni simpli ca o modalitate de a determina care element este mai aproape de cameră și care este mai departe, determinând astfel ordinea în care trebuie să fie aranjate pentru a transmite adâncimea potrivită în scenă.

În acest tutorial, vom explora mai adânc în sortare pentru niveluri izometrice în timp ce încercăm să adăugăm platforme în mișcare. Acesta nu este un tutorial începător în teoria izometrică și nu este vorba despre cod. Accentul este de a înțelege logica și teoria mai degrabă decât de a diseca codul. Instrumentul de alegere al tutorialului este Unitatea și, prin urmare, sortarea de adâncime în esență schimbă sortingOrder din spritele implicate. Pentru alte cadre, poate fi o schimbare a ordinii z sau a secvenței de ordine a desenului.

Pentru a începe despre teoria izometrică, vă rugăm să consultați această serie de tutori. Structura codului și scena urmează tutorialul meu izometric anterior. Te rog să te referi la acestea dacă găsești tutorialul greu de urmărit, deoarece mă voi concentra doar pe logică în acest tutorial.

1. Niveluri fără mișcare

Dacă nivelul dvs. izometric nu are elemente în mișcare sau are doar câteva caractere care se deplasează peste nivelul, sortarea adâncimii este simplă. În astfel de cazuri, caracterele care ocupă plăcile izometrice ar fi mai mici decât plăcile însele și pot folosi cu ușurință aceeași ordine / adâncime de desen ca și piesele pe care le ocupă. 

Să ne referim la astfel de nivele nemișcate ca nivele statice. Există câteva moduri în care astfel de niveluri pot fi desenate astfel încât să fie transmise adâncimea potrivită. În mod tipic, datele de nivel vor fi o matrice bidimensională în care rândurile și coloanele vor corespunde rândurilor și coloanelor nivelului. 

Luați în considerare următorul nivel izometric cu doar două rânduri și șapte coloane.

Numerele de pe plăcile indică sortingOrder sau adâncimea sau ordinul z, adică ordinea în care trebuie să fie trase. În această metodă, desenăm toate coloanele din primul rând, începând cu prima coloană cu a sortingOrder din 1. 

Odată ce toate coloanele sunt desenate în primul rând, cea mai apropiată coloană a camerei are a sortingOrder din 7, și vom trece la rândul următor. Deci, orice element din al doilea rând va avea o valoare mai mare sortingOrder decât orice element al primului rând. 

Acesta este exact modul în care plăcile trebuie să fie aranjate pentru a transmite adâncimea corectă ca o sprite cu o mai mare sortingOrder vor fi suprapuse peste orice alte sprite cu mai mic sortingOrder.

În ceea ce privește codul, aceasta este doar o chestiune de looping prin rândurile și coloanele matricei de nivel și alocarea sortingOrder succesiv într-o ordine crescătoare. Nu s-ar rupe, chiar dacă schimbăm rânduri și coloane, așa cum se poate vedea în imaginea de mai jos.

Aici tragem mai întâi o coloană completă înainte de a trece la rândul următor. Percepția adâncimii rămâne intactă. Deci, logica pentru un nivel static este să desenezi fie un rând complet sau o coloană completă și apoi să treci la următoarea în timp ce alocă sortingOrder succesiv într-o ordine crescătoare.

Adăugarea înălțimii

Dacă luăm în considerare nivelul ca o clădire, în prezent desenăm parterul. Dacă trebuie să adăugăm un nou etaj clădirii noastre, tot ce trebuie să facem este să așteptăm până când tragem primul podea și urmăm aceeași metodă pentru etajul următor. 

Pentru o adâncime adecvată, am așteptat până când întregul rând a fost complet înainte să ne mutăm la rândul următor și, în mod similar, așteptăm până când toate rândurile sunt complete înainte de a ne deplasa la etajul următor. Deci, pentru un nivel cu doar un singur rând și două etaje, ar arăta imaginea de mai jos.

În esență, orice țiglă de la etajul superior va avea un nivel mai ridicat sortingOrder decât orice țiglă de la etajul inferior. În ceea ce privește codul pentru adăugarea de etaje mai mari, trebuie doar să compensăm y valoarea coordonatelor ecranului pentru placi, în funcție de podeaua pe care o ocupă.

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

floorHeight Valoarea indică înălțimea percepută a imaginii izometrice a blocurilor, în timp ce nivelul podelei indică ce podea este ocupată de țiglă.

2. Mutarea pieselor pe axa X

Sortarea în adâncime pe un nivel izometric static nu a fost complicată, nu? Trecem mai departe, să decidem să urmăm prima metodă a rândului, pe care o atribuim sortingOrder la primul rând complet și apoi treceți la următoarea. Să luăm în considerare prima noastră țiglă sau platformă care se mișcă pe o singură axă, axa x. 

Când spun că mișcarea se află pe axa x, trebuie să înțelegeți că vorbim despre sistemul de coordonate cartesian și nu despre sistemul de coordonate izometric. Să considerăm un nivel cu doar un parter de trei rânduri și șapte coloane. Să considerăm, de asemenea, că al doilea rând are doar o singură țiglă, care este țigla noastră în mișcare. Nivelul va arăta ca imaginea de mai jos.

Placa întunecată este țigla noastră în mișcare, și sortingOrder ar fi atribuit va fi de 8 ca primul rând are 7 placi. Dacă țigla se mișcă pe axa x cartesiană, atunci se va deplasa de-a lungul șanțului dintre cele două rânduri. În toate pozițiile pe care le poate ocupa de-a lungul acelei căi, plăcile din rândul 1 vor avea o valoare mai mică sortingOrder

În mod similar, toate plăcile din rândul 2 vor avea o valoare mai mare sortingOrder, indiferent de poziția plăcii întunecate de-a lungul traseului menționat. Așa cum vom urma o primă metodă de atribuire sortingOrder, nu avem nevoie să facem nimic pentru mișcarea pe axa x. Acum, era ușor.

3. Deplasarea plăcilor pe axa Y

Problemele încep să apară atunci când începem să analizăm axa y. Să luăm în considerare un nivel în care țigla noastră întunecată se mișcă de-a lungul unui șanț dreptunghiular, după cum se arată mai jos. Puteți vedea același lucru în MovingSortingProblem Scena unității din sursă.

Folosind prima noastră abordare, putem oferi o sortingOrder pentru tigla noastră în mișcare bazată pe rândul pe care îl ocupă în prezent. Atunci când țigla se află între două rânduri, va fi atribuită a sortingOrder bazat pe rândul din care se mișcă. În acest caz, nu poate urma secvențial sortingOrder în rândul în care se mișcă. Acest lucru ne distruge în esență abordarea de sortare a adâncilor.

Sortarea în blocuri

Pentru a rezolva acest lucru, trebuie să ne împărțim nivelul în blocuri diferite, dintre care unul este blocul de probleme, care se rupe sub prima noastră abordare, iar restul sunt blocuri care pot urma prima abordare a rândului fără a se rupe. Luați în considerare imaginea de mai jos pentru o mai bună înțelegere.

Blocul de blocuri 2x2 reprezentat de zona albastră este blocul nostru de probleme. Toate celelalte blocuri pot să urmeze prima abordare a rândului. Nu vă confundați cu imaginea deoarece arată un nivel care este deja ordonat în mod corespunzător folosind abordarea blocului nostru. Blocul albastru este alcătuit din cele două plăci de coloană din rândurile dintre care țigla noastră întunecată se mișcă în prezent și plăcile imediat în stânga lor. 

Pentru a rezolva problema de adâncime pentru blocul de probleme, putem folosi prima abordare a coloanei numai pentru acest bloc. Deci, pentru blocurile verde, roz și galben, folosim primul rând, iar pentru blocul albastru folosim prima abordare a coloanei. 

Observați că trebuie încă să alocăm secvențial sortingOrder. Mai întâi blocul verde, apoi blocul roz spre stânga, apoi blocul albastru, acum vine blocul roz spre dreapta și, în final, blocul galben. Vom rupe comanda doar pentru a trece la prima abordare a coloanei în timp ce se află în blocul albastru.

Alternativ, putem lua în considerare și blocul 2x2 din dreapta coloanei țiglelor în mișcare. (Interesant este că nici nu trebuie să schimbați abordările, deoarece ruperea în blocuri însăși ne-a rezolvat deja problema în acest caz.) Soluția poate fi văzută în acțiune în BlockSort scenă.

Acest lucru se traduce la codul de mai jos.

void privat DepthSort () Vector2 movingTilePos = GetLevelIndicesFromScreenPoint (movingGO.transform.position); int blocColStart = (int) movingTilePos.y; int blocRowStart = (int) movingTilePos.x; int adâncime = 1; // sortează rândurile înainte de bloc pentru (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 = adâncime; // aloca adâncime adâncime ++; // adâncimea incrementului altceva depth = AssignDepth (i, j, depth);  // sortează coloanele în același rând după blocul pentru (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. Deplasarea plăcilor pe axa Z

O mișcare în axa z este o mișcare falsă la un nivel izometric. Este, în esență, doar mișcarea pe axa y a ecranului. Pentru un nivel izometric la un singur nivel, nu mai este nimic de făcut pentru a adăuga mișcarea pe axa z dacă ați făcut deja metoda de sortare bloc descrisă mai sus. Puteți vedea acest lucru în acțiune în SingleLayerWave Scena unității, unde am adăugat o mișcare suplimentară a undelor pe axa z, împreună cu mișcarea laterală a șanțului.

Z Mișcarea pe nivele cu mai multe etaje

Adăugarea unui nivel suplimentar la nivelul dvs. este doar o chestiune de compensare a coordonatei ecranului y, așa cum sa explicat mai sus. Dacă țigla nu se mișcă pe axa z, atunci nu este nevoie să faceți ceva special pentru sortarea în adâncime. Putem bloca sortarea parterului cu mișcare și apoi aplicăm ordinea în primul rând pentru fiecare etapă succesivă. Puteți vedea acest lucru în acțiune în BlockSortWithHeight Scena unității.

O problemă de profunzime foarte asemănătoare apare când placa începe să se deplaseze între etaje. Poate satisface ordinea secvențială a unui etaj folosind abordarea noastră și ar sparge sortarea de adâncime a celuilalt etaj. Trebuie să extindem sau să modificăm sortarea blocurilor noastre la trei dimensiuni pentru a rezolva această problemă de adâncime cu etajele.

Problema va fi în esență doar cele două etaje între care țigla se mișcă în prezent. Pentru toate celelalte etaje, ne putem baza pe abordarea noastră actuală de sortare. Nevoile speciale se aplică numai acestor două etaje, dintre care mai întâi putem determina etajul inferior, după cum urmează tileZOffset este cantitatea de mișcare pe axa z pentru țigla noastră mobilă.

float whichFloor = (tileZOffset / floorHeight); float inferior = Mathf.Floor (careFloor);

Aceasta înseamnă că inferior și inferior + 1 sunt etajele care necesită o abordare specială. Trucul este de a aloca sortingOrder pentru ambele podele împreună, așa cum se arată în codul de mai jos. Aceasta stabilește secvența astfel încât problemele de adâncime să fie sortate.

dacă (podea == inferior) // trebuie să sortați podeaua inferioară și podeaua chiar deasupra ei împreună într-o singură adâncime = (podea * (rânduri * cols)) + 1; int nextFloor = podea + 1; if (nextFloor> = totalFloors) nextFloor = podea; // sortează rândurile înainte de bloc pentru (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 = adâncime; // aloca adâncime adâncime ++; // adâncimea incrementului altceva depth = AssignDepth (i, j, adâncime, podea); adâncimea = AssignDepth (i, j, adâncimea, nextFloor);  // sortează coloanele în același rând după blocul pentru (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);   

În esență, avem în vedere două etaje ca un singur etaj și de a face un bloc bloc pe acel singur etaj. Verificați codul și acțiunea din scenă BlockSortWithHeightMovement. Cu această abordare, țigla noastră este acum liberă să se deplaseze pe oricare dintre cele două axe fără a sparge adâncimea scenei, după cum se arată mai jos.

Concluzie

Ideea acestui tutorial a fost clarificarea logicii abordărilor de sortare a adâncilor și sper că ați înțeles destul de bine acest lucru. Este evident că avem în vedere niveluri relativ simple, cu o singură placă în mișcare. 

Nu există nici pante, fie că inclusiv pante ar fi făcut acest lucru un tutorial mult mai mult. Dar, odată ce ați înțeles logica de sortare, puteți încerca să extindeți logica bidimensională a pantei în vederea izometrică.

Unitatea are o economie activă. Există multe alte produse care vă ajută să vă construiți proiectul. Natura platformei o face de asemenea o opțiune excelentă din care vă puteți îmbunătăți abilitățile. Indiferent de situație, puteți vedea ce avem la dispoziție în piața Envato.