Å lage organiske former som trær kan være et interessant sideprosjekt for potensielle spillutviklere. Du kan bruke samme logikk for å lage nivåer eller andre kompliserte logikkstrukturer. I denne opplæringen skal vi lage 2D treformer i Unity ved hjelp av to forskjellige tilnærminger: Fractal og L-System.
Selv om vi kaller disse 2D-treformene, er de i hovedsak 3D mesh objekter i Unity. Spillobjektet som har treskriptet, må ha disse 3D-maskinkomponentene festet for å skape vår treform. Disse komponentene er MeshRenderer
og MeshFilter
, som vist under.
Med disse komponentene festet, vil vi skape et nytt nett med de forskjellige algoritmer for fraktaler og L-Systems.
Et 3D-nett er opprettet ved hjelp av flere hjørner som kombinerer for å danne ansikter. For å lage et enkelt ansikt, trenger vi minst tre hjørner. Når vi kobler tre hjørner i en urvisende rekkefølge, får vi et ansikt som har en normal peker utover. Siktet av et ansikt er avhengig av retningen til det normale, og følgelig sekvensen der knutepunktene sendes inn for å skape et ansiktsforhold. Vennligst les den offisielle Unity-dokumentasjonen for ytterligere detaljer angående etableringen av et nett.
Med kraften i nettverksskaping under beltet, la oss gå videre til vår første metode for å lage et 2D-tre: fraktal.
En fraktal er en form opprettet ved å gjenta et mønster med varierende skalaer. Teoretisk sett kan en fraktal være et uendelig mønster, hvor basismønsteret blir gjentatt på ubestemt tid mens størrelsen blir redusert gradvis. Når det kommer til et tre, kan det grunnleggende fraktalmønster være en gren splittet i to grener. Dette grunnmønsteret kan gjentas for å lage den symmetriske treformen som er vist nedenfor.
Vi må stoppe gjentakelsen etter et visst antall iterasjoner, og resultatet er åpenbart ikke en veldig realistisk treform. Likevel er skjønnheten i denne tilnærmingen - og fraktaler generelt - at de lett kan opprettes ved hjelp av enkle rekursive funksjoner. Basemønster tegne metode kan rekursivt kalle seg selv mens du reduserer skalaen til et visst antall iterasjoner er fullført.
Den primære komponenten i en treform er en gren. I vår tilnærming har vi en Branch
klasse, som har a CreateBranch
metode som vist nedenfor.
privat tomrom CreateBranch (Vector3 opprinnelse, flytegrenLengde, flytegrenBredde, flytegrenAngle, Vector3-offset, float widthDecreaseFactor) Vector3 bottomLeft = ny Vector3 (origin.x, origin.y, origin.z), bottomRight = ny Vector3 (origin.x , origin.y, origin.z), topLeft = ny Vector3 (origin.x, origin.y, origin.z), topRight = ny Vector3 (origin.x, origin.y, origin.z); bottomLeft.x- = branchWidth * 0.5f; bottomRight.x + = branchWidth * 0.5f; topLeft.y = topRight.y = origin.y + branchLength; float newWidth = branchWidth * widthDecreaseFactor; topLeft.x- = newWidth * 0.5f; topRight.x + = newWidth * 0.5f; Vector3 akse = Vector3.back; Quaternion rotationValue = Quaternion.AngleAxis (grenAngle, akse); vertices.Add ((rotationValue * (bottomLeft)) + offset); vertices.Add ((rotationValue * (topLeft)) + offset); vertices.Add ((rotationValue * (topRight)) + offset); vertices.Add ((rotationValue * (bottomRight)) + offset);
En gren er i hovedsak en form (eller en Quad
) med fire hjørnepunger: nede til venstre
, øverst til venstre
, øverst til høyre
, og Nede til høyre
. De CreateBranch
Metoden gjør riktig posisjonering av grenen ved å oversette, rotere og skalere disse fire vertikaler basert på formen, posisjonen og rotasjonen av grenen. Spissen på grenen er avsmalnet ved hjelp av widthDecreaseFactor
verdi. Hovedmetoden kan kalle denne metoden mens den går i posisjon og rotasjonsverdier for den grenen.
De FractalTreeProper
klassen har en rekursiv CreateBranch
metode, som igjen vil skape Branch
klassens CreateBranch
konstruktormetode.
privat tomrom CreateBranch (int currentLayer, Vector3 branchOffset, floatvinkel, int baseVertexPointer) if (currentLayer> = numLayers) returnerer; float lengde = trunkLength; float width = trunkBaseWidth; for (int i = 0; iHver samtale til
CreateBranch
initierer to nye samtaler til seg selv for sine to barns grener. For vårt eksempel bruker vi en forgreningsvinkel på 30 grader og en verdi på 8 som antall forgrenings-iterasjoner.Vi bruker poengene fra disse grenene for å skape de nødvendige knutepunktene, som deretter brukes til å skape ansikter for vårt tremaske.
ansikter = ny liste(); vertices = ny liste (); ftree = GetComponent () .Mesh; fTree.name = "fraktaltre"; // ... (i CreateBranch) hvis (currentLayer == 0) vertices.AddRange (branch.vertices); faces.Add (baseVertexPointer); faces.Add (baseVertexPointer + 1); faces.Add (baseVertexPointer + 3); faces.Add (baseVertexPointer + 3); faces.Add (baseVertexPointer + 1); faces.Add (baseVertexPointer + 2); ellers int vertexPointer = vertices.Count; vertices.Add (branch.vertices [1]); vertices.Add (branch.vertices [2]); int indexDelta = 3; hvis (currentLayer! = 1) indexDelta = 2; faces.Add (baseVertexPointer-IndexDelta); faces.Add (vertexPointer); faces.Add (baseVertexPointer- (indexDelta-1)); faces.Add (baseVertexPointer- (indexDelta-1)); faces.Add (vertexPointer); faces.Add (vertexPointer + 1); baseVertexPointer = vertices.Count; // ... fTree.vertices = vertices.ToArray (); fTree.triangles = faces.ToArray (); fTree.RecalculateNormals (); De
baseVertexPointer
Verdien brukes til å gjenbruke eksisterende hjørner slik at vi unngår å skape dupliserte vertikaler, da hver gren kan ha fire hjørner, men bare to av dem er nye.Ved å legge til noen tilfeldighet i forgreningsvinkelen, kan vi også lage asymmetriske varianter av vårt fraktaltre, som kan se mer realistisk ut.
3. Opprette et L-System Tree
Den andre metoden, L-systemet, er et helt annet dyr. Det er et svært komplisert system som kan brukes til å lage intrikat komplekse organiske former eller å lage komplekse regelsett eller strengsekvenser. Den står for Lindenmayer System, detaljene som finnes på Wikipedia.
Applikasjoner av L-systemer inkluderer robotteknologi og AI, og vi vil bare røre toppen av isbreen mens du bruker den til våre formål. Med et L-system er det mulig å lage veldig realistiske utseende på tre eller buskformer manuelt med presis kontroll eller bruk av automatisering.
De
Branch
komponenten forblir den samme som i fraktaleksemplet, men måten vi lager grener til, endres.Dissecting L-System
L-systemer brukes til å lage kompliserte fraktaler hvor mønstrene ikke er lett å se. Det blir menneskelig umulig å finne disse repeterende mønstrene visuelt, men L-systemer gjør det lettere å lage dem programmatisk. L-systemer består av et sett med alfabeter som kombinerer for å danne en streng, sammen med et sett med regler som muterer disse strengene i en enkelt iterasjon. Ved å bruke disse reglene over flere iterasjoner opprettes en lang, komplisert streng som kan fungere som grunnlag for å skape vårt tre.
Alfabetene
For vårt eksempel vil vi bruke dette alfabetet til å lage vår tre streng:
F
,+
,-
,[
, og]
.Reglene
For vårt eksempel trenger vi bare en regel der alfabetet
F
endrer seg til en sekvens av alfabeter, sierF + [+ FF-F-FF] - [- FF + F + F]
. Ved hver iterasjon vil vi gjøre denne bytte samtidig som alle de andre alfabeter holdes uendret.Axiom
Axiomet, eller startstrengen, vil være
F
. Dette betyr i hovedsak at etter den første iterasjonen blir strengenF + [+ FF-F-FF] - [- FF + F + F]
.Vi vil iterere tre ganger for å lage en brukbar trestreng som vist nedenfor.
lString = "F"; regler = ny ordbok(); Reglene [ "F"] = "F + [+ FF-F-FF] - [- FF + F + F]"; for (int i = 0; i Parsing Tree String
Nå som vi har trestrengen ved hjelp av L-systemet, må vi analysere det for å lage vårt tre. Vi vil se på hvert tegn i trestrengen og gjøre bestemte handlinger basert på dem som nevnt nedenfor.
- På å finne
F
, Vi vil lage en gren med nåværende parametere med lengde og rotasjon.- På å finne
+
, Vi legger til den nåværende rotasjonsverdien.- På å finne
-
, Vi vil trekke fra den nåværende rotasjonsverdien.- På å finne
[
, Vi lagrer nåværende posisjon, lengde og rotasjonsverdi.- På å finne
]
, Vi vil gjenopprette ovennevnte verdier fra lagret tilstand.Vi bruker en vinkelverdi på
25
grader for grenrotasjon for vårt eksempel. DeCreateTree
metode iLSystemTree
klassen analyserer. For å lagre og gjenopprette stater bruker vi enLevelState
klasse som lagrer de nødvendige verdiene sammen med aBranchState
struct.levelStates = ny liste(); char [] chars = lString.ToCharArray (); float currentRotation = 0; float currentLength = startLength; float currentWidth = startWidth; Vector3 currentPosition = treeOrigin; int levelIndex = 0; LevelState levelState = new LevelState (); levelState.position = currentPosition; levelState.levelIndex = levelIndex; levelState.width = currentWidth; levelState.length = currentLength; levelState.rotation = currentRotation; levelState.logicBranches = ny liste (); levelStates.Add (levelState); Vector3 tipPosition = ny Vector3 (); Kø savedStates = ny kø (); for (int i = 0; i (); levelStates.Add (levelState); currentLength * = lengthDecreaseFactor; gå i stykker; case '+': currentRotation + = vinkel; gå i stykker; case '-': currentRotation- = vinkel; gå i stykker; sak '[': savedStates.Enqueue (levelState); gå i stykker; case ']': levelState = savedStates.Dequeue (); currentPosition = levelState.position; currentRotation = levelState.rotation; currentLength = levelState.length; currentWidth = levelState.width; levelIndex = levelState.levelIndex; gå i stykker; Variabelen
levelStates
lagrer en liste overLevelState
forekomster, hvor et nivå kan betraktes som et forgreningspunkt. Da hvert slik forgreningspunkt kan ha flere grener eller bare en gren, lagrer vi de grener i en listelogicBranches
holder flereBranchState
forekomster. DesavedStates
Kø
sporer lagring og gjenoppretting av forskjelligeLevelState
s. Når vi har den logiske strukturen for treet på plass, kan vi brukelevelStates
liste for å lage visuelle grener og lage tremasken.for (int i = 0; i
The GetClosestVextexIndices
Metoden brukes til å finne felles hjørner for å unngå duplikater mens du lager nettverket.Ved å variere reglene litt, kan vi få drastisk forskjellige trestrukturer, som vist på bildet nedenfor.
Det er mulig å manuelt opprette trestrengen for å designe en bestemt type tre, selv om dette kan være en veldig kjedelig oppgave.
Konklusjon
Å spille med L-systemer kan være morsomt, og det kan også gi svært uforutsigbare resultater. Prøv å endre reglene eller legge til flere regler for å lage andre kompliserte former. Den neste logiske tingen å gjøre, er å prøve å utvide dette til 3D-plass ved å erstatte disse 2D Quads med 3D-sylindere for grener og legge til Z-dimensjonen for forgrening.
Hvis du er seriøs om å lage 2D-trær, foreslår jeg at du ser på plass koloniseringsalgoritmer som neste trinn.