Tuesday, May 07, 2013

One must FAIL.

I've been investigating that "stuck in walls" bug for some times now, with little luck to fix it. I'm finally reaching the conclusion that this is not an usual bug, but the bitter outcome of some short-coming in the current implementation of the game engine.

The behaviour of Bilou and NPCs in the game is ruled by some iGobController classes, such as DPAD processing, momentum, gravity and the like. At each frame, each of them think() to evaluate the current game state and adapt the character's state accordingly (e.g. make it fall faster in the case of gravity controller).

The outcome of this "think()" function in turn defines whether some transitions on the state machine should be evaluated. It may either be NONE (no transition involve, we happily stay in the current state), EVENT (something special happened, like the direction reverting or some key being pressed, for which an change in the animation might be desirable), and FAIL (impossible to hold the current state for longer, e.g. we've landed on the ground, there's no further falling possible).

J'ai cherché la cause de ce bug 'planté-dans-le-mur' si longtemps que j'en arrive à me dire que ce n'est pas un 'bug' au sens propre, mais le résultat malencontreux d'un moteur de jeu encore incomplet. Vous vous souvenez du code de gestion découpé en 'contrôleurs' que l'on enchaîne ?Parmi les signaux qu'un contrôleur peut émettre, il y a 'FAIL', indiquant que vu le déplacement demandé, le contrôleur actuel ne peut plus assurer le contrôle. C'est le cas notamment si on atteint le sol alors qu'on était sous la gestion de GravityController.

C'était un bon départ, mais pour construire des choses plus intéressante (des rebonds, notamment), il m'a fallu prendre en charge la vitesse d'impact, et aligner sur un mur/sol plutôt que d'annuler purement et simplement le mouvement en cas de FAIL. Mais à l'heure actuelle, mon code va parfois supprimer toute vitesse quand il aurait fallu en garder, et garder de la vitesse quand il aurait fallu la supprimer. Je vais donc devoir redéfinir la sémantique de FAIL: plus moyen de continuer avec ce contôleur, mais sans que ça ne signifie nécessairement qu'il n'y a aucun mouvement à cette frame-ci.

Initially, when Bilou FAILed to fall, it usually meant that the next move would take him into a wall, and so the move itself was cancelled. Yet it proved to be impractical alone. I had need for the impact speed so that e.g. I could decide to make Bilou bounce if the speed was high enough. Then I had need for aligning the move to the closest wall, and not simply cancel it.

All in all, the remaining code is killing speed where it should better not, and obviously not killing it where it would have to. That smells like some legacy code that need to be re-thought from cleaner base.
[done] FAIL => can't do more than this frame in this state. (but move isn't necessarily cancelled)
[done] no longer sticks on walls
[todo]  hspeed is reset to 0 when Bilou leaves a platform. That should only be the case if forced so by a GobExpression
[think] smoother slope-to-fall transition required.

No comments: