#3 Construire une prison à la souris, et des pièces qui se trouvent toutes seules
· Nick
Le deuxième jalon est terminé, et pour la première fois il y a quelque chose à voir. Le client Godot repose désormais sur le noyau de simulation, et l’on peut construire une prison de plain-pied à la souris : poser des sols, tracer des murs, placer des portes, délimiter des pièces, démolir. Tout n’est que cubes simples et couleurs unies, et il n’y a encore personne dedans. Mais c’est un lieu, et un lieu autour duquel on peut promener la caméra.
Ce qu’on peut faire maintenant
On lance une nouvelle partie, on choisit une taille de carte (64, 128 ou 256 tuiles de côté) et une graine facultative, et on obtient une grille vide. La caméra est orthographique, avec une inclinaison fixe. Elle tourne par pas stricts de 90°, zoome vers le curseur, se déplace au clavier ou au bouton du milieu, et dispose d’une vue de dessus pour construire. Avec une caméra libre, les murs cacheraient les pièces, donc elle ne tourne pas librement.
Les sols et la démolition se font par rectangle. Les murs suivent un contour (ou une ligne droite si l’on maintient Maj). Une porte, c’est un clic, et elle choisit son orientation d’après les murs voisins, avec une touche pour la faire pivoter. Pendant qu’on fait glisser, la zone sous le curseur s’affiche en fantôme : les tuiles vertes seront construites, les rouges ignorées, et une étiquette indique combien et pourquoi. Rien n’est envoyé au monde avant qu’on relâche, et si le noyau répond qu’aucune tuile ne changerait, rien n’est envoyé du tout. Clic droit ou Échap annule.
Le modèle du monde comporte plusieurs étages, il y a donc un sélecteur d’étage, et les étages au-dessus de l’étage courant sont masqués. Les pièces ont leur propre outil, présenté plus bas. Une surcouche de débogage montre les bordures de chunks, les tuiles praticables, la tuile sous le curseur et un panneau de temps. Chaque mot de l’interface est en anglais.
L’aperçu, c’est le vrai résultat
Un fantôme qui contredit le résultat est pire que pas de fantôme du tout. Le client ne devine donc pas ce que fera une commande de construction. Le noyau sait répondre à « que se passerait-il si on exécutait ceci » pour la même zone, tuile par tuile, avec une raison pour chaque tuile qu’il refuserait. La vérification qui remplit l’aperçu est la même règle que celle qu’utilise la commande quand elle s’exécute vraiment, si bien que les deux ne peuvent pas diverger. Le client se contente de dessiner la réponse. Il ne réinterroge le noyau que lorsque quelque chose qui influe sur la réponse a changé : l’outil, la zone glissée, l’étage ou la carte elle-même.
Le même principe façonne le reste du client : il lit l’état du monde et envoie des commandes, sans logique de simulation propre. Pour l’instant, le noyau tourne sur le thread principal, et une commande issue d’un clic est appliquée au début de l’image suivante, avant le tick du monde. Si les temps de tick se mettent un jour à grimper quand le monde sera rempli de monde, le plan est de déplacer le noyau sur son propre thread, avec un seuil mesuré pour décider quand.
Des pièces qui se trouvent toutes seules
J’ai étudié deux façons de créer des pièces. Prison Architect vous laisse dessiner une zone, qui devient alors un objet à part avec ses propres tuiles. C’est facile à comprendre, mais ça tourne mal dans une prison : un bloc cellulaire entier serait une seule zone, et après avoir reconstruit un mur, il faut la repeindre. L’autre façon consiste à trouver automatiquement les zones fermées, mais on ne peut alors pas délimiter une cour sans murs autour.
J’ai choisi un mélange. La seule chose stockée est un type de pièce sur chaque tuile : cellule, cantine, douches, infirmerie, parloir, cour de promenade, et ainsi de suite. Tout le reste se déduit des murs. Un espace est un groupe de tuiles ouvertes reliées par leurs côtés, sur un même niveau, et il est fermé s’il ne touche ni le bord de la carte ni un vide. Une pièce, ce sont toutes les tuiles d’un même type à l’intérieur d’un même espace. Les murs et les portes sont des frontières, et un type peint sous un mur est conservé mais ignoré, si bien qu’il revient quand on abat le mur.
Cela donne un comportement qui paraît juste sans qu’on ait à l’expliquer. Faites passer un mur à travers une pièce, et elle devient deux pièces du même type. Retirez le mur, et elles fusionnent. Mettez une porte là où se trouvait un mur, et rien ne change. Une pièce invalide n’est pas supprimée, ce serait le contraire de « sans douleur » ; elle s’affiche en rouge avec ses raisons : non fermée, partagée avec un autre type, trop petite ou sans porte. On peut peindre un rectangle, remplir d’un clic tout un espace fermé, ou l’effacer.
La liste des pièces est une donnée dérivée : elle n’est ni sauvegardée ni incluse dans le hash du monde ; seul le type de chaque tuile l’est. Elle est reconstruite après les commandes et après les systèmes, et vérifier un monde inchangé ne coûte rien. Dans ce jalon, le niveau modifié est recalculé en entier, ce qui prend environ une demi-milliseconde sur une carte de 256×256 ; le calcul chunk par chunk peut attendre que les régions de cheminement aient besoin de la même mécanique. La règle que je m’impose : une reconstruction incrémentale doit donner le même résultat qu’une reconstruction complète.
Ne reconstruire que ce qui a changé, découper les étages gratuitement
La carte est dessinée sous forme d’un maillage par chunk et par niveau, et non d’un objet par mur. Chaque chunk a un compteur de version dans le noyau, et le moteur de rendu retient la dernière version qu’il a dessinée. Quand le monde change, seuls les chunks d’une version plus récente entrent dans une file, et cette file est traitée avec un budget de temps par image : un énorme changement s’étale ainsi sur quelques images au lieu d’en figer une. Peindre un type de pièce ne fait pas avancer la version de la carte, donc cela ne réveille jamais le constructeur de maillages.
La découpe des étages suit la même idée d’en faire moins. Les étages au-dessus de l’étage courant sont simplement masqués. Les murs de l’étage courant sont ramenés à un mètre par un shader, avec un seul matériau partagé et une seule valeur de hauteur, si bien que changer d’étage ou basculer en murs pleins ne reconstruit rien. Passer en revue quatre niveaux ne déclenche aucune reconstruction de maillage.
Un écart de dix centimètres
Quand j’ai essayé la première prison de plain-pied lors de la vérification finale, il y avait un sol au niveau supérieur, et de côté on voyait une fine ligne claire entre le haut d’un mur et la dalle au-dessus. À un angle, on voyait le sol flotter dans les airs. J’ai soupçonné la découpe des étages, puis le z-fighting, puis le lissage.
Rien de tout cela. Le sol était un simple quadrilatère plat sans épaisseur, dessiné à la hauteur de son niveau plus dix centimètres. Les murs du niveau inférieur montaient exactement à trois mètres. Le sol du niveau suivant commençait à trois mètres dix. L’écart faisait dix centimètres, et à travers lui on voyait la face supérieure éclairée du mur. Horizontalement, tout s’alignait parfaitement, c’est pourquoi il a fallu un moment pour le voir.
La correction : un sol est désormais une dalle d’une vraie épaisseur, qui descend jusqu’au début de son niveau. Le dessus reste à la même hauteur, donc la sélection des tuiles à la souris et toutes les surcouches sont inchangées. Les faces latérales sont omises là où un sol ou un mur se touchent dans le même chunk et dessinées aux bordures de chunks, si bien que le maillage d’un chunk ne dépend jamais de ses voisins. Il reste un bord sombre de dix centimètres au bout de la dalle. J’ai regardé des captures d’écran avant et après, et j’ai décidé de le garder. Il se lit comme le bord d’un sol, ce qu’il est.
Est-ce que ça tient ?
La dernière tâche du jalon était de construire une prison de plain-pied à la souris et de voir si c’était pénible. Globalement, non. En plus de l’écart ci-dessus, l’essai n’a trouvé que des petites choses agaçantes : les boutons d’outils restaient en surbrillance quand on choisissait un autre outil à la souris, et il n’y avait aucun moyen de quitter la boîte de dialogue New Game avec Échap. Les deux sont corrigés. Un menu pause est sur la liste des idées pour plus tard.
La suite
Ensuite vient M2, le déplacement et la routine quotidienne. Le monde reçoit des régions et l’accessibilité, une recherche de chemin qui fonctionne d’un étage à l’autre, et des champs de flux pour les foules. Le noyau a enfin du vrai travail à chaque tick, donc le seuil du banc d’essai va cesser de dormir. Voir la feuille de route.