Creați forme de copac 2D cu cod

Ce veți crea

Crearea de forme organice precum copacii poate fi un proiect interesant pentru dezvoltatorii potențiali de jocuri. Puteți folosi aceeași logică pentru a crea nivele sau alte structuri logice complicate. În acest tutorial, vom crea forme de arbori 2D în Unity folosind două abordări diferite: Fractal și L-System.

1. Crearea 2D cu 3D

Deși numim aceste forme de arbori 2D, ele sunt, în esență, obiecte cu plasă 3D în unitate. Obiectul jocului care are scriptul copac va trebui să aibă atașate aceste componente 3D pentru a crea forma copacului nostru. Aceste componente sunt MeshRenderer si MeshFilter, așa cum se arată mai jos.

Cu aceste componente atașate, vom crea o plasă nouă utilizând diferiți algoritmi pentru fractali și L-Systems.

Crearea unei rețele

O plasă 3D este creată utilizând mai multe noduri care se combină pentru a forma fețe. Pentru a face o singură față, vom avea nevoie de minim trei vârfuri. Când conectăm trei vârfuri într-o secvență în sensul acelor de ceasornic, vom obține o față care are o direcție normală spre exterior. Vizibilitatea unei fețe depinde de direcția normală și, prin urmare, de secvența în care sunt transmise vârfurile pentru a crea o problemă a feței. Vă rugăm să citiți documentația oficială Unity pentru detalii suplimentare referitoare la crearea unei rețele.

Cu puterea de creare a ochiurilor sub centura noastră, să mergem la prima noastră metodă pentru crearea unui arbore 2D: fractal.

2. Crearea unui arbore fractal

Un fractal este o formă creată prin repetarea unui model cu scări diferite. Teoretic, un fractal poate fi un model nesfârșit, unde modelul de bază se repetă pe o perioadă nedeterminată, în timp ce mărimea lui devine progresivă. Când vine vorba de un copac, modelul fractal de bază poate fi o ramură care se împarte în două ramuri. Acest model de bază poate fi repetat pentru a crea forma arborelui simetric prezentată mai jos.

Va trebui să oprim repetarea după un anumit număr de iterații, iar rezultatul nu este, evident, o formă de copac foarte realistă. Cu toate acestea, frumusețea acestei abordări - și fractale în general - este că ele pot fi create cu ușurință folosind funcții simple recursive. Metoda de desenare a modelului de bază poate suna în mod recursiv în timp ce reduce scara până când un anumit număr de iterații sunt complete.

Ramura

Componenta primară într-o formă de copac este o ramură. În abordarea noastră, avem a ramură clasa, care are a CreateBranch așa cum se arată mai jos.

void privat CreateBranch (vector3 de origine, float ramurăLength, flotară ramurăWidth, float ramurăAngle, vector3 offset, float widthDecreaseFactor) Vector3 bottomLeft = Vector3 nou (origin.x, origin.y, orig.z), bottomRight = new Vector3 , origin.y, origin.z), topLeft = Vector3 nou (origin.x, origin.y, orig.z), topRight = Vector3 nou (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 = ramurăWidth * widthDecreaseFactor; topLeft.x- = newWidth * 0.5f; topRight.x + = newWidth * 0.5f; Vector3 axa = Vector3.back; Quaternion rotationValue = Quaternion.AngleAxis (ramurăAngle, axă); vertices.Add ((rotationValue * (bottomLeft)) + offset); vertices.Add ((rotationValue * (topleft)) + offset); vertices.Add ((rotationValue * (topRight)) + offset); vertices.Add ((rotationValue * (bottomRight)) + offset); 

O ramură este în esență o formă (sau a Quad) cu patru noduri de colț: stânga jos, stânga sus, sus în dreapta, și dreapta-jos. CreateBranch metoda face poziționarea corespunzătoare a ramurii prin traducerea, rotirea și scalarea acestor patru vârfuri pe baza formei, poziției și rotației ramurii. Vârful ramurii este conic cu ajutorul widthDecreaseFactor valoare. Metoda principală arbore poate apela această metodă în timp ce trece în poziția și valorile de rotație pentru acea ramură.

Arbore fractal

FractalTreeProper clasa are un caracter recursiv CreateBranch , care la rândul său va crea ramură clasa lui CreateBranch constructor.

void privat CreateBranch (int currentLayer, Vector3 branchOffset, unghi de flotare, int baseVertexPointer) if (currentLayer> = numLayers) retur; float length = trunkLength; float width = trunkBaseWidth; pentru (int i = 0; i

Fiecare apel la CreateBranch inițiază două apeluri noi pentru sine pentru cele două ramuri ale copilului său. Pentru exemplul nostru, folosim un unghi de ramificare de 30 de grade și o valoare de 8 ca numărul de iterații de ramificație.

Utilizăm punctele din aceste ramuri pentru a crea nodurile necesare, care apoi sunt folosite pentru a crea fețe pentru ochiurile noastre de copaci.

fețe = listă nouă(); vertices = new List(); fTree = GetComponent().plasă; fTree.name = "copac fractal"; // ... (în CreateBranch) dacă (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);  altfel int vertexPointer = vertices.Count; vertices.Add (branch.vertices [1]); vertices.Add (branch.vertices [2]); int indexDelta = 3; dacă (curentLayer! = 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 ();

baseVertexPointer Valoarea este folosită pentru reutilizarea vârfurilor existente, astfel încât să evităm crearea de vârfuri duplicate, deoarece fiecare ramură poate avea patru vârfuri, dar numai două dintre acestea sunt altele noi.

Prin adăugarea unor nivele aleatorii la unghiul de ramificație, putem crea variante asimetrice ale copacului nostru fractal, care pot arăta mai realiste.

3. Crearea unui arbore L-sistem

A doua metodă, sistemul L, este o fiară complet diferită. Este un sistem foarte complicat care poate fi folosit pentru a crea forme organice complicate sau pentru a crea seturi de reguli complexe sau secvențe de șir. Este vorba despre sistemul Lindenmayer, detaliile cărora pot fi găsite pe Wikipedia.

Aplicațiile sistemelor L includ robotică și AI și vom atinge doar vârful aisbergului în timp ce îl folosim în scopurile noastre. Cu un sistem L, este posibil să se creeze manual forme de copac sau arbale foarte realist, cu control precis sau cu ajutorul automatizării.

ramură componenta rămâne aceeași ca în exemplul fractal, dar modul în care vom crea ramuri se va schimba.

Disecarea sistemului L

L-sistemele sunt folosite pentru a crea fracturi complicate unde modelele nu sunt ușor de evidențiat. Se face imposibil din punct de vedere uman să găsiți aceste modele repetate vizual, însă sistemele L o fac mai ușor să le creați programabil. Sistemele L constau dintr-un set de alfabete care se combină pentru a forma un șir, împreună cu un set de reguli care mută aceste șiruri într-o singură iterație. Aplicarea acestor reguli pe mai multe iterații creează un șir lung, complicat, care poate servi drept bază pentru crearea arborelui nostru.

Alfabetul

Pentru exemplul nostru, vom folosi acest alfabet pentru a crea șirul de arborescentă: F+-[, și ].

Regulile

Pentru exemplul nostru, vom avea nevoie doar de o singură regulă în cazul în care alfabetul F se transformă într-o secvență de alfabete, să zicem F + [+ FF-F-FF] - [- FF + F + F]. La fiecare iterație, vom face acest schimb, menținând neschimbate toate celelalte alfabete.

Axiomul

Axiomul sau șirul de pornire vor fi F. Acest lucru înseamnă în mod esențial că, după prima iterație, șirul va deveni F + [+ FF-F-FF] - [- FF + F + F].

Vom itera de trei ori pentru a crea un șir de copaci utilizabil după cum se arată mai jos.

lString = "F"; reguli = noul dicționar(); reguli [ "F"] = "F + [+ FF-F-FF] - [- FF + F + F]"; pentru (int i = 0; i

Parsarea arborelui copac

Acum că avem șirul de arbori folosind sistemul L, trebuie să îl analizăm pentru a crea arborele nostru. Vom analiza fiecare caracter din arborele copac și vom face anumite acțiuni bazate pe ele, așa cum sunt enumerate mai jos.

  • La constatare F, vom crea o ramificație cu parametrii actuali de lungime și rotație.
  • La constatare +, vom adăuga la valoarea curentă de rotație.
  • La constatare -, vom scădea din valoarea curentă de rotație.
  • La constatare [, vom stoca valoarea curentă a poziției, lungimea și rotația.
  • La constatare ], vom restabili valorile de mai sus din starea stocată.

Utilizăm o valoare a unghiului de 25 grade pentru rotația ramurilor pentru exemplul nostru. CreateTree metodă în LSystemTree clasa face parsarea. Pentru stocarea și restaurarea stărilor, vom folosi a LevelState clasa care stochează valorile necesare împreună cu a BranchState struct.

levelStates = Listă nouă(); caracterul char [] chars = lString.ToCharArray (); float currentRotation = 0; float currentLength = startLength; float curentWidth = startWidth; Vector3 curentPosition = treeOrigin; int levelIndex = 0; NivelState nivelState = Nivel nou (); levelState.position = currentPosition; levelState.levelIndex = levelIndex; levelState.width = currentWidth; levelState.length = currentLength; levelState.rotation = currentRotation; levelState.logicBranches = Listă nouă(); levelStates.Add (levelState); Vector3 tipPosition = Vector3 nou (); Coadă savedStates = noua coadă(); pentru (int i = 0; i(); levelStates.Add (levelState); currentLength * = lengthDecreaseFactor; pauză; cazul "+": currentRotation + = unghiul; pauză; cazul '-': currentRotation- = unghiul; pauză; caz "[': savedStates.Enqueue (nivelState); pauză; cazul ']': levelState = savedStates.Dequeue (); currentPosition = levelState.position; currentRotation = levelState.rotation; currentLength = levelState.length; currentWidth = levelState.width; levelIndex = levelState.levelIndex; pauză; 

Variabila levelStates stochează o listă de LevelState în care un nivel poate fi considerat un punct de ramificare. Deoarece fiecare punct de ramificație poate avea mai multe ramuri sau o singură ramură, vom stoca aceste ramuri într-o listă logicBranches care deține multiple BranchState instanțe. savedStates Coadă urmărește stocarea și restaurarea diferitelor LevelStates. Odată ce avem structura logică pentru copacul nostru, putem folosi levelStates pentru a crea ramuri vizuale și a crea o plasă de copac.

pentru (int i = 0; i

GetClosestVextexIndices metoda este utilizată pentru a găsi vârfuri comune pentru a evita duplicatele în crearea rețelei.

Prin modificarea ușoară a regulilor, putem obține drastice diferite structuri de copaci, așa cum se arată în imaginea de mai jos.

Este posibilă crearea manuală a șirului de arbori pentru proiectarea unui anumit tip de copac, deși aceasta poate fi o sarcină foarte obositoare.

Concluzie

Redarea cu sistemele L poate fi distractivă și poate duce la rezultate foarte imprevizibile. Încercați să modificați regulile sau să adăugați mai multe reguli pentru a crea alte forme complicate. Următorul lucru logic ar fi să încercați să extindeți acest aspect la spațiul 3D înlocuind aceste 2D Quad cu cilindri 3D pentru ramuri și adăugând dimensiunea Z pentru branșare.

Dacă sunteți serios în ceea ce privește crearea copacilor 2D, aș sugera că vă uitați în algoritmii de colonizare spațială ca următorul pas.