Isometrisk djupsortering för rörliga plattformar

Vad du ska skapa

Djupsortering kan förklaras i enkla termer som ett sätt att bestämma vilket element som är närmare kameran och som ligger längre bort och därigenom bestämma vilken ordning de behöver anordnas för att förmedla rätt djup i scenen.

I denna handledning gräver vi djupare sortering för isometriska nivåer när vi försöker lägga till rörliga plattformar. Det här är inte en nybörjarehandledning om isometrisk teori och handlar inte om koden. Fokus ligger på att förstå logiken och teorin snarare än att dissekera koden. Det valfria verktyget för handledningen är Unity, och djup sortering ändrar i huvudsak sortingOrder av spritesna involverade. För andra ramverk kan det vara en förändring av z-ordningen eller sekvensen av ritningsordningen.

För att komma igång med isometrisk teori, hänvisas till denna handledningsserie. Koden och scenstrukturen följer min tidigare isometriska handledning. Vänligen hänvisa till dessa om du hittar handledningen svår att följa eftersom jag kommer att fokusera endast på logik i denna handledning.

1. Nivåer utan rörelse

Om din isometriska nivå inte har några rörliga element eller bara har några tecken att gå över nivån, är djup sorteringen okomplicerad. I sådana fall skulle de tecken som upptar isometriska plattor vara mindre än plattorna själva och kan enkelt bara använda samma ritningsordning / djup som kakel de upptar. 

Låt oss hänvisa till sådana orörliga nivåer som statiska nivåer. Det finns några sätt på vilka sådana nivåer kan dras så att rätt djup transporteras. Normalt kommer nivådata att vara en tvådimensionell array där raderna och kolumnerna kommer att motsvara raderna och kolumnerna på nivån. 

Tänk på följande isometriska nivå med bara två rader och sju kolumner.

Siffrorna på plattorna indikerar deras sortingOrder eller djup- eller z-ordningen, dvs den ordning i vilken de behöver dras. I denna metod ritar vi alla kolumnerna i första raden, med början med den första kolumnen med a sortingOrder av 1. 

När alla kolumner är ritade i första raden har den närmaste kolumnen till kameran a sortingOrder av 7, och vi fortsätter till nästa rad. Så något element i den andra raden kommer att ha en högre sortingOrder än något element i första raden. 

Det är just hur kakelarna måste ordnas för att förmedla det korrekta djupet som en sprite med en högre sortingOrder kommer att överlagras över alla andra sprites med lägre sortingOrder.

När det gäller koden är det bara en fråga om looping genom raderna och kolumnerna i nivån array och tilldelning sortingOrder sekventiellt i en ökande ordning. Det skulle inte bryta, även om vi byter rader och kolumner, vilket kan ses på bilden nedan.

Här ritar vi en komplett kolumn först innan vi flyttar till nästa rad. Djupuppfattningen förblir intakt. Så logiken för en statisk nivå är att dra antingen en komplett rad eller komplett kolumn och fortsätt sedan till nästa medan du tilldelar sortingOrder sekventiellt i en ökande ordning.

Lägger till höjd

Om vi ​​betraktar nivån som en byggnad ritar vi för närvarande bottenvåningen. Om vi ​​behöver lägga till ett nytt golv till vår byggnad är allt vi behöver göra för att vänta tills vi drar hela bottenvåningen först och följer samma metod för nästa våning. 

För rätt djup väntade vi tills hela raden var färdig innan vi flyttade till nästa rad, och på samma sätt väntar vi tills alla rader är färdiga innan vi flyttar till nästa våning. Så för en nivå med endast en enda rad och två våningar, skulle det se ut som bilden nedan.

I huvudsak kommer alla plattor på högre våningen att ha en högre sortingOrder än någon kakel på nedre våningen. När det gäller koden för att lägga till högre våningar behöver vi bara kompensera y värdet av skärmkoordinaterna för kakel, beroende på vilket golv det upptar.

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

De floorHeight värde anger den uppfattade höjden på den isometriska blockbilden, medan våningsplan Anger vilket golv kakeln upptar.

2. Flyttning av plattor på X-axeln

Djup sortering på en statisk isometrisk nivå var inte komplicerat, eller hur? Förflyttning, låt oss bestämma oss för att följa raden första metoden, där vi tilldelar sortingOrder till första raden helt och fortsätt sedan till nästa. Låt oss överväga vår första rörliga kakel eller plattform som rör sig på en enda axel, x-axeln. 

När jag säger att rörelsen är på x-axeln, måste du inse att vi pratar om kartesiska koordinatsystemet och inte det isometriska koordinatsystemet. Låt oss överväga en nivå med endast en bottenvåning i tre rader och sju kolumner. Låt oss också betrakta att den andra raden bara har en enda kakel, som är vår rörliga kakel. Nivån kommer att se ut som bilden nedan.

Den mörka kakel är vår rörliga kakel, och sortingOrder det skulle bli tilldelat kommer att vara 8 som första raden har 7 plattor. Om kakeln flyttas på kartesian x-axeln kommer den att röra sig längs grävningen mellan de två raderna. Vid alla positioner som det kan uppta längs den vägen kommer plattorna i rad 1 att ha en mindre sortingOrder

På samma sätt kommer alla plattor i rad 2 att ha en högre sortingOrder, oberoende av placeringen av den mörka plattan längs nämnda bana. Så som vi följer en rad första metod för att tilldela sortingOrder, vi behöver inte göra något för rörelse på x-axeln. Nu var det lätt.

3. Flyttning av plattor på Y-axeln

Problem uppstår när vi börjar överväga y-axeln. Låt oss överväga en nivå där vår mörka kakel rinner längs en rektangulär gräv, som visas nedan. Du kan se detsamma i MovingSortingProblem Enhetsplats i källan.

Med hjälp av vår rad första tillvägagångssätt kan vi ge en sortingOrder för vår rörliga kakel baserat på den rad som den för närvarande upptar. När kakeln är mellan två rader, skulle den bli tilldelad a sortingOrder baserat på raden den flyttar från. I det fallet kan det inte följa sekventiellt sortingOrder i raden i vilken den rör sig. Detta bryter väsentligen vår djup sortering tillvägagångssätt.

Sortering i block

För att lösa detta måste vi dela upp vår nivå i olika block, bland vilka ett är problemblocket som bryter under vår rad första tillvägagångssätt, och resten är block som kan följa raden första tillvägagångssättet utan att bryta. Tänk på bilden nedan för en bättre förståelse.

Det 2x2 kakelblock som representeras av det blå området är vårt problemblock. Alla andra block kan fortfarande följa radens första tillvägagångssätt. Vänligen bli inte förvirrad av bilden eftersom den visar en nivå som redan är ordentligt sorterad med hjälp av vår blockinriktning. Det blå blocket består av de två kolonnplattorna i raderna mellan vilka våra mörkplattor för närvarande rör sig och plattorna omedelbart till vänster om dem. 

För att lösa djupproblemet för problemblocket kan vi använda kolumnens första tillvägagångssätt för det här blocket ensam. Så för de gröna, rosa och gula blocken använder vi först raden och för det blå blocket använder vi kolumnens första tillvägagångssätt. 

Observera att vi fortfarande behöver sekvensiellt tilldela sortingOrder. Först det gröna blocket, då det rosa blocket till vänster, sedan det blå blocket, kommer nu det rosa blocket till höger, och slutligen den gula blocket. Vi bryter ordningen bara för att byta till kolumnens första tillvägagångssätt vid det blå blocket.

Alternativt kan vi också överväga 2x2-blocket till höger om den rörliga kakelkolonnen. (Det intressanta är att du inte ens behöver byta tillvägagångssätt som att bryta sig in i blocken själv har redan löst vårt problem i det här fallet.) Lösningen kan ses i handling i BlockSort scen.

Detta översätter till kod enligt nedan.

privat tomrum DepthSort () Vector2 movingTilePos = GetLevelIndicesFromScreenPoint (movingGO.transform.position); int blockColStart = (int) movingTilePos.y; int blockRowStart = (int) movingTilePos.x; int djup = 1; // sortera rader före block för (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 = djup; // tilldela nytt djupdjup ++; // inkrementdjup annat djup = AssignDepth (jag, j, djup);  // sortera kolumner i samma rad efter blocket för (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. Flyttande plattor på Z-axeln

En rörelse i z-axeln är en falsk rörelse på isometrisk nivå. Det är i huvudsak bara rörelse på skärmen y-axeln. För en isometrisk nivå på en våning finns det inget mer att göra för att lägga till rörelse på z-axeln om du redan har gjort blockeringsmetoden som beskrivs ovan. Du kan se detta i aktion i SingleLayerWave Unity-scenen, där jag har lagt till en ytterligare vågrörelse på z-axeln tillsammans med lateral trench-rörelsen.

Z Rörelse på nivåer med flera våningar

Att lägga till ett extra golv till din nivå handlar bara om att kompensera skärm y-koordinaten, som förklarats tidigare. Om kakelan inte rör sig på z-axeln är det inte nödvändigt att göra något speciellt för djupsortering. Vi kan blockera sortera bottenvåningen med rörelse och sedan tillämpa rad första sortering till varje efterföljande golv. Du kan se detta i aktion i BlockSortWithHeight Enhetsscenen.

Ett mycket liknande djupproblem uppstår när kakel börjar röra sig mellan golv. Det kan bara uppfylla sekventiell ordning på en våning med hjälp av vårt tillvägagångssätt och skulle bryta djup sortering av andra våningen. Vi behöver utvidga eller ändra vår blockering till tre dimensioner för att hantera detta djupproblem med golv.

Problemet kommer i huvudsak att vara bara de två våningarna mellan vilka kakeln för närvarande rör sig. För alla andra våningar kan vi hålla fast vid vårt nuvarande sortiment. Särskilda behov gäller endast dessa två våningar, bland annat kan vi först bestämma nedre våningen som nedan tileZOffset är mängden rörelse på z-axeln för vår rörliga kakel.

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

Detta innebär att lägre och lägre + 1 Är golven som behöver en särskild inställning. Tricket är att tilldela sortingOrder för båda dessa golv tillsammans, som visas i koden nedan. Detta fixar sekvensen så att djupproblemen sorteras ut.

om (golv == lägre) // vi måste sortera nedre våningen och golvet strax ovanför det på ett gång djup = (golv * (rader * kols)) + 1; int nextFloor = golv + 1; if (nextFloor> = totalFloors) nextFloor = golvet; // sortera rader före block för (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 = djup; // tilldela nytt djupdjup ++; // inkrementdjup annat djup = AssignDepth (jag, j, djup, golv); djup = AssignDepth (i, j, djup, nextFloor);  // sortera kolumner i samma rad efter blocket för (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);   

I grund och botten överväger vi två våningar som ett enda våning och gör en block sortera på det enda våningen. Kolla in koden och åtgärden i scenen BlockSortWithHeightMovement. Med detta tillvägagångssätt är vår kakel nu fri att flytta på någon av de två axlarna utan att bryta djupet av scenen, som visas nedan.

Slutsats

Tanken med denna handledning var att klargöra logiken i djupsorteringsmetoderna, och jag hoppas att du har förstått det här. Det är uppenbart att vi överväger relativt enkla nivåer med endast en rörlig kakel. 

Det finns inga backar, antingen som att ha backar skulle ha gjort detta till en mycket längre handledning. Men när du förstått sorteringslogiken kan du försöka utvidga den tvådimensionella lutningslogiken till isometrisk vy.

Enhet har en aktiv ekonomi. Det finns många andra produkter som hjälper dig att bygga upp ditt projekt. Plattformens karaktär gör det också till ett bra alternativ som du kan förbättra dina färdigheter. Oavsett fall kan du se vad vi har tillgängligt på Envato Market.