PARTIE III: Structures Récursives

Maintenant que nous avons nos blocs élémentaires, que nous savons comment les empiler, il est grand temps de fabriquer des structures un peu plus complexes. Pour les plus impatients, vous pouvez télécharger le projet Unity3D, et le voir en action sur cette page.

Nous allons commencer par étoffer notre boite a outil, en ajoutant 2 méthodes à la classe MorphoModule:

  • AddChild (GameObject obj): pour ajouter un GameObject en tant que fils du gameObject local.
  • GameObject AddChild (string prefabname, string script): pour créer un GameObject, à partir du nom d’un prefab, lui ajouter un script (désigné par son nom), et l’ajouter en tant que fils du gameObject local.

Rappelons qu’un script, en tant que composant du GameObject, a accès à ce dernier, par le membre gameObject. Voici donc le fichier MorphoModule.cs complet:

[snippet id= »97″]

Nous pouvons nous consacrer à l’écriture du script  MorphoScript, et modéliser une première espèce de plantes.

Variable locales

Notre modèle de croissance, va être contrôlé par un ensemble de variables locales à notre script. Certaines seront publiques, d’autres privées. Voyons leur définition:

[snippet id= »98″]

  • level, maxlevel: Comme dans tout processus récursif, il faut définir quand s’arrête la boucle récursive. Ici, nous allons définir un niveau maximal de récursion. Pour cela, nous allons ajouter une variable ‘level’, au script, qui permettra a chaque organe de connaitre combien d’éléments le séparent de la racine, ainsi qu’une  variable ‘maxlevel‘, qui définit le niveau maximum.
  • inclin, phyllo: L’orientation des branches dans un arbre est le résultat de phénomènes mécaniques au niveau des bourgeons et apex. Nous allons le simuler en orientant chaque bloc par rapport à son parent, selon l’axe X, pour l’inclinaison (variable inclin) et l’axe Y, pour la phyllotaxie (phyllo).L’axe Z n’est pas nécessaire pour nous permettre de construire une structure arborescente en 3D. Les angles sont exprimés en degré.
  • childPrefabName, fruitPrefabName: contiennent le nom des prefabs utilisés. C’est assez pratique si on veut essayer d’autres prefabs, changer la forme des organes.

Ajouter un organe

Maintenant que nous avons défini nos variables, ajouter un organe va consister a:

  1. créer et ajouter un GameObject,
  2. le doter du script MorphoScript,
  3. lui affecter les mêmes valeurs de variables locales, à l’exception de « level« , qui sera augmenté de 1

Voici l’implémentation de la méthode addOrgan():

[snippet id= »99″]

Si ‘level‘ est égal à 0, nous avons affaire à la graine initiale. Etant donné que la forme de la graine est indifférente (ce n’est pas forcément une tige), son fils sera placé au même endroit que la graine (positon relative (0,0,0)). C’est la raison du test à la ligne 8.

Notez l’utilisation de Vector.up (0,1,0), qui au même titre que Vector.one (1,1,1) permettent d’alléger la syntaxe. Ici, on voit même la possibilité d’utiliser l’opérateur ‘multiplication’, comme à la ligne 9.

Croissance de l’organisme

Update() est une méthode appelée a chaque trame calculée dans Unity3D. C’est ici que nous allons implémenter la croissance de notre organisme, en ajoutant un ou plusieurs sous organes, si un ensemble de conditions sont réunies. Définissons les deux conditions suivantes comme préalables avant l’ajout d’un organe dans la hiérarchie:

  • l’organe n’a pas (déjà) de fils.
  • le niveau maximum de récursivité n’est pas atteint (level<maxlevel),

Si ces conditions sont réunies, nous créerons un ou plusieurs modules selon les règles suivantes:

  • si level est inférieur à 2, on ajoute un organe ‘tige’, sans inclinaison.
  • sinon, si le niveau est le niveau max, on ajoute un ‘fruit’
  • sinon, on ajoute 2 tiges, en tenant compte de l’inclinaison

La  première règle, (level<2), sert simplement à éviter d’avoir des embranchements dès la racine. La seconde crée un fruit aux extrémités des embranchements, et la dernière crée 2 branches, à 180 degrés l’une de l’autre. En C#, l’implémentation donne ceci:

[snippet id= »100″]

A ce stade, il est possible de créer un GameObject dans la scène (un cube par exemple), et lui assigner ‘MorphoScript‘, et lancer l’application… Si tout s’est bien déroulé, on obtient une plante comme celle ci:

Elagage

Comme on a placé le code dans Update(), si on venait à détruire une partie de la plante (dans la fenêtre d’édition de la scène), elle se mettrait à repousser aussitôt.

Avec Unity3D, il est possible de faire en sorte qu’en cliquant sur un élément de la plante, on détruise le dit élément, ainsi que tous ceux qui dépendent de lui hiérarchiquement. C’est même très simple, la preuve en est, il suffit d’ajouter la méthode OnMouseOver() suivante et de relancer l’application:

[snippet id= »103″]

Le cas où l’on clique sur la graine n’est pas géré ici, c’est laissé en exercice pour le lecteur. Le comportement souhaité étant que si on clique sur la graine, ses fils soient détruits. (indice: Il faudra utiliser les membres ChildCount et GetChild() de la classe Transform.)

Pour que les objets soient « cliquables », il faut qu’ils aient un composant Collider. Par exemple, dans le cas de la tige, c’est un Capsule Collider qui est utilisé, comme on peut le voir pour le prefab pour les tiges.

Variations

Il est possible de modifier les paramètres de la graine (inclinaison, etc.) et de relancer l’application pour commencer à explorer les formes possibles que l’on peut obtenir. C’est même le moment pour vous d’expérimenter un peu, en apportant vos propres modifications  au code.

Les structures obtenues étant très régulières, Il est possible de briser légèrement la régularité de la structure, en introduisant de l’aléatoire à différents endroits du code. Par exemple dans la méthode update, on pourra ajouter les quelques lignes suivantes, ce qui aura pour effet de ne pas systématiquement produire tous les embranchements:

[snippet id= »101″]

Ou encore, la méthode addOrgan() peut être modifiée, et ne pas se contenter de recopier la valeur des variables locales, on pourra modifier l’inclinaison, changer le facteur d’échelle, etc. N’hésitez pas à expérimenter! Quelques exemples: ici la taille des éléments va décroître à chaque étage de la plante:

[snippet id= »105″]

Ou ici quelques modifications sur l’inclinaison, la position du fils, etc:

[snippet id= »106″]

Bien entendu, il est possible d’enrichir le modèle, an ajoutant des règles, de paramètres, etc.

Mutations

Pour offrir un peu de variété dans notre application, ajoutons une méthode mutation(), qui configure aléatoirement les paramètres d’une plante. Par exemple, l’inclinaison peut être choisie au hasard dans l’intervalle 10 et 30 degrés.

[snippet id= »104″]

En appelant cette nouvelle méthode depuis MouseOver(), on pourra appliquer la mutation au parent du segment coupé.

[snippet id= »102″]

Il est possible alors d’obtenir des plantes avec des fleurs de différentes formes!

Conclusion

Nous avons vu qu’avec quelques paramètres il était possible de produire une assez grande diversité de formes, et qu’avec l’introduction d’aléatoire, il était possible d’explorer cet espace de formes. L’utilisation de la méthode Update() nous a permis de créer des structures toujours actives, prêtes à croître, et nous avons mis cela en évidence en proposant la possibilité de tailler nos plantes, et de les voir repousser immédiatement.

Cependant, la façon dont les structures apparaissent est bien trop rapide et abrupte: en quelques trames, la plante a atteint sa forme finale. Dans la partie suivante, nous verrons comment faire croître de façon progressive les plantes.