Modèles procéduraux que vous pouvez utiliser avec Tilemaps, partie 1

De nombreux créateurs ont utilisé la génération procédurale pour ajouter de la diversité à leur jeu. Parmi les mentions notables, on peut citer Minecraft, ou plus récemment Enter the Gungeon et Descenders. Cet article explique certains des algorithmes que vous pouvez utiliser avec Tilemap, introduit en tant que fonctionnalité 2D dans Unity 2017.2, et RuleTile.
Avec des cartes créées de manière procédurale, vous pouvez vous assurer qu'aucune partie de votre jeu n'est identique. Vous pouvez utiliser différentes entrées, telles que le temps ou le niveau actuel du joueur, pour garantir que le contenu change de manière dynamique même après la création du jeu.
Nous examinerons certaines des méthodes les plus courantes de création d’un monde procédural et quelques variantes personnalisées que j’ai créées. Voici un exemple de ce que vous pourrez peut-être créer après avoir lu cet article. Trois algorithmes fonctionnent ensemble pour créer une carte, en utilisant un Tilemap et un RuleTile:

Lorsque nous générons une carte avec l’un des algorithmes, nous recevrons un tableau int contenant toutes les nouvelles données. Nous pouvons ensuite prendre ces données et continuer à les modifier ou à les restituer sous forme de tuiles.
Bon à savoir avant de continuer à lire :
La façon dont nous distinguons ce qui est une tuile et ce qui n'en est pas est d'utiliser le binaire. 1 étant allumé et 0 étant éteint.
Nous stockerons toutes nos cartes dans un tableau d'entiers 2D, qui est renvoyé à l'utilisateur à la fin de chaque fonction (sauf lorsque nous effectuons le rendu).
J'utiliserai la fonction de tableau GetUpperBound() pour obtenir la hauteur et la largeur de chaque carte afin que nous ayons moins de variables entrant dans chaque fonction et un code plus propre.
J'utilise souvent Mathf.FloorToInt(), car le système de coordonnées Tilemap commence en bas à gauche et l'utilisation de Mathf.FloorToInt() nous permet d'arrondir les nombres à un entier.
Tout le code fourni dans cet article de blog est en C#.
GenerateArray crée un nouveau tableau int de la taille qui lui est donnée. Nous pouvons également dire si le tableau doit être plein ou vide (1 ou 0). Voici le code :
Type de bloc inconnu « codeBlock », veuillez spécifier un sérialiseur pour celui-ci dans la propriété « serializers.types »
Cette fonction est utilisée pour rendre notre carte sur le tilemap. Nous parcourons la largeur et la hauteur de la carte, en plaçant uniquement des tuiles si le tableau contient un 1 à l'emplacement que nous vérifions.
Type de bloc inconnu « codeBlock », veuillez spécifier un sérialiseur pour celui-ci dans la propriété « serializers.types »
Cette fonction est utilisée uniquement pour mettre à jour la carte, plutôt que de la restituer. De cette façon, nous pouvons utiliser moins de ressources car nous ne redessinons pas chaque tuile et ses données de tuile.
Type de bloc inconnu « codeBlock », veuillez spécifier un sérialiseur pour celui-ci dans la propriété « serializers.types »
Le bruit de Perlin peut être utilisé de différentes manières. La première façon dont nous pouvons l’utiliser est de créer une couche supérieure pour notre carte. C'est aussi simple que d'obtenir un nouveau point en utilisant notre position x actuelle et une graine.
Cette génération prend la forme la plus simple d'implémentation du bruit Perlin dans la génération de niveau. Nous pouvons utiliser la fonction Unity pour Perlin Noise pour nous aider, il n'y a donc pas de programmation sophistiquée à effectuer. Nous allons également nous assurer que nous avons des nombres entiers pour notre tilemap en utilisant la fonction Mathf.FloorToInt().
Type de bloc inconnu « codeBlock », veuillez spécifier un sérialiseur pour celui-ci dans la propriété « serializers.types »
Voici à quoi cela ressemble une fois rendu sur une carte en tuiles :

Nous pouvons également prendre cette fonction et la lisser. Définissez des intervalles pour enregistrer la hauteur de Perlin, puis lissez entre les points. Cette fonction finit par être légèrement plus avancée, car nous devons prendre en compte des listes d'entiers pour nos intervalles.
Type de bloc inconnu « codeBlock », veuillez spécifier un sérialiseur pour celui-ci dans la propriété « serializers.types »
Pour la première partie de cette fonction, nous vérifions d’abord si l’intervalle est supérieur à un. Si c’est le cas, nous générons alors le bruit. Nous le faisons à intervalles réguliers pour permettre le lissage. L’étape suivante consiste à lisser les points.
Type de bloc inconnu « codeBlock », veuillez spécifier un sérialiseur pour celui-ci dans la propriété « serializers.types »
Le lissage s'effectue selon les étapes suivantes :
- Obtenir la position actuelle et la dernière position
- Obtenez la différence entre les deux positions, l'information clé que nous voulons est la différence sur l'axe des y
- Ensuite, nous déterminons de combien nous devons modifier le coup, cela se fait en divisant la différence y par la variable d'intervalle.
Nous pouvons maintenant commencer à définir les positions. Nous allons travailler jusqu'à zéro
Lorsque nous atteignons 0 sur l'axe des Y, nous ajouterons le changement de hauteur à la hauteur actuelle et répéterons le processus pour la position x suivante.
Une fois que nous avons fait toutes les positions entre la dernière position et la position actuelle, nous passerons au point suivant
Si l'intervalle est inférieur à un, nous utilisons simplement la fonction précédente pour faire le travail à notre place.
Type de bloc inconnu « codeBlock », veuillez spécifier un sérialiseur pour celui-ci dans la propriété « serializers.types »
Voyons à quoi cela ressemble une fois rendu :

Le fonctionnement de cet algorithme est le suivant : on lance une pièce de monnaie. Nous obtenons alors l’un des deux résultats suivants. Si le résultat est pile, nous montons d'un bloc, si le résultat est pile, nous descendons d'un bloc. Cela crée une certaine hauteur à notre niveau en se déplaçant toujours vers le haut ou vers le bas. Le seul inconvénient de cet algorithme est qu’il semble très compact. Voyons comment cela fonctionne.
Type de bloc inconnu « codeBlock », veuillez spécifier un sérialiseur pour celui-ci dans la propriété « serializers.types »
Cette génération nous donne une hauteur plus douce par rapport à la génération de bruit Perlin.
Cette génération nous donne une hauteur plus douce par rapport à la génération de bruit Perlin.
Cette variante de Random Walk permet une finition beaucoup plus douce que la version précédente. Nous pouvons le faire en ajoutant deux nouvelles variables à notre fonction :
- La première variable est utilisée pour déterminer combien de temps nous avons maintenu notre taille actuelle. Il s'agit d'un entier et il est réinitialisé lorsque nous modifions la hauteur.
- La deuxième variable est une entrée pour la fonction et est utilisée comme largeur de section minimale pour la hauteur. Cela aura plus de sens lorsque vous aurez vu la fonction
Maintenant, nous savons ce que nous devons ajouter. Regardons la fonction :
Type de bloc inconnu « codeBlock », veuillez spécifier un sérialiseur pour celui-ci dans la propriété « serializers.types »
Comme vous pouvez le voir sur le gif ci-dessous, le lissage de l'algorithme de marche aléatoire permet d'obtenir de belles pièces plates dans le niveau.

J’espère que cela vous a inspiré à commencer à utiliser une forme de génération procédurale dans vos projets. Si vous souhaitez en savoir plus sur la génération procédurale de cartes, consultez le Wiki de génération procédurale ou Roguebasin.com, qui sont tous deux d'excellentes ressources.
Vous pouvez consulter le prochain article de la série pour voir comment nous pouvons utiliser la génération procédurale pour créer des systèmes de grottes.
Si vous créez quelque chose de cool en utilisant la génération procédurale, n'hésitez pas à m'envoyer un message sur Twitter ou à laisser un commentaire ci-dessous !
Vous souhaitez en savoir plus et obtenir une démonstration en direct ? Je parlerai également des modèles procéduraux à utiliser avec les Tilemaps à Unite Berlin, dans le mini-théâtre du hall d'exposition le 20 juin. Je serai là après la conférence si vous souhaitez discuter en personne !