Aller au menu - Aller au contenu

[Plan du site] Vous êtes ici --- > Le Site du Zéro > Les tutoriels > Non-Officiels > Programmation > C > Les sauts 2D en C > Lecture du tutoriel

Les sauts 2D en C

Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Avatar
Auteur : George369
Note : 16 / 20 (8 votes)
Visualisations : 4 936

Plus d'informations Plus d'informations
Hello les zér0s,

que peuvent donc bien être les sauts en C avec la bibliothèque SDL ?

Des GoTo, des Lbl o_O ?

Nan, nan, rien de tout cela :p . Dans le forum, il y a souvent eu des questions du genre : "Comment je fais pour faire sauter mon perso ?", et très peu de réponses ...

Image utilisateur


Aujourd'hui, on va donc apprendre à faire sauter vos personnages, en 2 méthodes !

Il faut quelques mises en garde et des pré-requis.
Ce tutorial va utiliser des maths de niveau première S pour la méthode 1 et une partie du programme de Sciences Physiques de Terminale. Je vais vous expliquer mais il faudra savoir ce qu'est une fonction en maths, une parabole, une équation de courbe, la constante gravitationnelle, les fonctions paramétriques ... Bref, soyez au moins en troisième pour la méthode 1, et ... terminale pour la méthode 2. De plus, il faut avoir lu et compris le tuto de M@téo21 disponible sur votre gauche (tuto C / SDL), ceci en entier. Ca, c'était les pré-requis.
Ensuite, sachez que je ne vais pas parler de vitesse initiale ici. Les paramètres du saut, vous les ferez vous-mêmes (en effet, un saut dépend de la vitesse initiale du personnage).


Venez donc faire un saut dans mon tuto (c'est le cas de le dire) ! :D
Sommaire du tutoriel :
Icône du chapitre

Préparer le terrain

Donc, je disais qu'il y a besoin de connaître le tuto de M@atéo21 sur le langage C; ainsi vous savez, en C, initialiser une fenêtre SDL. Notre but est, sur cette fenêtre, afficher un personnage qui fait un saut. Commencez par ouvrir une fenêtre :

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#ifdef __cplusplus
    #include <cstdlib>
#else
    #include <stdlib.h>
#endif
#include <SDL/SDL.h>
 
int main ( int argc, char** argv )
{
    //Init de la SDL
    SDL_Init( SDL_INIT_VIDEO );
 
    //Et on quitte bien
    atexit(SDL_Quit);
 
    // On crée une fenêtre
    SDL_Surface* ecran = SDL_SetVideoMode(640, 480, 16,SDL_HWSURFACE|SDL_DOUBLEBUF);
 
    // La boucle principale
    bool fin = 0;
    while (!fin)
    {
        SDL_Event event;
        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_QUIT:
                fin = 1;
                break;
 
            case SDL_KEYDOWN:
                {
                    if (event.key.keysym.sym == SDLK_ESCAPE)
                        fin = 1;
                    break;
                }
            }
        }
 
        // Affichage
 
        //On vide l'écran
        SDL_FillRect(ecran, 0, SDL_MapRGB(ecran->format, 0, 0, 0));
 
        // Fin de l'affichage
 
        SDL_Flip(ecran);
    }
 
    return 0;
}


Voilà, ça, c'est le plus facile :p . Qu'est-ce qu'on obtient ? Un écran blanc, pas trop grand, prenant bien sûr tout l'UC à cause de la fonction SDL_PollEvent();.

Notre but sera d'afficher un personnage qui saute du sol, pour revenir jusqu'au sol. Dites bonjour à ... Mario !

Image utilisateur


Téléchargez mon Mario; il est en PNG donc il va nous falloir ajouter la bibliothèque SDL_Image. Je vous rappelle comment faire. On met un include en haut de la page :

Code : C
1
#include <SDL/SDL_image.h>


Et pour charger notre image, il faut rajouter une fonction au début de notre code :

Code : C
1
SDL_Surface* mario = IMG_Load("mario.png");


Maintenant qu'on a notre cher Mario, il faudrait peut-être qu'on ait un sol sur lequel il saute. Ne vous tuez pas à ça, faites simplement comme moi :

Image utilisateur


La ligne, c'est le sol, hein :p !

Nous avons maintenant tout pour commencer !

Méthode 1 : Paraboles

Là, c'est la partie difficile qui commence... En effet, on va faire des maths et de la physique !

Commençons avec la première méthode.

Les sauts, une question de paraboles



Tout est dans le titre. En effet, d'après la les lois physiques, un objet est en chute libre si uniquement son poids travaille. Par conséquent, ayant une vitesse initiale plus ou moins grande, sa vitesse horizontale est constante, mais la vitesse verticale devient rapidement négative (dès que l'objet a atteint le plus haut point de son saut) ... ... Ne faites pas attention à ce que je viens de vous dire si vous n'y comprenez rien :p .

En gros, un saut, c'est le mouvement d'un objet en chute libre ce qui aura pour conséquence que la courbe de sa trajectoire est ... une parabole ! Non pas un texte qui cache un enseignement moral ou religieux :p , mais un courbe arrondie au-dessus qui imite les vrais sauts.

L'objet monte vite, ralentit, puis redescend lentement en accélérant. C'est donc bien une parabole.

La méthode des paraboles fonctionne tout à fait correctement, on peut même dire que c'est la plus facile et la plus rapide des deux méthodes que je vous propose, et aussi la plus intuitive (vous verrez pourquoi après). Toutefois, comme les équations de parabole sont diverses et nombreuses, il y a un certain risque de ne pas faire de sauts "réalistes", car, si les lois physiques donnent des paraboles, seules une certaine quantité de paraboles suivent les lois physiques. Néanmoins, vous pouvez faire de très beaux sauts avec les paraboles :p , et elles sont très facilement ré-adaptables. Sinon, quel intérêt de proposer cette méthode ? :D


Trouver l'équation d'une parabole



Pour définir la trajectoire du saut de l'image, il faut définir l'équation de la parabole correspondante.

Qui connait les équations des paraboles ?


Bon, je vais vous le dire :) : une parabole a pour équation y = ax²+bx+c.

C'est là la partie fondamentale de notre saut : on connait l'altitude de notre personnage (y) en fonction de son déplacement horizontal (x) !

Je vous montre à quoi ressemblerait cette parabole :

Image utilisateur

On va donc dessiner un repère fictif dans notre programme. o_O Je vous expliquerai ça plus en détail après :p .

On va maintenant s'occuper à trouver les coefficients a, b et c dans cette équation. En effet, si on connait l'équation de notre parabole, on pourra donner le déplacement horizontal à notre programme pour qu'il calcule le déplacement vertical. :D Très pratique parce que cela s'appelle un saut :p !

Trouver b



Et oui je commence bien dans l'ordre ! :p b est le plus facile. En effet, comme on le voit sur mon schéma terrible de la mort qui tue, la courbe est symétrique par rapport à l'axe des abscisses. Ce qui veut dire que le déplacement horizontal est nul, tout comme b.

Donc : b = 0.

Trouver c



Qui connait la méthode pour trouver c ? c est ce qu'on pourrait appeler l'ordonnée à l'origine. C'est en gros l'ordonnée du sommet de la parabole.

Par quoi cette ordonnée est-elle définie ?

Par la hauteur du saut, bien sûr !

Allez, combien on fait sauter notre mario ? 50 pixels ? 100 pixels ? Je choisirai 100 pour cet exemple, mais vous pouvez choisir plus, bien entendu.

Donc : c = 100.

Trouver a



Aïe, les choses se corsent encore :p . Où en somme-nous d'ailleurs ? L'équation qu'on a pour le moment est :

y = a*x²+100 (car b = 0).

De quoi peut bien dépendre a ? Je vous donne deux pistes :



Pour la première info, a doit bien sûr être négatif. En effet, on ne veut pas que notre personnage tombe et remonte, mais qu'il saute et qu'il redescende.

Exemple de parabole tournée vers le haut, où a est par conséquent positif :

Image utilisateur


Et une tournée vers le bas, où a est négatif :

Image utilisateur


J'ai surligné avec mes grands talents artistiques :p la partie qui va nous intéresser. Les coordonnées négatives, c'est pas pour tout de suite. On prend donc un a négatif !

La seconde information est capitale. Une parabole devient de plus en plus large quand a diminue... Il faudrait donc peut-être choisir un point de départ et d'arrivée. Le point de départ est le moment du saut, et le point d'arrivée, le moment d'atterrissage.

Je choisis comme point de départ, 200 px, et 301 px comme moment d'atterrissage. Pourquoi 301 ? Si j'avais choisi un nombre pair, il serait plus difficile de placer notre repère fictif. Les demi-pixels n'existent malheureusement pas :p ...

Où sera donc placé notre repère fictif ?

Au point 251 bien sûr ! C'est le milieu entre 200 et 301.

Maintenant, on veut savoir les coordonnées de ces points, que j'appelle A, le point de départ, et B, le point d'arrivée. Le personnage est au sol pendant ces deux moments, non ? Dans ce cas :

A(200;300)
B(301;300)


Je vous rappelle que le sol, le trait que j'ai tracé, est à 300 px du bord inférieur de notre fenêtre :) . D'où le schéma suivant, avec les coordonnées absolues :

Image utilisateur


Malheureusement, on ne peut utiliser ça pour notre parabole, car la parabole a un autre repère que notre fenêtre (l'origine est (251;300)). Réfléchissons donc : A et B sont distants de 101 px; l'origine est au milieu de cette distance; par conséquent, on peut dire que B est distant de 50px de l'origine, et A aussi. Mais A est à gauche de l'axe des ordonnées. Donc, les abscisses de A sont négatives.

Ce qui nous donne ces points relatifs :

A(-50;0)
B(50;0)


D'où le second schéma, avec les coordonnées dans le repère fictif :

Image utilisateur


On met 0 en ordonnées car les ordonnées de l'origine sont identiques à celles de nos points. Il est capital d'avoir compris cette partie, n'hésitez pas à relire ;) .

Maintenant qu'on a ces nouveaux points (dont vous pouvez personnaliser les abscisses d'ailleurs), on reconsidère notre équation :

y = a*x²+100.

On peut remplacer x et y par les coordonnées de l'un ces points. Je choisis A; par conséquent,

0 = a*(-50)² + 100 ; on résout l'équation :
2500a = -100.

Donc : a = -0,04.

a est-il bien négatif ? Oui, donc nous pouvons continuer !

Pourquoi faire tout ça



Et bien, parce que nous allons l'utiliser dans notre programme. Le repère choisi sera fictif, non dessiné, et ce qui sera surtout intéressant, c'est que nous allons positionner mario selon un intervalle régulier du temps, pour créer un mouvement fluide.

Argh :pirate: pourquoi on était là déjà ? Ah oui, pour programmer !

Mario, il est présent dans 2 repères, un fictif, et le repère de notre fenêtre. L'ennui avec le repère de notre fenêtre, c'est qu'il est inversé; le 0 est tout en haut. On ne va pas s'embêter, on verra ça plus tard :) .

2 repères, ça veut dire ... 2 positions ! Et oui ! J'ai nommé ...

Code : C
1
2
3
4
5
6
7
SDL_Rect posMarioAbs;
posMarioAbs.x = 200;
posMarioAbs.y = 300;
 
SDL_Rect posMarioRel;
posMarioRel.x = -50;
posMarioRel.y = 0;


Hein ? Abs et Rel ?


Et oui, il y a une position que j'appelle absolue : c'est la position de Mario dans la fenêtre. La seconde position est relative : c'est la position de Mario dans notre nouveau repère.

J'ai pris les coordonnées du point A pour la première position de notre Mario ; cf. ci-dessus.

Pourquoi en faire 2 ? Eh bien, parce que la SDL ne comprend rien aux repères relatifs : lui, y a que la fenêtre ! Et nous, on ne peut pas travailler dans le repère de la fenêtre; conséquence, on travaille autre-part, et ça nous fait deux positions. :p

Ne vous inquiétez pas, elles sont liées, c'est ça le plus grand intérêt :) .

Ne reposons pas sur nos lauriers tout de suite ; notre Mario est toujours immobile :p .

Nous allons donc nous intéresser au code, et plus particulièrement à la partie qui suit la gestion d'évènements, la partie que j'appelle "l'évolution".

L'évolution



Ici, on va travailler sur les coordonnées de notre Mario.

L'évolution se situe ici :

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
main()
{
    initialisation();
    while
    {
        gestion_évènements();
        EVOLUTION();
        affichage();
    }
    quitter();
}


Je vous ai fait un schéma bien sympathique de là où on va travailler. On va d'abord s'intéresser au temps.

Le temps



Comme vous travaillez tous avec des ordis 4.0 GHz, le code va s'exécuter super vite :p . Vous n'aurez même pas le temps de voir votre pauvre Mario sauter... On va donc mettre un intervalle de temps régulier, entre chaque affichage :

Code : C
1
SDL_Delay(10);


SDL_Delay interrompt le programme pendant un temps exprimé en ms. L'intervalle est de 10 ms, assez petit pour qu'on voit un mouvement bien fluide. On place cette fonction à la fin du code de l'évolution.

Maintenant, on veut qu'à chaque fois que la boucle passe, Mario avance. On va d'abord s'intéresser à sa vitesse horizontale.

La vitesse horizontale



Dans une chute libre, la vitesse horizontale ne varie pas. Si vous ne savez pas ce qu'est la vitesse horizontale, sachez que tout objet a une vitesse que l'on peut décomposer en vitesse horizontale et une vitesse verticale. Sous forme de vecteurs, on obtient ça :

Image utilisateur


Conséquence : on incrémente les abscisses de notre Mario à chaque fois que la boucle passe.

Les abscisses absolues ou relatives ?


Bonne question :) . Nous travaillons dans le repère fictif, donc ce seront les abscisses relatives :

Code : C
1
posMarioRel.x++;


Voilà pour la vitesse horizontale.

La vitesse verticale



Pour la vitesse verticale, c'est là que notre équation de parabole va venir en aide. En effet, on avait dit qu'on pouvait trouver y en fonction de x :

y = 0,04x² + 100;

Donc :

Code : C
1
posMarioRel.y=(-0.04*(posMarioRel.x*posMarioRel.x)+100);


En C, "²" n'existe pas donc il faut le faire manuellement.

Voilà, nous avons géré nos variables relatives. Quant aux absolues, il faut se référer à la loi de Chasles ; si I est l'origine de notre nouvelle origine, et M un point quelconque dans le plan, on a :

\vec {OM} = \vec {OI} + \vec {IM}

Par conséquent, on peut ajouter les coordonnées :

Code : C
1
2
posMarioAbs.x = posMarioAbs.x + posMarioRel.x +50;
posMarioAbs.y = posMarioAbs.y + posMarioRel.y;


NB : dans le repère fictif, on commence à -50 px. Par conséquent, quand notre Mario saute dans le repère absolu, il sera décalé de 50 px de son point de départ absolu. D'où le "+50" dans la formule.


Il nous reste une dernière chose à faire : à chaque passage de boucle, les variables absolues sont modifiées ; si on ne veut pas que notre Mario parte à Saint-Perpète-les-Bains :p , il faut les remettre comme elles étaient avant ; on rajoute donc :

Code : C
1
2
posMarioAbs.x = 200;
posMarioAbs.y = 300;


Affichage



N'oubliez pas non plus de blitter vos surfaces :

Code : C
1
2
SDL_BlitSurface(fond, 0, ecran, &posFond);
SDL_BlitSurface(mario, 0, ecran, &posMarioAbs);


Lancez votre code, et observez ... :p

Corriger les erreurs



Hé hé hé vous avez dû oublier quelque chose ! :D

On va corriger les erreurs une à une :

Mon Mario commence avec la tête en-dessous du sol !



C'est normal, l'origine du repère de dessin est l'angle supérieur gauche de notre surface. Pour remédier à cela, il faut enlever la hauteur de Mario aux coordonnées absolues :

Code : C
1
2
posMarioAbs.x = 200;
posMarioAbs.y = 300-(mario->h);


Mon Mario tombe et remonte !



Là on va voir ceux qui ont bien écouté : je l'ai dit un peu au-dessus ; le repère de la SDL est inversé. Il faut donc rajouter un "-" en ajoutant les ordonnées absolues et relatives :

Code : C
1
2
posMarioAbs.x = posMarioAbs.x + posMarioRel.x;
posMarioAbs.y = posMarioAbs.y - posMarioRel.y;


Mon Mario quitte l'écran à la fin du saut !



Il faut remettre à zéro les variables relatives quand elles deviennent trop grandes ; rajoutez ça au début de l'évolution , juste après avoir incrémenté la valeur relative des abscisses :

Code : C
1
2
3
4
if(posMarioRel.x>=50)
{
    posMarioRel.x=-50;
}


Pour éviter que Mario ne s'enfonce dans le sol ! :p

Voilà, normalement, il n'y a plus d'erreurs.

Tenez, voici le code complet pour ceux qui n'ont pas écouté :p :

Code : C
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#ifdef __cplusplus
    #include <cstdlib>
#else
    #include <stdlib.h>
#endif
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
 
int main ( int argc, char** argv )
{
    //Init de la SDL
    SDL_Init( SDL_INIT_VIDEO );
 
    //Et on quitte bien
    atexit(SDL_Quit);
 
    // On crée une fenêtre
    SDL_Surface* ecran = SDL_SetVideoMode(640, 480, 16,
                                           SDL_HWSURFACE|SDL_DOUBLEBUF);
    // On charge le fond
    SDL_Surface* fond = SDL_LoadBMP("fond.bmp");
 
    //On charge mario
 
    SDL_Surface* mario = IMG_Load("mario.png");
 
 
    // La position du fond
    SDL_Rect posFond;
    posFond.x = 0;
    posFond.y = 0;
 
    // La position absolue de mario
    SDL_Rect posMarioAbs;
    posMarioAbs.x = 200;
    posMarioAbs.y = 300-(mario->h);
 
    // La position relative de mario
    SDL_Rect posMarioRel;
    posMarioRel.x = -50;
    posMarioRel.y = 0;
 
    // program main loop
    bool done = false;
    while (!done)
    {
        // message processing loop
        SDL_Event event;
        while (SDL_PollEvent(&event))
        {
            // check for messages
            switch (event.type)
            {
                // exit if the window is closed
            case SDL_QUIT:
                done = true;
                break;
 
                // check for keypresses
            case SDL_KEYDOWN:
                {
                    // exit if ESCAPE is pressed
                    if (event.key.keysym.sym == SDLK_ESCAPE)
                        done = true;
                    break;
                }
            }
        }
 
        // EVOLUTION
            //On avance de 1
            posMarioRel.x++;
 
            if(posMarioRel.x>=50)
            {
                posMarioRel.x=-50;
            }
 
            //On met à "0" les pos abs:
 
            posMarioAbs.x = 200;
            posMarioAbs.y = 300-(mario->h);
 
            //On calcule la valeur relative de y:
            posMarioRel.y=(-0.04*(posMarioRel.x*posMarioRel.x)+100);
 
            //On calcule maintenant les valeurs abs
 
            posMarioAbs.x = posMarioAbs.x + posMarioRel.x+50;
            posMarioAbs.y = posMarioAbs.y - posMarioRel.y;
 
        //Intervalle de 10ms
        SDL_Delay(10);
 
        // FIN EVOLUTION
 
        // DRAWING STARTS HERE
 
        // clear ecran
        SDL_FillRect(ecran, 0, SDL_MapRGB(ecran->format, 0, 0, 0));
 
        // draw bitmap
        SDL_BlitSurface(fond, 0, ecran, &posFond);
        SDL_BlitSurface(mario, 0, ecran, &posMarioAbs);
 
        // DRAWING ENDS HERE
 
        // finally, update the ecran :)
        SDL_Flip(ecran);
    } // end main loop
 
    // free loaded bitmap
    SDL_FreeSurface(fond);
    SDL_FreeSurface(mario);
 
    return 0;
}


Hé, moi je le trouve moche ce saut ! En vrai, je suis sûr que les lois physiques empêchent de sauter de cette façon !


Oui, vous devez sûrement tous penser ça :p . Voilà pourquoi je vous présente maintenant une seconde méthode ... plus compliquée, plus déroutante, plus sanglante, plus horrible que jamais ... :pirate: J'ai nommé ... les courbes paramétriques :pirate: !

Méthode 2 : Lois de Newton & Cie

Un poil plus dure que la précédente :D , cette méthode relève du programme de Terminale S.

Attention ! Je ne vais pas m'amuser à redémontrer les acquis mentionnés ci-dessus. Soit tout cela est dans votre cours, soit vous l'acceptez comme c'est écrit :p !


À quoi ça sert de faire encore plus compliqué ?


^^ Pour obtenir des sauts réalistes, bien sûr ! En effet, les paraboles permettent parfois de faire... trop de choses. Je m'explique : on peut, avec la méthode précédente, faire sauter Mario de 200 px vers la droite, en ne l'élevant au total que de 1 px ; c'est complètement contre les lois de la nature ! Ou plutôt, les lois de Newton :p ...

La seconde loi de Newton



Je ne vous la citerai pas ici, elle est trop barbare et elle va sûrement vous faire peur et vous décourager pour le QCM :p . Sachez simplement que l'on se base dessus pour le reste du tutorial, car le saut que l'on va programmer a une vitesse non rectiligne ni uniforme.

Le principe de cette méthode est bien plus réaliste, car elle permet de s'adapter à des situations plus "réalistes" qu'avec la méthode précédente. En effet, imaginez que votre perso court avec une vitesse initiale V et qu'il saute... Comment reproduire ça avec une parabole o_O ?!?

J'ai déjà révélé le premier paramètre de notre saut : la vitesse initiale ; maintenant, si je vous annonçais qu'on peut programmer un saut uniquement avec l'angle de saut et cette vitesse initiale ? Si, si :D !

En gros, le début du saut sera comme ceci :

Image utilisateur


L'angle initial de saut est bien sûr dans le sens direct, par convention opposé aux aiguilles d'une montre :D .

Il va maintenant falloir appliquer ces deux paramètres à notre saut. Commençons par les vitesses horizontale et verticale.

Décomposer la vitesse, et en déduire la trajectoire



Tout d'abord, la vitesse



Bon je vous rappelle mon magnifique schéma :p :

Image utilisateur


Notre but est d'abord de trouver la vitesse horizontale Vx puis la vitesse verticale Vy. Les accros de la trigo verront vite que l'on peut établir ces relations :

\left \{ v_x = v \ \times \ \cos\ \theta \\ v_y = v \ \times \ \sin\ \theta \\

Ca, c'était la partie facile.

Trajectoire : deux façons de la trouver



Continuons d'innover ; en effet, nous allons utiliser ici les axes paramétriques !

En gros, y ne dépend plus de x. o_O

Et oui ! À partir de maintenant, y dépend de t, et x aussi.

t ?!?! Qu'est-ce que c'est que ça ?


"Ca" c'est tout simplement notre intervalle de temps régulier que l'on retrouve ici. "t" est l'instant où y vaudra tant, et x vaudra tant. Pour nous programmeurs, t est l'instant où on blittera notre image à une abscisse donnée et une ordonnée donnée. Or, avec notre SDL_Delay();, les instants deviennent virtuels. En effet, si on met un délai de 10 ms, toutes les 10 ms notre image sera réaffichée. Ainsi, t = 0 est l'instant donné où mario n'a pas encore quitté le sol, et pour nous ça correspond peut-être au 1er passage de boucle.

Reprenons :) . Maintenant que nous avons calculé les vitesses, intéressons-nous à la trajectoire. De quoi dépend-elle exactement ? On vient de dire qu'elle dépendait de l'instant où on affiche, et ... la vitesse horizontale ou verticale ! En fait, on peut dire que :

x_{(t)} = v_x \ \times \ t \\ y_{(t)} = v_y \ \times \ t \ - \ \frac{gt^2}{2}

Pour la vitesse horizontale, on retrouve bien nos paramètres. x(t) veut dire : "x à l'instant t". Mais pour la position verticale ? Ce serait une erreur d'exprimer y de la même façon que x, car on obtiendrait en fin de compte ... une fonction linéaire, c'est-à-dire, une droite :( Notre Mario irait droit dans le ciel ! :p Or nous recherchons une parabole, il nous faut donc des x².

Mais, tu ne nous avais pas dit que y ne dépendait plus de x ?


C'est là la réponse au sujet. En effet, il faut en fait enlever à chaque instant quelques pixels de notre position verticale, à la façon d'une parabole mais par rapport à la constante gravitationnelle. D'où la formule complexe ci-dessus.


Ne vous posez pas trop de questions si vous ne comprenez pas. Dites-vous simplement que l'on retrouve dans ce calcul :



Voilà :) . Votre Mario est prêt à être lancé :p . Passons donc à la programmation.

Ce que ça donne en C



Résumons ce qu'il faut faire :) .

Les variables



Il y a plus de variables à définir que chez les paraboles.
Tout d'abord, il y a la constante gravitationnelle, qui fera "l'effet poids" :

Code : C
1
const double g = 9.81;


Puis, on définit un instant t que l'on initialise à 0 :

Code : C
1
int t = 0;


Enfin, on établit les paramètres initiaux de notre saut :

Code : C
1
2
int v_init = 2;
int angle_init = 70;


On peut ainsi tout de suite calculer nos vitesses horizontale et verticale :

Code : C
1
2
double v_x = cos(angle_init)*v_init;
double v_y = sin(angle_init)*v_init;


Gardez bien nos positions absolues et relatives. On peut maintenant passer à l'évolution !

Evolution



Dans l'évolution, on va d'abord faire changer le temps. Placez votre SDL_Delay(); à la fin, et occupez-vous de l'instant t :

Code : C
1
t+=10;


t est ici exprimé en ms. Cela sera conséquent pour la suite. Continuons donc avec nos formules pour les trajectoires :

Code : C
1
2
posMarioRel.x=(int)(v_x*t);
posMarioRel.y=(int)((v_y*t)-((g*t*t)/2000));


Ca fait une expression barabare, je l'admets :p . J'ai rajouté un convertisseur en entiers devant, car la fonction cosinus et la fonction sinus, à partir desquelles sont calculées les décompositions de la vitesse intiale, sont loin de donner un résultat toujours entier. Or, notre compilateur risque de nous insulter poliment si on donne un nombre décimal à une variable sensée contenir un nombre entier. D'où le (int), ayant le même effet qu'une troncature. Vous pouvez vous amuser à trouver la fonction pour arrondir si jamais vous avez besoin d'avoir des résultats précis :D .

De plus, ici, ce n'est plus gt²/2 mais gt²/2000 o_O . En effet, t est exprimé en ms, et nous on veut un résultat en secondes, d'où la division supplémentaire par mille.

Gardez bien sûr ça :

Code : C
1
2
3
4
5
posMarioAbs.x = 200;
posMarioAbs.y = 300-(mario->h);
 
posMarioAbs.x = posMarioAbs.x + posMarioRel.x;
posMarioAbs.y = posMarioAbs.y - posMarioRel.y;


Et pour remettre à 0 notre position de Mario, tapez ceci :

Code : C
1
2
3
4
if((posMarioAbs.y)>=(300))
{
     t=0;
}


Je vous rapelle que tout dépend de t ici :) !

Cette fois, il ne devrait pas y avoir d'erreurs... :p

Le code complet



Code : C
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#ifdef __cplusplus
    #include <cstdlib>
#else
    #include <stdlib.h>
#endif
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <math.h>
 
int main ( int argc, char** argv )
{
    //Init de la SDL
    SDL_Init( SDL_INIT_VIDEO );
 
    //Et on quitte bien
    atexit(SDL_Quit);
 
    // On crée une fenêtre
    SDL_Surface* ecran = SDL_SetVideoMode(640, 480, 16,
                                           SDL_HWSURFACE|SDL_DOUBLEBUF);
    // On charge le fond
    SDL_Surface* fond = SDL_LoadBMP("fond.bmp");
 
    //On charge mario
 
    SDL_Surface* mario = IMG_Load("mario.png");
 
 
    // La position du fond
    SDL_Rect posFond;
    posFond.x = 0;
    posFond.y = 0;
 
    // La position absolue de mario
    SDL_Rect posMarioAbs;
    posMarioAbs.x = 200;
    posMarioAbs.y = 300-(mario->h);
 
    // La position relative de mario
    SDL_Rect posMarioRel;
    posMarioRel.x = 0;
    posMarioRel.y = 0;
 
    //Variables méthode 2:
 
    const double g = 9.81;
    int v_init = 2;
    int angle_init = 89;
    int t = 0;
    double v_x = cos(angle_init)*v_init;
    double v_y = sin(angle_init)*v_init;
 
    // program main loop
    bool done = false;
    while (!done)
    {
        // message processing loop
        SDL_Event event;
        while (SDL_PollEvent(&event))
        {
            // check for messages
            switch (event.type)
            {
                // exit if the window is closed
            case SDL_QUIT:
                done = true;
                break;
 
                // check for keypresses
            case SDL_KEYDOWN:
                {
                    // exit if ESCAPE is pressed
                    if (event.key.keysym.sym == SDLK_ESCAPE)
                        done = true;
                    break;
                }
            }
        }
 
        // EVOLUTION
            //On avance de 1
 
 
 
 
            //On met à "0" les pos abs:
 
            posMarioAbs.x = 200;
            posMarioAbs.y = 300-(mario->h);
 
            //On calcule la valeur relative de y:
            posMarioRel.x=(int)(v_x*t);
            posMarioRel.y=(int)((v_y*t)-((g*t*t)/2000));
 
            //On calcule maintenant les valeurs abs
 
            posMarioAbs.x = posMarioAbs.x + posMarioRel.x;
            posMarioAbs.y = posMarioAbs.y - posMarioRel.y;
 
        //Intervalle de 10ms
 
        t+=1;
 
        // FIN EVOLUTION
        if((posMarioAbs.y+(mario->h))<=(100))
            {
                t=0;
            }
        // DRAWING STARTS HERE
 
        // clear ecran
        SDL_FillRect(ecran, 0, SDL_MapRGB(ecran->format, 0, 0, 0));
 
        // draw bitmap
        SDL_BlitSurface(fond, 0, ecran, &posFond);
        SDL_BlitSurface(mario, 0, ecran, &posMarioAbs);
 
        // DRAWING ENDS HERE
 
        // finally, update the ecran
        SDL_Flip(ecran);
    } // end main loop
 
    // free loaded bitmap
    SDL_FreeSurface(fond);
    SDL_FreeSurface(mario);
 
    return 0;
}


Adapter et améliorer



Essayez de modifier les paramètres du saut ; vous vous rendrez vite compte que vos modifications n'auront que très peu d'effet. Le saut est souvent très large, et, à moins de modifier la formule de la trajectoire d'une chute libre ou la constante gravitationnelle, votre saut est vraiment très peu maniable. Toutefois, il existe une solution :p .

On obtient une parabole. Vous me direz, une parabole peut s'écrire de la façon y = ax²+bx+c. Et bien, je vous annonce un scoop : d'après la formule hyper-compliquée ci-dessus, on peut en tirer une équation de parabole ! Je ne le démontrerai pas ici ^^ , mais ça donne :

y = - \frac{g}{2} \ . \ \frac{x^2^}{(v . \cos \ \theta)^2} \ + \ \frac{v . (\sin \ \theta) . x}{v . \cos \ \theta}

Normalement il y a un "+ h" à la fin de cette équation de suicidaire :p . h correspondrait à c dans une équation parabolique. Ainsi, h = 0 car nous sommes toujours dans les coordonnées relatives. Têta correspond à notre angle initial. Si vous remplacez la parabole dans la méthode 1 ci-dessus par cette équation, vous donnez en fait à votre ordinateur l'équation suivante :

Code : C
1
2
3
posMarioRel.y = (int)(-(g/2)*((posMarioRel.x*posMarioRel.x)/
((v_init*cos(angle_init))*(v_init*cos(angle_init))))
+((v_init*sin(angle_init)*posMarioRel.x)/(v_init*cos(angle_init))));


Normalement je n'ai pas oublié de parenthèses :D . Il faut avouer que cette méthode est vraiment barbare. Mais si vous calculez a, b et c auparavant, cela ne devrait pas trop poser de problèmes à l'ordinateur de les recalculer lorsque vous voulez adapter la parabole à vos rêves les plus fous :p .

Et maintenant, QCM !

Q.C.M.

Le repère fictif concerne ...
Je veux que mon perso saute d'une hauteur de 300 px, parte du pixel 500 pour arriver au pixel 701. Quelle est l'équation de la parabole dans le repère fictif ?
Je veux faire un saut ayant une vitesse initiale de 15 px/s et un angle initial de 30°. Quelle méthode est-ce que j'utilise ?
Quelle méthode est-ce que je choisis pour faire un saut que je veux entièrement adapter à mon projet, n'ayant pas besoin d'être "réaliste" ?

Statistiques de réponses au QCM


Fini les sauts bricolés comme on en trouve de très nombreux sur le forum :D !

Vos personnages sautent maintenant de façon efficace et totalement réadaptable en cours de saut selon des lois logiques.

Ceci est en fait un grand pas dans la programmation de jeux, car :


J'espère donc, qu'en fin de compte, vous avez compris ce que je vous ai expliqué, et que vous saurez le réutiliser dans tous les cas possibles et imaginables. N'hésitez pas à m'envoyer un MP si jamais erreur il y a ;) .
Retour en haut Retour en haut


Créé : le 29/02/2008 à 14:27:49
Modifié : le 22/08/2008 à 16:08:29
Avancement : 100%
Licence : Copie non autorisée

Changer de design | En savoir plus | Plan du site | Politique d'accessibilité | Règles | RSS tutoriels | RSS news
Édité par Simple IT SARL : Nous contacter | Notre blog | Revue de presse | Publicité

Y'a plus rien à lire, faut remonter maintenant !

Hébergement web - Correction de tutoriels - Créer un site
Vous souhaitez apparaître ici ? Contactez-nous.

Nombre de connectés 561 Zéros connectés | Requêtes SQL 8 requêtes | Temps de génération de la page : Total (SQL) 0.0347s (0.0235s)