Auteur : Saddik Amine
Chapitre 3 TD 3 - Génération Procédurale de Villes
Principes de base et mise en place dans Unity
Sur le plan théorique, la génération procédurale sert à créer du contenu automatiquement via des algorithmes plutôt que de tout placer à la main. Pour les villes, c’est super utile pour remplir de grands espaces. Les techniques classiques utilisent souvent des L-Systems pour tracer des routes cohérentes ou des automates cellulaires pour définir des quartiers. Mais ici, on s’est concentré sur l’approche de Müller et Parish qui utilise des cartes de densité.
Dans la pratique sur Unity, on a utilisé le bruit de Perlin d’une manière différente du TD précédent. Au lieu de s’en servir pour la hauteur du terrain, on s’en sert pour choisir quel bâtiment placer. En gros, on parcourt chaque point de notre plan, on récupère une valeur de bruit (entre 0 et 1), et on convertit cette valeur en un index entier pour piocher dans notre tableau de Prefabs.
J’ai utilisé ce genre de logique pour transformer la valeur floue du bruit en un choix précis de bâtiment :

Pour gérer la densité et la variété, il suffit de jouer sur la fréquence du bruit : une fréquence basse donne de grands quartiers homogènes, alors qu’une fréquence haute mélange tout de façon plus chaotique.
Intérêt de la méthode et gestion de l’altitude
L’intérêt majeur de cette technique, c’est le gain de temps et d’espace disque. On n’a pas besoin de sauvegarder la position de milliers de maisons, juste la “seed” (graine) et l’algo qui recrée tout au lancement. C’est ce qui permet aux jeux comme Minecraft ou No Man’s Sky d’être infinis.
Pour rendre ça plus intéressant niveau gameplay, on ne peut pas laisser la ville toute plate. L’astuce technique, c’est d’utiliser deux bruits de Perlin différents. Le premier décide “quel bâtiment je pose” (comme on a fait), et le second décide “à quelle hauteur je le pose”. Ça permet d’avoir des villes vallonnées. Ça change tout pour le joueur car ça crée des obstacles visuels et physiques.
Voici comment on pourrait combiner les deux couches dans le code :

Réalisme et cohérence des quartiers
Le problème avec le bruit de Perlin pur, c’est que c’est parfois un peu trop aléatoire pour une ville. On peut se retrouver avec une usine isolée au milieu d’un parc, ce qui n’est pas très réaliste. Les vraies villes suivent des logiques économiques et sociales, pas juste du bruit mathématique.
Pour corriger ça dans une approche procédurale, on pourrait ajouter une étape de vérification des voisins (type automate cellulaire). Avant de placer un bâtiment, on regarde ce qu’il y a autour. Si les voisins sont des zones résidentielles, on force le nouveau bâtiment à être résidentiel aussi, même si le bruit disait autre chose. Ça permettrait de “lisser” les quartiers pour qu’ils fassent plus vrais.
On pourrait imaginer une fonction de contrôle comme ça :

Optimisation et problèmes de latence
Enfin, quand on génère une ville dense, le gros souci technique c’est la performance. Si on utilise juste Instantiate pour chaque maison, le CPU va devoir envoyer des milliers de commandes au GPU (les Draw Calls), et le jeu va ramer. C’est le défi principal des jeux à monde ouvert.
Pour optimiser ça, il ne faut surtout pas garder des milliers d’objets séparés. La meilleure technique c’est le GPU Instancing. Ça permet de dire à la carte graphique : “Voici ce modèle de maison, affiche-le à ces 500 positions différentes”. On le fait en une seule passe au lieu de 500. On peut aussi utiliser l’Object Pooling pour éviter de créer/détruire les bâtiments quand le joueur avance, on se contente de déplacer ceux qui sortent de l’écran.
Pour l’instancing, ça ressemblerait plus à ça qu’à une boucle d’instantiate classique :
