Tuto de mécanique très simpliste.

 

Je vais parler rapidement de comment on peut implémenter des effets physique à des objets ; ce qui est décrit là est simple et sans prétention. Ce tuto contient des techniques que j’ai trouvé seul (à l’exception des calculs à virgules fixes), donc elles sont peut être maladroite et/ou mal expliquée.
Je n’ai pas les capacités ou l’intention de faire un cour de physique. Si vous voulez des détails, allez voir des bouquins de 1ere/TleS. Et toc.

 


1/ Les variables servant à décrire notre objet.

Une objet qui bouge en programmation, c’est 2 coordonnées (quand on est en) + 1 vecteur vitesse. Sachant qu’un vecteur c’est 1 différence de coordonnées, on stock un vecteur dans 2 variables. (En 3d, il y a 3 variables pour les coordonnées, donc il y aura 3 variables pour le vecteur).

On a donc :
x
y
vx
vy

2/ Initialiser les variables.

Pour initialiser les coordonnées, vous savez faire…
StructureDuTrucQuiBouge O ;
x = 20;
y = 10;


Pour initialiser les coordonnées du vecteur suivant l’angle par rapport à l’horizontal, il suffit d’appliquer des formules de trigo :



On dira que « a » est la vitesse de l’objet.

Par exemple :

 


3/ Faire bouger l’objet.

Pour faire bouger l’objet, rien de plus simple. Il suffit d’additionner les coordonnées avec les composantes des vecteurs :
x += vx ;
y +=vy ;

C’est tout :D


4/ Faire varier les vecteurs.

Le vecteur change de valeur quand une force est appliquée sur l’objet. Cette force peut être une force de contact (frottement, contact) ou une force à distance (gravité, électromagnétisme). On en a rien à faire mais c’est amusant de jouer au prof de phys.


On va parler de la gravité parce que c’est la plus simple des forces à programmer, à partir du moment où l’on considère qu’elle est constante (ce qui est vrai dans 100% des jeux qui ne se déroulent pas dans l’espace…)

En temps normal, la gravité applique une force verticale par rapport au sol. La gravité affecte donc que la composante verticale du vecteur vitesse.

Sachant ces 2 choses (la gravité est constante et s’exerce suivant l’axe ‘y’), on peut en déduire comment faire pour varier le vecteur vitesse :
Vy += constante;


5/ L’organisation d’un prgm. ATTENTION : SI VOUS COMMENCEZ A LIRE CETTE PARTIE, IL FAUT ABSOLUMENT LIRE LA PARTIE SUR LES CALCULS A VIRGULES FIXES.

La « théorie » est finie. Tout ce dont j’ai parlé jusqu'à maintenant ne sert à rien si vous avez un peu suivi le programme de TleS.


Pour faire un programme qui simule le lancé d’une balle, voici l’organisation qu’il doit avoir :

-création des variables décrivant la balle (x,y,vx,vy)
-initialisation des variables (a=60,f=10,x=10,y=20,vx=f*cos(a),vy=f*sin(a))
while(1) {
-affichage de la balle
-mouvement de la ball (x+=vx,y+=vy)
-calcul des nouvelles composantes du vecteur vitesse (vy+=constante)
}

6/ Application et les calculs à virgules fixe, ou comment faire que son code aille 50x plus vite (je n’exagère pas).

Vous allez sûrement avoir besoin à un moment ou un autre de manipuler des coordonnés qui demande de la précision. Là, vous vous dites « pas de problème, je peux utiliser des variables de type float ».


Exemple :

typedef struct {
float x; //position suivant l’axe x
float y; //position suivant l’axe y
float vx; //vecteur suivant l’axe x (différence de position suivant l’axe x)
float vy; //vecteur suivant l’axe y (différence de position suivant l’axe y)
} StructureDuTrucQuiBouge;


Et bien bonne nouvelle, ça marche. Faisons un petit programme avec des coordonnées de ce style :


#include <tigcclib.h>
#define nombre_de_particules (30)

typedef struct {
float x;
float y;
float vx;
float vy;
} PARTICULE;

void _main(void)
{
ClrScr();
int i;
PARTICULE *p;
p = malloc(sizeof(PARTICULE)*nombre_de_particules);
if (!p)
exit(0);

for (i = 0; i < nombre_de_particules; i ++) {
p[i].y = 60;
p[i].vy = -random(80)/10;
p[i].x = 80;
p[i].vx = (random(60)-30)/10;
}

while (!_keytest (RR_ESC)) {
for (i = 0; i < nombre_de_particules; i ++) {
//Quand une particule sort de l’écran, on la réinitialise :
if (p[i].y >= 89 || p[i].y <= 5 || p[i].x <= 1 || p[i].x >= 159) {
DrawPix (p[i].x,p[i].y,A_REVERSE);
p[i].y = 60;
p[i].vy = -random(80)/10;
p[i].x = 80;
p[i].vx = (random(60)-30)/10;

}
DrawPix (p[i].x,p[i].y,A_REVERSE);//on efface l’ancien point
p[i].vy += 0.5;
p[i].x += p[i].vx;
p[i].y += p[i].vy;

DrawPix (p[i].x,p[i].y,A_NORMAL); //on dessine le nouveau point
}
}
free(p);
}


Les couleurs mettent en évidence les différentes parties du programme. Comme je disais, c’est super, ça marche, mais c’est LENT !
C’est lent parcequ’on utilise des float, et tous les calculs fait avec ces variables sont des calculs à virgule flottante. Dans les jeux pour TI68k, ce type de variable est à fuir comme la peste. On va donc utiliser des variables entières (char, short ou long)


Mais comment faire pour mettre des nombres décimaux (1.34, 8.284, 100.5) dans une variable entière (1, 8, 100)?!
Il suffit de multiplier le nombre décimal par un nombre qu’on aura choisi (d’où l’expression « virgule fixe »):
1.34 * 100 = 134
Rq : Si on choisi de multiplier par 100, on perd de l’information quand on converti un nombre comme 8.284 en un entier :
8.284 * 100 = 828


Une fois les nombres décimaux converti en entier, toutes les opérations (additions, multiplications, divisions…) seront beaucoup plus rapides.

Une fois qu’on a fais nos calculs avec les entiers, on réobtient une valeur décimal en divisant par le même nombre.

 


Exemple :

float a;
for (i=0;i<1000;i++)
a += 1.5;
printf("%d",(short)a);

Sera bcp plus lent que:

short a;
for (i=0;i<1000;i++)
a += 15;
printf("%d",a/10);

 


Dernière chose: la multiplication et la division par des constante est (sauf cas particuliers) quand même assez lent. C’est justement en utilisant ces cas particuliers qu’on peux rendre les calculs à virgule fixe incroyablement rapide : En utilisant les décalages binaires!
Les opérations de décalages binaires permettent de multiplier/diviser par une puissance de 2 très très rapidement.

Rappel:
a>>n est équivalent à: a/(2^n)
et
a<<n est équivalent à a*(a^n).

Le même l’exemple de la fontaine avec à la place des float des int :


void _main(void)
{
ClrScr();
int i;
PARTICULE *p;
p = malloc(sizeof(PARTICULE)*nombre_de_particules);
if (!p)
exit(0);

for (i = 0; i < nombre_de_particules; i ++) {
p[i].y = 60<<7;
p[i].vy = (random(100)-70)<<(7-4);
p[i].x = 80<<7;
p[i].vx = (random(60)-30)<<(7-4);

}
while (!_keytest (RR_ESC)) {
for (i = 0; i < nombre_de_particules; i ++) {
if (p[i].y >= 89<<7 || p[i].y <= 5<<7 || p[i].x <= 1<<7 || p[i].x >= 159<<7) {
DrawPix (p[i].x,p[i].y,A_REVERSE);
p[i].y = 60<<7;
p[i].vy = (random(100)-70)<<(7-4);
p[i].x = 80<<7;
p[i].vx = (random(60)-30)<<(7-4);

}
DrawPix (p[i].x>>7,p[i].y>>7,A_REVERSE);
p[i].vy += 25;
p[i].x += p[i].vx;
p[i].y += p[i].vy;

DrawPix (p[i].x>>7,p[i].y>>7,A_NORMAL);
}
}
free(p);
}


© 2005 Jyaif