Créer des formes organiques comme des arbres peut être un projet parallèle intéressant pour les développeurs de jeux potentiels. Vous pouvez utiliser la même logique pour créer des niveaux ou d'autres structures logiques compliquées. Dans ce didacticiel, nous allons créer des formes d'arbre 2D dans Unity en utilisant deux approches différentes: Fractal et L-System..
Bien que nous appelions ces formes d'arborescence 2D, il s'agit essentiellement d'objets de maillage 3D dans Unity. L'objet de jeu qui contient le script d'arborescence devra avoir ces composants de maillage 3D liés afin de créer notre forme d'arborescence. Ces composants sont les MeshRenderer
et le MeshFilter
, comme indiqué ci-dessous.
Avec ces composants attachés, nous allons créer un nouveau maillage en utilisant les différents algorithmes pour fractals et L-Systems..
Un maillage 3D est créé en utilisant plusieurs sommets qui se combinent pour former des faces. Pour créer une seule face, nous aurons besoin d'un minimum de trois sommets. Lorsque nous connectons trois sommets dans le sens des aiguilles d'une montre, nous obtenons une face dont l'orientation normale est dirigée vers l'extérieur. La visibilité d'une face dépend de la direction de sa normale, et donc de la séquence dans laquelle les sommets sont passés pour créer une face qui compte. Veuillez lire la documentation officielle de Unity pour plus de détails concernant la création d’un maillage..
La puissance de la création de maillage à notre actif, passons à notre première méthode pour créer un arbre 2D: fractal.
Une fractale est une forme créée en répétant un motif d'échelles variables. Théoriquement, une fractale peut être un motif sans fin, où le motif de base est répété indéfiniment alors que sa taille est réduite progressivement. Lorsqu'il s'agit d'un arbre, le motif fractal de base peut être une branche se scindant en deux branches. Ce motif de base peut être répété pour créer la forme en arbre symétrique présentée ci-dessous..
Nous devrons arrêter la répétition après un certain nombre d'itérations, et le résultat n'est évidemment pas une forme d'arbre très réaliste. Pourtant, la beauté de cette approche (et des fractales en général) réside dans le fait qu’elles peuvent être facilement créées à l’aide de simples fonctions récursives. La méthode de dessin du modèle de base peut s'appeler de manière récursive tout en réduisant l'échelle jusqu'à ce qu'un certain nombre d'itérations soit terminé.
Le composant principal dans une forme d'arborescence est une branche. Dans notre approche, nous avons un Branche
classe, qui a un CreateBranch
méthode comme indiqué ci-dessous.
CreateBranch dans le vide privé (origine Vector3, float brancheLength, float brancheWidth, float brancheAngle, offset Vector3, float widthDecreaseFactor) Vector3 bottomLeft = new Vector3 (origin.x, origin.y, origin.z), bottomRight = new Vector3 (origin.x , origin.y, origin.z), topLeft = nouveau Vector3 (origin.x, origin.y, origin.z), topRight = nouveau Vector3 (origine.x, origin.y, origin.z); bottomLeft.x- = branchWidth * 0.5f; bottomRight.x + = branchWidth * 0.5f; topLeft.y = topRight.y = origine.y + branchLength; float newWidth = branchWidth * widthDecreaseFactor; topLeft.x- = newWidth * 0.5f; topRight.x + = newWidth * 0.5f; Vector3 axis = Vector3.back; Quaternion rotationValue = Quaternion.AngleAxis (branchAngle, axis); sommets.Add ((rotationValue * (bottomLeft)) + offset); sommets.Add ((rotationValue * (topLeft)) + offset); sommets.Add ((rotationValue * (topRight)) + offset); sommets.Add ((rotationValue * (bottomRight)) + offset);
Une branche est essentiellement une forme (ou une Quad
) avec quatre sommets de coin: en bas à gauche
, en haut à gauche
, en haut à droite
, et en bas à droite
. le CreateBranch
Cette méthode permet de positionner correctement la branche en effectuant une translation, une rotation et une mise à l’échelle de ces quatre sommets en fonction de la forme, de la position et de la rotation de la branche. La pointe de la branche est effilée à l’aide du bouton widthDecreaseFactor
valeur. La méthode main tree peut appeler cette méthode en transmettant les valeurs de position et de rotation pour cette branche..
le FractalTreeProper
la classe a un récursif CreateBranch
méthode, qui à son tour créera la Branche
la classe CreateBranch
méthode constructeur.
CreateBranch (void privé, int currentLayer, Vector3 branchOffset, angle flottant, int baseVertexPointer) if (currentLayer> = numLayers) return; float length = trunkLength; float width = trunkBaseWidth; pour (int i = 0; iChaque appel à
CreateBranch
initie deux nouveaux appels à lui-même pour ses deux branches enfants. Pour notre exemple, nous utilisons un angle de branchement de 30 degrés et une valeur de 8 comme nombre d'itérations de branchement..Nous utilisons les points de ces branches pour créer les sommets nécessaires, qui sont ensuite utilisés pour créer des faces pour notre maille d’arbre..
faces = nouvelle liste(); sommets = nouvelle liste (); fTree = GetComponent ().engrener; fTree.name = "arbre fractal"; //… (dans 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; sommets.Add (branch.vertices [1]); sommets.Add (branch.vertices [2]); int indexDelta = 3; if (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 (); le
baseVertexPointer
La valeur est utilisée pour réutiliser les sommets existants afin d'éviter de créer des sommets en double, car chaque branche peut avoir quatre sommets, mais seulement deux d'entre eux sont nouveaux..En ajoutant un peu de hasard à l'angle de branchement, nous pouvons également créer des variantes asymétriques de notre arbre fractal, ce qui peut sembler plus réaliste..
3. Créer un arbre L-System
La deuxième méthode, le système L, est une bête totalement différente. C'est un système très compliqué qui peut être utilisé pour créer des formes organiques complexes ou pour créer des ensembles de règles complexes ou des séquences de chaînes. Il est synonyme de système Lindenmayer, dont les détails peuvent être trouvés sur Wikipedia.
Les applications des systèmes L incluent la robotique et l'IA, et nous ne toucherons que la partie visible de l'iceberg lorsque nous l'utilisons à nos fins. Avec un système en L, il est possible de créer manuellement des formes très réalistes d'arbres ou d'arbustes avec un contrôle précis ou en utilisant l'automatisation..
le
Branche
composant reste le même que dans l'exemple fractal, mais la façon dont nous créons des branches va changer.Dissection du système L
Les systèmes L sont utilisés pour créer des fractales complexes où les motifs ne sont pas facilement évidents. Il devient humainement impossible de trouver visuellement ces motifs répétitifs, mais les systèmes L facilitent leur création par programmation. Les systèmes-L consistent en un ensemble d’alphabets qui se combinent pour former une chaîne, ainsi qu’un ensemble de règles qui muent ces chaînes en une seule itération. L’application de ces règles sur plusieurs itérations crée une chaîne longue et compliquée qui peut servir de base à la création de notre arborescence..
Les alphabets
Pour notre exemple, nous allons utiliser cet alphabet pour créer notre chaîne d’arbre:
F
,+
,-
,[
, et]
.Les règles
Pour notre exemple, nous n’aurons besoin que d’une règle où l’alphabet
F
se transforme en une suite d’alphabets, disonsF + [+ FF-F-FF] - [- FF + F + F]
. A chaque itération, nous effectuerons cet échange en conservant tous les autres alphabets..L'axiome
L'axiome, ou la chaîne de départ, sera
F
. Cela signifie essentiellement qu'après la première itération, la chaîne deviendraF + [+ FF-F-FF] - [- FF + F + F]
.Nous allons itérer trois fois pour créer une chaîne d'arborescence utilisable comme indiqué ci-dessous.
lString = "F"; rules = nouveau dictionnaire(); règles ["F"] = "F + [+ FF-F-FF] - [- FF + F + F]"; pour (int i = 0; i Analyser la chaîne d'arbre
Maintenant que la chaîne d'arborescence utilise le système L, nous devons l'analyser pour créer notre arborescence. Nous allons examiner chaque caractère de la chaîne et effectuer des actions spécifiques en fonction de celles-ci, comme indiqué ci-dessous..
- À trouver
F
, nous allons créer une branche avec les paramètres actuels de longueur et de rotation.- À trouver
+
, nous allons ajouter à la valeur de rotation actuelle.- À trouver
-
, nous allons soustraire de la valeur de rotation actuelle.- À trouver
[
, nous allons stocker la position actuelle, la longueur et la valeur de rotation.- À trouver
]
, nous allons restaurer les valeurs ci-dessus à partir de l'état stocké.Nous utilisons une valeur d'angle de
25
degrés pour la rotation de branche pour notre exemple. leCreateTree
méthode dans leLSystemTree
la classe fait l'analyse. Pour stocker et restaurer les états, nous utiliserons unLevelState
classe qui stocke les valeurs nécessaires avec unBranchState
struct.levelStates = nouvelle 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 = nouvelle liste (); levelStates.Add (levelState); Vector3 tipPosition = new Vector3 (); Queue savedStates = nouvelle file d'attente (); pour (int i = 0; i (); levelStates.Add (levelState); currentLength * = lengthDecreaseFactor; Pause; case '+': currentRotation + = angle; Pause; case '-': currentRotation- = angle; Pause; case '[': savedStates.Enqueue (levelState); Pause; case ']': levelState = savedStates.Dequeue (); currentPosition = levelState.position; currentRotation = levelState.rotation; currentLength = levelState.length; currentWidth = levelState.width; levelIndex = levelState.levelIndex; Pause; La variable
levelStates
stocke une liste deLevelState
cas, où un niveau peut être considéré comme un point de branchement. Chaque point de branchement pouvant avoir plusieurs branches ou une seule branche, nous les stockons dans une liste.logiquesBranches
tenant plusieursBranchState
les instances. leétat enregistré
Queue
suit le stockage et la restauration de différentsLevelState
s. Une fois que nous avons la structure logique de notre arbre en place, nous pouvons utiliser lelevelStates
liste pour créer des branches visuelles et créer le maillage de l'arbre.pour (int i = 0; i
Les GetClosestVextexIndices
méthode est utilisée pour trouver les sommets communs afin d'éviter les doublons lors de la création du maillage.En variant légèrement les règles, nous pouvons obtenir des arborescences radicalement différentes, comme le montre l'image ci-dessous..
Il est possible de créer manuellement la chaîne d'arborescence pour la conception d'un type d'arbre spécifique, bien que cela puisse être une tâche très fastidieuse..
Conclusion
Jouer avec les systèmes L peut être amusant et donner des résultats très imprévisibles. Essayez de modifier les règles ou d'ajouter d'autres règles pour créer d'autres formes compliquées. La prochaine chose à faire serait d'essayer d'étendre cela à l'espace 3D en remplaçant ces Quads 2D par des cylindres 3D pour les branches et en ajoutant la dimension Z pour les branches..
Si vous êtes sérieux au sujet de la création d'arbres 2D, je vous suggérerais de vous pencher sur les algorithmes de colonisation de l'espace à l'étape suivante..