La Virgule Fixe

Mais qu'est ce que c'est donc ?

Méthode tombée en désuétude aujourd'hui, moi je la trouve toujours bien pratique et je l'utilise constament.

Sur les vieux 16/32 bits, on ne pouvait pas vraiment utilser des variables de type 'float' ou 'double' car c'était horriblement lent. Pour autant, si on avait du déplacer ses sprites avec uniquement des multiples de 1 pixel, on aurait été vite limité. Donc, on est passé par un subterfuge : la virgule fixe.

L'idée est très simple : Sur une donnée entière choisie, on va utiliser les x bits de poids faible de la variable pour représenter la partie décimale d'un nombre. Et tant qu'à faire, on utilisera toujours le même nombre de bits (dans 'virgule fixe', il y a 'fixe' !) pour que l'arithmétique basique fonctionne sur nos variables.

Explication par l'exemple

En application, c'est très simple. Imaginons qu'on veuille coder un star field en vue de côté. Si les étoiles se déplacent toutes à la même vitesse, bon ben, c'est un peu nul. Sans virgule fixe, on peut déplacer certaines étoiles 1 pixel par 1 pixel, et d'autres 2 par 2, mais ça ne fait pas beaucoup de variété.

Mettons en place la virgule fixe. Disons que le 'x' de chaque étoile soit codé sur un u32. On va dire par exemple (et complètement au hasard bien sûr) qu'on va utiliser 8 bits de virgule fixe pour la partie décimale.

Soit sur un u32 :
bits 31           8 7    0
     |____________| |____|
         24 bits    8 bits
           |          |
    Partie entière / Partie décimale   

Nous avons donc 8 bits pour la partie décimale, soit toutes les valeurs de 0 à 255. Si on veut ajouter 1 (donc à la partie entière), il va falloir ajouter 256 à notre variable. En hexa : 0x100. Si on veut ajouter 0.5, on va ajouter 128. En hexa : 0x80.

Et quand on voudra afficher une étoile à la position x, on éliminera la partie décimale avec un décalage : var >> 8.

Avec notre star field :

Une étoile a une vitesse de 1 pixel/frame, soit 0x100 en hexa :
u32 x = 0;
Itération 1 : x = x + 0x100; => x = $ 00 00 01 00 => Affichage en x>>8 : 1
Itération 2 : x = x + 0x100; => x = $ 00 00 02 00 => Affichage en x>>8 : 2
Itération 3 : x = x + 0x100; => x = $ 00 00 03 00 => Affichage en x>>8 : 3
etc...

Une étoile a une vitesse de 0,5 pixel/frame, soit 0x80 en hexa :
u32 x = 0;
Itération 1 : x = x + 0x80; => x = $ 00 00 00 80 => Affichage en x>>8 : 0
Itération 2 : x = x + 0x80; => x = $ 00 00 01 00 => Affichage en x>>8 : 1
Itération 3 : x = x + 0x80; => x = $ 00 00 01 80 => Affichage en x>>8 : 1
Itération 4 : x = x + 0x80; => x = $ 00 00 02 00 => Affichage en x>>8 : 2
etc...

Une étoile a une vitesse de 1,5 pixel/frame, soit 0x180 en hexa :
u32 x = 0;
Itération 1 : x = x + 0x180; => x = $ 00 00 01 80 => Affichage en x>>8 : 1
Itération 2 : x = x + 0x180; => x = $ 00 00 03 00 => Affichage en x>>8 : 3
Itération 3 : x = x + 0x180; => x = $ 00 00 04 80 => Affichage en x>>8 : 4
Itération 4 : x = x + 0x180; => x = $ 00 00 06 00 => Affichage en x>>8 : 6
Itération 5 : x = x + 0x180; => x = $ 00 00 07 80 => Affichage en x>>8 : 7
Itération 6 : x = x + 0x180; => x = $ 00 00 09 00 => Affichage en x>>8 : 9
etc...

J'ai déroulé un peu plus le dernier exemple exprès, car on y voit que de temps en temps, on va sauter un pixel. Alors là vous vous dites que ça va être moche parce que ça va se voir. Et bien non. Si vous avez bien régulé votre frame rate (voir le chapitre sur la VBL), le mouvement sera fluide.

Autre exemple pratique : Disons qu'on a un perso sur une map en vue de dessus. Comme nous sommes malins, nous avons pris une taille de blocs qui est un multiple d'une puissance de 2, au hasard 16 pixels * 16 pixels.

Nous avons vu au dessus que si nous avons 8 bits de virgule sur les variables x et y du perso, on aura sa position au pixel en faisant x>>8 et y>>8. Mais comme nous avons une taille de blocs maline, il y a un deuxième effet kiss cool : Pour coder les chiffres de 0 à 15, on a besoin de 4 bits. Ce qui veut dire qu'en décalant de 4 bits de plus, on a la position du perso en blocs : x>>12 et y>>12. De même, si on a besoin de savoir sur quelle ligne ou colonne d'un bloc on se trouve, il suffira de masquer pour ne garder que les bits 8 à 11 : colonne = (x >> 8) & 0x0F.

Et tout ça sur une seule variable, avec des opérations logiques qui ne consomment rien ! Oui Madame ! (^_^)

Home