La trigo basique

De bonnes valeurs

Rassurrez-vous, je ne vais pas vous faire un cours de maths. Je n'étais déjà pas bon à l'école, et l'école c'était il y a 15 ans, vous imaginez les dégats. Par contre, il y a quelques petits trucs que je connais et qui peuvent être utiles.

Il m'est arrivé de voir sur certains forums que certains se lançaient dans des calculs trigonométriques (sin et cos) avec des valeurs pas du tout arrangeantes. Exemple typique : Raisonner sur 360 degrés.

En effet, 360 degrés ce n'est pas du tout arrangeant. A commencer par le fait qu'à chaque fois qu'on va ajouter quelque chose à son angle, il va falloir tester si on est toujours dans l'intervalle [0;359]. Rien que de le dire, on se doute qu'il y a mieux.

Le mieux, c'est une fois encore de prendre une puissance de 2. Au hasard, 256. En voilà une bonne valeur. Avec celle là, on stocke son angle sur un 8 bits et on ne teste jamais rien, puisque la valeur sera dans tous les cas dans l'intervalle [0;255].

Si vous trouvez que 256 c'est trop peu (dites vous quand même que 95% des productions jeux/démo des années 90 utilisaient cette valeur), passez à 512. Il suffira de faire un & 0x1FF après chaque opération sur l'angle, ça limite quand même bien les dégats.

Sur 256 valeurs, on se retrouve avec un cercle trigonométrique qui ressemble à ceci :

Cercle trigonométrique

Et bien sûr, le fait qu'on ait choisi une valeur maline (une puissance de 2) fait qu'on a plein de valeurs remarquables...

Tables de sinus et cosinus

Maintenant qu'on a une bonne valeur pour son angle, "il se peut" qu'on ait besoin de calculer des sinus et des cosinus. Alors ça non plus, on ne va pas les calculer à chaque fois qu'on en a besoin, c'est bien trop couteux. A la place, on va les précalculer. Ensuite, lorsqu'on doit récupérer le sinus ou le cosinus d'un angle, il suffira d'un accès dans une table. Temps machine requis : Proche du zéro.

Evidement, maintenant qu'on connait la virgule fixe, on se doute bien qu'on ne va pas précalculer les sinus et les cosinus en float ! On va multiplier les valeurs par un chiffre arrangeant (au hasard, 256) et garder les valeurs sur des entiers (s16 pour moi).

On se retrouve donc avec une table dont les valeurs varient de -256 à 256. Une fois multiplié la valeur qu'on doit multiplier dans son calcul, un petit décalage de 8 vers la droite (soit une division par 256), et voilà : On vient de multiplier des chiffres à virgule avec des valeurs entières.

On peut aussi gagner un peu d'espace mémoire, puisqu'on sait que les valeurs de sin et cos sont les mêmes, à 90 degrés près. Je calcule donc 256+64 valeurs de cosinus, et je fais pointer les cos sur le début de la table, les sin à l'index 64.

(Vous noterez que j'ai conservé une table et deux pointeurs. Oui, on peut faire plus court. Moi je trouve ça clair comme ça, alors je laisse comme ça).

Variables (Chez moi, se trouvent dans une sructure qui est une variable générale) afficher/masquer
s16 pSinCos[256 + 64];    // Table contenant les sin et cos * 256, sur 256 angles.
s16 *pSin;                // Ptrs sur les tables.
s16 *pCos;

Fonction de précalcul afficher/masquer
#define PI 3.1415927

// Précalcul des tables de sinus-cosinus.
// 256 angles, val *256 (=> varie de -256 à 256).
void PrecaSinCos(void)
{
    u32 i;

    for (i = 0; i < 256 + 64; i++)
    {
        gVar.pSinCos[i] = (s16) (cos(i * 2 * PI / 256) * 256);
    }
    gVar.pSin = gVar.pSinCos + 64;
    gVar.pCos = gVar.pSinCos;

}

Et voilà !

L'angle entre 2 points ?

En voilà un truc qui mathématiquement ne doit pas vouloir dire grand chose ! (^_^) Mais bon, on voit l'idée : Un monstre est à la coordonnée (xA, yA), à quel angle doit-il tirer pour que sa balle se dirige vers le joueur en (xB, yB) ?

La fonction qu'il vous faut, c'est atan2 !

Il y a une version standard dans "math.h". J'utilise personnellement une version en virgule fixe et qui me renvoie une valeur comprise entre 0 et 255, dérivée de l'algo trouvé sur cette page, et reproduit ci-dessous :

Fast arctan2 - Pseudo code afficher/masquer
// Fast arctan2
float arctan2(float y, float x)
{
   coeff_1 = pi/4;
   coeff_2 = 3*coeff_1;
   abs_y = fabs(y)+1e-10   // kludge to prevent 0/0 condition
   if (x>=0)
   {
      r = (x - abs_y) / (x + abs_y);
      angle = coeff_1 - coeff_1 * r;
   }
   else
   {
      r = (x + abs_y) / (abs_y - x);
      angle = coeff_2 - coeff_1 * r;
   }
   if (y < 0)
      return(-angle);   // negate if in quad III or IV
   else
      return(angle);
}

Il suffit ensuite de passer un delta y et un delta x à la fonction, sans oublier qu'en général le repère Y est inversé.

Le plus court chemin ?

Une tourelle dans un shoot-em'up est tournée à un angle A. Elle doit se tourner pour viser le joueur jusqu'à un angle B. Dans quel sens tourner ?

Quand on a choisi une valeur qui va bien, c'est très simple : Une simple soustraction suffit. Il suffira ensuite de regarder le signe du résultat, soit le bit le plus à gauche.

Exemple avec des angles sur 8 bits (Reprise d'un de mes posts sur le sdz) :

On est sur l'angle 10, on veut aller au 170 :
170 - 10 = 160 = $A0. Le bit 7 est à 1, il faut décrémenter l'angle.

On est sur l'angle 40, on veut aller sur le 130 :
130 - 40 = 90 = $5A. Le bit 7 est à 0, il faut incrémenter l'anlgle.

On est sur l'angle 224, on veut aller au 64 :
64 - 224 = -160 = $FF60, mais sur 8 bits $60. Le bit 7 est à 0, il faut incrémenter l'angle.

Comme les angles et les calculs tiennent sur des u8, on peut ignorer les débordements.

Home