Saturday, August 12, 2023

Swinging animations

Plus le design de Bilou: Dreamland avance, plus la possibilité de s'accrocher et se balancer prend de l'importance dans le gameplay envisagé. Et c'est tant mieux sauf pour une chose: mon moteur d'animations est actuellement très mal adapté pour ce genre de chose. Comprenez, on risquerait plus de se retrouver avec quelque-chose de raide comme Mickey Magical Quest plutôt que les mouvements souples de Fury of the Furries. Ce serait d'autant plus dommage que Spongebop ne souffre pas de ce genre de limitation et que ça rend furieusement bien. Mais voilà, l'image de Spongebop est indépendante de l'angle de sa corde.

The more I sketch some design ideas for Bilou: Dreamland, the more swinging with a rope-like object occurs. I won't claim it will be a primary game mechanic, but it will certainly be an important one. There's just one drawback : so far my game engine offers no support for that kind of animation. Oh, sure, I could go the Mickey Magical Quest way of hard-coding a few frames and the positions Bilou should take to swing, but given that I have a freely-swinging Spongebop in the previous game, I'd prefer go for something like what I've enjoyed in Fury of the Furries: game physics that allows 360° swinging, with free positioning anywhere on the circle defined by the current rope length and almost free extension / reduction of the rope length (Mickey can only climb).

One picture every 10° to swing a tiny
Pour Fury on a pas moins de 36 images différentes et la possibilité de faire un tour complet de son point d'accroche si la physique le permet, soit 10° entre chaque image. Mickey n'offre que 7 images différentes pour un angle de balancier de 90°, soit 15° entre chaque image. La différence devrait être légère (on passe quand-même de 60 fps à 40 fps avec un rapport pareil), mais là où ça change tout, c'est que les angles de la corde elle-même sont discrets à 15° près avec Mickey, dû au fait du hardware 'par tiles' de la super NES. Du coup, les positions de Mickey deviennent elle-même discrètes (bien qu'il puisse remonter sa corde). 

Je me suis donc repenché là-dessus pendant mes p'tites vacances, à l'abri dans mon épave de bateau pirate et j'ai peut-être bien une solution. L'idée serait de compléter les deux modes d'animation actuels (l'un qui suit une ligne du temps, l'autre qui colle à une série de déplacements) pour faire un système dans lequel on va choisir d'avancer ou non dans l'animation selon le vecteur-déplacement actuel. En gros, je fais une animation dans laquelle Bilou se balance autour de sa main et le moteur passera à l'image suivante quand Bilou va dans la même direction que celle suivie par cette image dans l'animation.

The summer idea about it was to handle that with a new way of playing back an animation: rather than enforcing a delay between two frames or a specific motion, we would compare the in-game speed vector (xg, yg) against inter-frame motion vectors (xa, ya). You'd then step to the next frame in the animation only if (xg, yg) is "beyond" the expected motion to the next frame. A bit of maths done in a sandbox (literally) shown that we can tell that just by two integer multiplications per frame. It will need separate animation for swing-to-the-left and swing-to-the-right, but I'm perfectly fine with that.

Mais comment savoir si un vecteur effectif (xg, yg) est plus proche de l'ancien vecteur de l'animation (xa, ya) ou de celui de l'étape suivante (xa', ya') ? Je suis parti de mon vieux cours d'analyse math avec "l'extrémité du nouveau vecteur (xg, yg) doit être au-dessus de la droite définie par k*xa' = k*ya' (que l'on peut réécrire y = (ya' / xa') * x). Mathématiquement parlant, on sera au-dessus si yg > ya' / xa' * xg. Programmatiquement parlant, c'est moins drôle, à cause des divisions par zéro dans les verticales et du manque de précision des divisions / multiplications quand on se contente de travailler avec des nombres entiers. J'ai donc voulu ruser et faire un peu d'algèbre pour passer à yg * xa' > ya' * xg, qui ne fait que des nombres plus grands que 1. En fait, je venais de retomber sur l'expression de l'aire signée d'un triangle, bon vieux truc du cours d'algo II à l'unif (merci, feu PAdM). ça tombe bien: elle sert justement à calculer si un point est à gauche ou à droite d'un vecteur donné.

Il faudra vérifier expérimentalement si je dois modifier le sens de l'inégalité pour le balancier-retour, trouver un moyen d'intégrer ça au scripts (probablement avec un 'speedmove' comme on a déjà un 'selfmove'). La bonne nouvelle, c'est que le même système pourrait probablement marcher aussi pour une voiture vue du haut, des segments de lianes qui se balancent et segments de ponts qui s'inclinent :-P

extra bonus with the anim.x * gob.y > anim.y * gob.x expression, is that a faster motion (e.g. longer rope) or a slower motion (e.g. shorter rope) will not affect things, because it would affect both gob.x and gob.y by an identical factor k and thus their effects will compensate in the comparison.

No comments: