Maak 2D-boomvormen met code

Wat je gaat creëren

Het maken van organische vormen zoals bomen kan een interessant nevenproject zijn voor potentiële game-ontwikkelaars. U kunt dezelfde logica gebruiken om niveaus of andere gecompliceerde logische structuren te maken. In deze zelfstudie maken we 2D-boomvormen in Unity met behulp van twee verschillende benaderingen: Fractal en L-System.

1. 2D creëren met 3D

Hoewel we deze 2D-boomvormen noemen, zijn het in wezen 3D-netobjecten in Unity. Het spelobject dat het tree-script heeft, moet deze 3D-mesh-componenten hebben om onze boomvorm te creëren. Die componenten zijn de MeshRenderer en de MeshFilter, zoals hieronder getoond.

Met deze componenten gekoppeld, zullen we een nieuwe mesh maken met behulp van de verschillende algoritmen voor fractals en L-Systems.

Een gaas maken

Er wordt een 3D-net gemaakt met behulp van meerdere hoekpunten die samen gezichten vormen. Om een ​​enkel gezicht te maken, hebben we minimaal drie hoekpunten nodig. Wanneer we drie hoekpunten met de klok mee verbinden, krijgen we een gezicht dat naar buiten wijst. De zichtbaarheid van een gezicht is afhankelijk van de richting van zijn normaal, en daarom is de volgorde waarin de hoekpunten worden gepasseerd om een ​​gezicht te maken van belang. Lees de officiële Unity-documentatie voor meer informatie over het maken van een mesh.

Laten we met de kracht van meshcreatie onder onze riem doorgaan naar onze eerste methode voor het maken van een 2D-tree: fractal.

2. Een fractalstructuur maken

Een fractal is een vorm die is gecreëerd door een patroon met verschillende schalen te herhalen. Theoretisch kan een fractal een oneindig patroon zijn, waarbij het basispatroon voor onbepaalde tijd wordt herhaald, terwijl de grootte ervan geleidelijk afneemt. Als het om een ​​boom gaat, kan het fractale basispatroon een tak zijn die zich splitst in twee takken. Dit basispatroon kan worden herhaald om de hieronder getoonde symmetrische boomvorm te creëren.

We moeten de herhaling na een bepaald aantal iteraties stoppen en het resultaat is duidelijk niet een erg realistische boomvorm. Maar de schoonheid van deze benadering - en fractals in het algemeen - is dat ze gemakkelijk kunnen worden gemaakt met behulp van eenvoudige recursieve functies. De basispatroon-tekenmethode kan zichzelf recursief noemen terwijl de schaal wordt verkleind totdat een bepaald aantal iteraties is voltooid.

De tak

Het primaire onderdeel in een boomvorm is een tak. In onze benadering hebben we een Tak klasse, die een heeft CreateBranch methode zoals hieronder getoond.

private void CreateBranch (Vector3 origin, float branchLength, float branchWidth, float branchAngle, Vector3 offset, float widthDecreaseFactor) Vector3 bottomLeft = new Vector3 (origin.x, origin.y, origin.z), bottomRight = new Vector3 (origin.x , origin.y, origin.z), topLeft = new Vector3 (origin.x, origin.y, origin.z), topRight = new 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-as = Vector3.back; Quaternion rotationValue = Quaternion.AngleAxis (branch Angle, axis); vertices.Add ((rotationValue * (bottomleft)) + offset); vertices.Add ((rotationValue * (topLeft)) + offset); vertices.Add ((rotationValue * (topright)) + offset); vertices.Add ((rotationValue * (bottomRight)) + offset); 

Een tak is in wezen een vorm (of een Quad) met vier hoekhoekpunten: linksonder, linksboven, rechtsboven, en rechts onder. De CreateBranch methode doet de juiste positionering van de tak door deze vier hoekpunten te vertalen, roteren en schalen op basis van de vorm, positie en rotatie van de tak. De punt van de tak loopt taps toe met behulp van de widthDecreaseFactor waarde. De hoofdboommethode kan deze methode aanroepen bij het doorgeven van de positie- en rotatiewaarden voor die tak.

Fractal Tree

De FractalTreeProper klas heeft een recursieve CreateBranch methode, die op zijn beurt de Tak klasse CreateBranch constructormethode.

private void CreateBranch (int currentLayer, Vector3 branchOffset, float angle, int baseVertexPointer) if (currentLayer> = numLayers) return; vlotterlengte = stamlengte; zwevende breedte = trunkBaseWidth; voor (int i = 0; i

Elke oproep aan CreateBranch initieert twee nieuwe oproepen naar zichzelf voor de twee onderliggende takken. Voor ons voorbeeld gebruiken we een vertakkingshoek van 30 graden en een waarde van 8 als het aantal vertakkingsiteraties.

We gebruiken de punten van deze takken om de benodigde hoekpunten te maken, die vervolgens worden gebruikt om vlakken te maken voor onze boomgaas.

gezichten = nieuwe lijst(); hoekpunten = nieuwe lijst(); fTree = GetComponent() .Mesh; fTree.name = "fractal tree"; // ... (in CreateBranch) if (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);  else int vertexPointer = vertices.Count; vertices.Add (branch.vertices [1]); vertices.Add (branch.vertices [2]); int indexDelta = 3; if (currentLayer! = 1) indexDelta = 2;  faces.Add (baseVertexPointer-index Delta); 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 waarde wordt gebruikt om bestaande hoekpunten opnieuw te gebruiken, zodat we geen dubbele hoekpunten maken, omdat elke tak vier hoekpunten kan hebben, maar slechts twee daarvan zijn nieuwe hoekpunten.

Door wat willekeur aan de vertakkingshoek toe te voegen, kunnen we ook asymmetrische varianten van onze fractale boom maken, die er realistischer uit kunnen zien.

3. Een L-systeemstructuur maken

De tweede methode, het L-systeem, is een heel ander beest. Het is een zeer ingewikkeld systeem dat kan worden gebruikt om ingewikkeld complexe organische vormen te maken of om complexe regelsets of reeksreeksen te maken. Het staat voor Lindenmayer System, waarvan de details te vinden zijn op Wikipedia.

De toepassingen van L-systemen omvatten robotica en AI, en we zullen alleen het topje van de ijsberg raken terwijl we het voor onze doeleinden gebruiken. Met een L-systeem is het mogelijk om zeer realistisch ogende boom- of heestervormen handmatig te creëren met precieze besturing of automatisering.

De Tak component blijft hetzelfde als in het fractal-voorbeeld, maar de manier waarop we takken maken, zal veranderen.

Het L-systeem ontleden

L-systemen worden gebruikt om gecompliceerde fractals te maken waar de patronen niet gemakkelijk zichtbaar zijn. Het wordt menselijk onmogelijk om deze herhalingspatronen visueel te vinden, maar L-systemen maken het eenvoudiger om ze programmatisch te maken. L-systemen bestaan ​​uit een reeks alfabetten die samen een reeks vormen, samen met een reeks regels die deze reeksen muteren in een enkele iteratie. Als u deze regels toepast op meerdere iteraties, wordt een lange, gecompliceerde reeks gemaakt die als basis kan dienen voor het maken van onze structuur.

De alfabetten

Voor ons voorbeeld gebruiken we dit alfabet om onze tree string te maken: F+-[, en ].

De regels

Voor ons voorbeeld hebben we maar één regel nodig waar het alfabet staat F verandert bijvoorbeeld in een reeks alfabetten F + [+ FF-F-FF] - [- FF + F + F]. Op elke iteratie maken we deze ruil terwijl alle andere alfabetten ongewijzigd blijven.

Het Axioma

Het axioma, of de startreeks, zal zijn F. Dit betekent in essentie dat na de eerste iteratie de string wordt F + [+ FF-F-FF] - [- FF + F + F].

We herhalen drie keer om een ​​bruikbare boomstructuur te maken, zoals hieronder weergegeven.

lString = "F"; rules = new Dictionary(); voorschriften [ "F"] = "F + [+ FF-F-FF] - [- FF + F + F]"; voor (int i = 0; i

De Tree String parseren

Nu we de boomstring met het L-systeem hebben, moeten we deze analyseren om onze boom te maken. We zullen elk karakter in de boomstring bekijken en specifieke acties uitvoeren op basis van hen zoals hieronder vermeld.

  • Over het vinden F, we zullen een tak creëren met huidige parameters van lengte en rotatie.
  • Over het vinden +, we zullen toevoegen aan de huidige rotatiewaarde.
  • Over het vinden -, we zullen aftrekken van de huidige rotatiewaarde.
  • Over het vinden [, we slaan de huidige positie, lengte en rotatiewaarde op.
  • Over het vinden ], we zullen de bovenstaande waarden herstellen vanuit de opgeslagen status.

We gebruiken een hoekwaarde van 25 graden voor takrotatie voor ons voorbeeld. De CreateTree methode in de LSystemTree klasse doet de ontleding. Voor het opslaan en herstellen van staten gebruiken we een LevelState klasse die de noodzakelijke waarden samen met een opslaat BranchState struct.

levelStates = nieuwe lijst(); char [] chars = lString.ToCharArray (); float currentRotation = 0; float currentLength = startLength; float currentWidth = startWidth; Vector3 currentPosition = treeOrigin; int levelIndex = 0; LevelState levelState = nieuwe LevelState (); levelState.position = currentPosition; levelState.levelIndex = levelIndex; levelState.width = currentWidth; levelState.length = currentLength; levelState.rotation = currentRotation; levelState.logicBranches = nieuwe lijst(); levelStates.Add (levelState); Vector3 tipPosition = new Vector3 (); Wachtrij savedStates = nieuwe wachtrij(); voor (int i = 0; i(); levelStates.Add (levelState); currentLength * = lengthDecreaseFactor; breken; case '+': currentRotation + = angle; breken; case '-': currentRotation- = hoek; breken; case '[': savedStates.Enqueue (levelState); breken; case ']': levelState = savedStates.Dequeue (); currentPosition = levelState.position; currentRotation = levelState.rotation; currentLength = levelState.length; currentWidth = levelState.width; levelIndex = levelState.levelIndex; breken; 

De variabele levelStates slaat een lijst op van LevelState gevallen waarin een niveau als een vertakkingspunt kan worden beschouwd. Omdat elk vertakkingspunt meerdere takken of slechts één tak kan hebben, slaan we die takken op in een lijst logicBranches meerdere vasthouden BranchState instances. De savedStates Wachtrij volgt het opslaan en herstellen van verschillende LevelStates. Zodra we de logische structuur voor onze boom op zijn plaats hebben, kunnen we de levelStates lijst om visuele vertakkingen te maken en de maas te maken.

voor (int i = 0; i

De GetClosestVextexIndices methode wordt gebruikt om gemeenschappelijke hoekpunten te vinden om duplicaten te voorkomen tijdens het maken van de mesh.

Door de regels enigszins te variëren, kunnen we drastisch verschillende boomstructuren krijgen, zoals in de onderstaande afbeelding.

Het is mogelijk om de boomstring handmatig te maken voor het ontwerpen van een specifieke soort boom, hoewel dit een zeer vervelende taak kan zijn.

Conclusie

Spelen met L-systemen kan leuk zijn en het kan ook tot zeer onvoorspelbare resultaten leiden. Probeer de regels te veranderen of meer regels toe te voegen om andere gecompliceerde vormen te maken. Het volgende logische ding om te doen zou zijn om dit uit te breiden naar 3D ruimte door deze 2D Quads te vervangen door 3D cilinders voor takken en de Z-dimensie toe te voegen voor vertakking.

Als je serieus bent over het maken van 2D-bomen, zou ik willen voorstellen dat je de volgende stap bekijkt in algoritmen voor ruimtekolonisatie.