Le jour où mes touches de debug m'ont menti
Aujourd’hui, grosse journée sur Eternal Champion. De celles où on commit le soir avec un message qui ressemble à une liste de courses. Mais s’il ne fallait retenir qu’une chose, ce serait celle-ci : mes touches de debug me mentaient depuis des semaines. Le run propre qui dit la vérité Pour tester vite, j’ai des touches magiques : Pratique. Trop pratique. Ce matin, pour la première fois depuis longtemps, j’ai joué la chaîne narrative de l’Étape 1 à l’Étape 2 sans toucher une seule touche de triche. Grotte, loups, boss, Lame, village, choix moral, et là… plus rien. Le deuxième boss, le Collecteur de Rancœur, ne spawnait jamais. Le coupable ? Une variable jamais nettoyée :
guard var boss = activeBoss, boss.isAlive else {
bossVisible = false
return // ← le boss MORT reste dans activeBoss... pour toujours
}
Quand un boss meurt pour de vrai, il restait stocké dans activeBoss avec isAlive = false. Et la condition de spawn du boss suivant exigeait activeBoss == nil. Verrou éternel. Pourquoi je ne l’avais jamais vu ? Parce que ma touche N faisait activeBoss = nil à ma place. Le debug nettoyait derrière le bug. Une fr`me après la mort, on libère le slot, et c’est réglé :
if activeBoss?.isAlive == false { activeBoss = nil }
Même run, deuxième révélation : après la mort du Boss, le dialogue de se jouait… et la transition vers l’Étape 3 ne partait jamais. Cette fois, c’était une double écriture de flag : skyRedForever était posé à la fois par le système de boss (à la mort) et par le moteur de jeu (à la fermeture du dialogue). Or ce flag servait de verrou one-shot à la transition — posé trop tôt par le premier, il tuait la garde du second. On tranche : un flag narratif appartient à la couche narrative. Le BossSystem constate les faits de combat, le monde réagit ailleurs. Leçon gravée dans le journal de bord : une validation qui fait autorité est un run propre. Les raccourcis de debug testent un chemin qui n’existe pas.
Des cristaux enfin collés à la paroi
Autre victoire du jour, plus géométrique. Mes cristaux de grotte flottaient au milieu du tunnel depuis leur création, malgré un raymarching censé les plaquer contre la roche. Le raymarching était bon. Sa condition était à l’envers :
// AVANT — ne marche jamais : videCreuse est NÉGATIF dans l'air
while it < 160, CaveSDF.videCreuse(p, s2) > 1.2 { p += n * 0.2; it += 1 }
// APRÈS — on marche dans l'air (négatif) jusqu'à 25 cm DANS la roche
while it < 160, CaveSDF.videCreuse(p, s2) < 0.25 { p += n * 0.2; it += 1 }
Mon champ de distance signé est négatif à l’intérieur du vide creusé et croise zéro à la paroi. La boucle attendait des valeurs supérieures à 1.2 en partant du centre de la salle, où le champ vaut environ −15 : elle ne faisait donc zéro itération, et chaque cristal restait posé à son point de départ, en l’air. Un signe. Des semaines de flottement pour un signe. Bonus : maintenant qu’ils sont incrustés, chaque cristal projette sa lumière colorée sur la paroi — dix petites lumières ponctuelles (position + couleur par minéral) passées dans les uniforms et appliquées dans le shader après l’assombrissement de la grotte, pour que la pénombre ne les éteigne jamais. J’ai poussé la pénombre au passage : une caverne doit être sombre pour que l’améthyste ait le droit de briller.
Une route peinte dans le shader
Dernier chantier du jour : guider le joueur du spawn jusqu’au village, à la Elden Ring — une route qu’on peut suivre, jamais qu’on doit suivre. Pas de mesh, pas de decals, pas d’assets : une polyligne de quatre points relevés en jeu, et une bande de terre battue calculée au fragment dans le shader de terrain. Distance du pixel au segment le plus proche, un smoothstep pour les bords, et le bruit macro déjà échantillonné pour casser la rectitude :
float wRoute = 1.0 - smoothstep(ROUTE_DEMI, ROUTE_DEMI + ROUTE_BORD,
dmin + (macro1 - 0.825) * 4.0);
wRoute *= (1.0 - wCliff) * (1.0 - u.caveFactor);
albedo = mix(albedo, albRoute, wRoute * 0.85);
Sept mètres de large, des bords qui ondulent, coupée net sur les falaises et dans les grottes. Coût : quelques opérations par pixel. Et pour déplacer la route un jour, je change quatre nombres.
Et le reste
En vrac dans le même commit : le sol de la grotte enfin aligné entre la physique et le visuel (un +1.2 vestigial relevait les pieds du joueur d’un bon mètre), l’assèchement global des flaques côtières avec préservation des vraies poches d’eau, la Source qui passe du sang à l’eau pure quand on en retire la Lame, et un rappel d’Elara qui vous renvoie gentiment vers la grotte si vous traînez près de l’Arbre sans avoir pris votre destinée en main. Prochaine bataille : la couture entre le trou du terrain et la coque de la grotte — la subdivision locale des quads a déjà bien resserré le trou, mais il reste quelques lambeaux à faire disparaître. Dans les calculs, pas avec des cailloux. À la prochaine session.