Aller au menu - Aller au contenu

[Plan du site] Vous êtes ici --- > Le Site du Zéro > Les tutoriels > Officiels > Programmation > Apprenez à programmer en C ! > [Pratique] Création de jeux 2D en SDL > Maîtrisez le temps ! > Lecture du tutoriel

Maîtrisez le temps !

Avatar
Auteur : M@teo21
Difficulté : Connaisseur (3 / 5)
Note : 20 / 20 (11 votes)
Visualisations : 122 524

Plus d'informations Plus d'informations
Ce chapitre est d'une importance capitale : il va vous apprendre à gérer le temps en SDL.

Maîtriser le temps est quasiment indispensable... j'ai bien dit quasiment car, comme vous l'avez vu dans le TP Mario Sokoban, on peut très bien faire un jeu sans manipuler le temps.
Mais c'est rare. Pour un nombre pratiquement incalculable de jeux, la gestion du temps est fondamentale.

Tenez par exemple, comment coderiez-vous un Tetris ou un Snake (jeu du serpent) ? Il faut bien que les blocs bougent toutes les X secondes, et ça vous ne savez pas faire. Du moins, vous ne savez pas encore le faire ;)
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Le Delay et les Ticks

Dans un premier temps, nous allons apprendre à utiliser 2 fonctions très simples :


Ces 2 fonctions sont peut-être super simples comme nous allons le voir, mais bien savoir les utiliser n'est pas évident... alors ouvrez grand vos oreilles yeux :)


SDL_Delay



Comme je l'ai précédemment dit, cette fonction effectue une pause sur le programme durant un certain temps. Pendant que le programme est en pause, on dit qu'il dort ("sleep" en anglais) : il n'utilise pas le processeur.

SDL_Delay peut donc être utile pour réduire l'utilisation du processeur. Notez que j'abrègerai processeur par CPU désormais, ce qui signifie Central Processing Unit, soit "Unité centrale de calcul".
Grâce à SDL_Delay, vous pourrez rendre votre programme moins gourmand en ressources processeur. Il fera donc moins "ramer" votre PC si SDL_Delay est utilisée intelligemment.

Tout dépend du programme que vous faites : parfois on aimerait bien que notre programme utilise le moins de CPU possible pour que l'utilisateur puisse faire autre chose en même temps, comme c'est le cas pour un lecteur MP3 qui tourne en fond pendant que vous naviguez sur Internet.
Mais... d'autres fois on se moque complètement que notre programme utilise tout le temps 100% de CPU. C'est le cas de la quasi-totalité des jeux. En effet : quand vous jouez, en théorie vous ne faites que ça, donc on peut se permettre d'utiliser tout le temps le CPU. Quand vous jouez à Far Cry ou Half-Life 2, votre PC travaille donc à 100% tout le temps ! ;)


Bien, revenons à la fonction qui nous intéresse. Son prototype est d'une simplicité affligeante :

Code : C
1
void SDL_Delay(Uint32 ms);


En clair, vous envoyez à la fonction le nombre de millisecondes pendant lesquelles votre programme doit "dormir". C'est une simple mise en pause.

Par exemple, si vous voulez que votre programme se mette en pause 1 seconde, vous devrez taper :

Code : C
1
SDL_Delay(1000);


N'oubliez pas que ce sont des millisecondes :
1000 millisecondes = 1 seconde
500 millisecondes = 1/2 seconde
250 millisecondes = 1/4 seconde
etc etc.

Attention : vous ne pouvez rien faire dans votre programme pendant qu'il est en pause ! Un programme qui "dort" ne peut rien faire puisqu'il n'est pas actif pour l'ordinateur.


Le problème de la granularité du temps



Non rassurez-vous, je ne vais pas vous faire un traité de physique quantique au beau milieu d'un chapitre SDL :p
Toutefois, il y a quelque chose que j'estime que vous devez savoir : SDL_Delay n'est pas une fonction "parfaite". Et ce n'est pas sa faute, c'est la faute à votre OS (comme Windows).

Qu'est-ce que vient faire l'OS là-dedans ?

Oh mais l'OS a tout à faire là-dedans : c'est lui qui contrôle les programmes qui tournent !
Votre programme va donc dire à l'OS : "Je dors, réveille-moi dans 1 seconde". Mais l'OS ne va pas forcément le réveiller au bout d'une seconde exactement.

En effet, il aura peut-être un peu de retard (un retard de 10ms en moyenne environ, ça dépend des PC). Pourquoi ? Parce que votre CPU ne peut travailler que sur un programme à la fois. Le rôle de l'OS est de dire au CPU ce sur quoi il doit travailler : "Alors, pendant 40ms tu vas travailler sur firefox.exe, puis pendant 110ms tu vas travailler sur explorer.exe, et ensuite pendant 80ms tu vas travailler sur programme_sdl.exe, puis tu vas retravailler sur firefox.exe pendant 65ms" etc etc...
L'OS est le véritable chef d'orchestre de l'ordinateur !

Maintenant, imaginez qu'au bout d'une seconde un autre programme soit encore en train de travailler : il faudra qu'il ait fini de travailler pour que votre programme puisse "reprendre la main" comme on dit, c'est-à-dire être traité à nouveau par le CPU.

Tout ça pour dire quoi ? o_O


Ooops, excusez-moi j'étais en train de dériver :-°
En gros, j'essayais de vous expliquer que votre CPU ne pouvait pas gérer plus d'un programme à la fois. Pour donner l'impression que l'on peut faire tourner plusieurs programmes en même temps sur un ordinateur, l'OS "découpe" le temps et autorise les programmes à travailler au tour par tour.

Or, cette gestion des programmes est très complexe et on ne peut donc pas avoir la garantie que notre programme sera réveillé au bout d'une seconde exactement.

Toutefois, ça dépend des PC comme je vous l'ai dit plus haut. Chez moi, la fonction SDL_Delay est assez précise.

A cause de ce problème de "granularité du temps", vous ne pouvez donc pas mettre en pause votre programme pendant un temps trop court. Par exemple, si vous faites :
Code : C
1
SDL_Delay(1);

Vous pouvez être sûrs que votre programme ne sera pas mis en pause 1ms mais un peu plus (peut-être 9-10ms).


En résumé : SDL_Delay c'est cool, mais ne lui faites pas trop confiance. Elle ne mettra pas en pause votre programme pendant le temps exact que vous indiquez.
Ce n'est pas parce que la fonction est mal codée, c'est parce que le fonctionnement d'un ordinateur est très complexe et ne permet pas d'être très précis à ce niveau.


SDL_GetTicks



Cette fonction renvoie le nombre de millisecondes écoulées depuis le lancement du programme.

Hein ? Mais on s'en fout de savoir ça :p


Au contraire, c'est un indicateur de temps indispensable. Cela vous permet de vous repérer dans le temps, vous allez voir !

Voici le prototype :

Code : C
1
Uint32 SDL_GetTicks(void);


La fonction n'attend aucun paramètre, elle renvoie juste le nombre de millisecondes écoulées.
Ce nombre augmente au fur et à mesure du temps, inlassablement. Pour info, la doc de la SDL indique que le nombre atteint son maximum et est réinitialisé au bout de 49 jours ! A priori votre programme SDL devrait tourner moins longtemps que ça, donc pas de souci de ce côté-là ^^


Utiliser SDL_GetTicks pour gérer le temps



Si SDL_Delay est assez facile à comprendre et à utiliser, ce n'est pas le cas de SDL_GetTicks. Il est temps d'apprendre à bien s'en servir...
Voici un exemple ! Nous allons reprendre notre bon vieux programme avec la fenêtre affichant Zozor à l'écran. Souvenez-vous :

Image utilisateur


Cette fois, au lieu de le diriger au clavier ou à la souris, nous allons faire en sorte qu'il bouge tout seul sur l'écran !
Pour faire simple, on va le faire bouger horizontalement sur la fenêtre.

On reprend pour commencer exactement le même code source que celui qu'on avait utilisé dans le chapitre sur les évènements. Cette fois, j'ai enlevé la partie gérant les évènements au clavier vu qu'on ne va plus bouger Zozor au clavier :

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
int main(int argc, char *argv[])
{
    SDL_Surface *ecran = NULL, *zozor = NULL;
    SDL_Rect positionZozor;
    SDL_Event event;
    int continuer = 1;

    SDL_Init(SDL_INIT_VIDEO);

    ecran = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
    SDL_WM_SetCaption("Gestion du temps en SDL", NULL);

    zozor = SDL_LoadBMP("zozor.bmp");
    SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor->format, 0, 0, 255));

    positionZozor.x = ecran->w / 2 - zozor->w / 2;
    positionZozor.y = ecran->h / 2 - zozor->h / 2;

    SDL_EnableKeyRepeat(10, 10);

    while (continuer)
    {
        SDL_WaitEvent(&event);
        switch(event.type)
        {
            case SDL_QUIT:
                continuer = 0;
                break;
        }

        SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 255, 255, 255));
        SDL_BlitSurface(zozor, NULL, ecran, &positionZozor);
        SDL_Flip(ecran);
    }

    SDL_FreeSurface(zozor);
    SDL_Quit();

    return EXIT_SUCCESS;
}


Bon, si vous testez ce code, vous devriez voir un Zozor au centre de la fenêtre. Rien de bien palpitant encore.

Nous voulons le faire bouger. Pour cela, le mieux est d'utiliser SDL_GetTicks. On va avoir besoin de 2 variables : tempsPrecedent et tempsActuel. Elles vont stocker le temps retourné par SDL_GetTicks à des moments différents.
Il nous suffira de faire la différence entre tempsActuel et tempsPrecedent pour voir le temps qui s'est écoulé. Si le temps écoulé est supérieur à 30ms par exemple, alors on change les coordonnées de Zozor.

Commencez donc par créer ces 2 variables dont on va avoir besoin :

Code : C
1
int tempsPrecedent = 0, tempsActuel = 0;


Maintenant, dans notre boucle infinie, nous allons rajouter le code suivant :

Code : C
1
2
3
4
5
6
tempsActuel = SDL_GetTicks();
if (tempsActuel - tempsPrecedent > 30) /* Si 30 ms se sont écoulées */
{
    positionZozor.x++; /* On bouge Zozor */
    tempsPrecedent = tempsActuel; /* Le temps "actuel" devient le temps "precedent" pour nos futurs calculs */
}


Attention, là c'est super important. C'est là qu'on comprend le truc (ou pas :D )

  1. On prend le temps actuel grâce à SDL_GetTicks
  2. On compare au temps précedemment enregistré. Si il y a un écart de 30 ms au moins, alors...
  3. ... alors on bouge Zozor, car on veut qu'il se déplace toutes les 30 ms. Ici, on le décale juste vers la droite toutes les 30 ms.
    Il faut vérifier si le temps est supérieur à 30ms, et non égal à 30ms ! En effet, il faut vérifier si au moins 30ms se sont écoulées. Rien ne vous garantit que l'instruction sera exécutée pile poil toutes les 30ms ;)
  4. Puis (et c'est vraiment le truc à pas oublier), on place le temps "actuel" dans le temps "précédent". En effet, imaginez au prochain tour de boucle : le temps "actuel" aura changé, et on pourra le comparer au temps précédent. A nouveau, on pourra vérifier si 30 ms se seront écoulées et bouger Zozor


Et que se passe-t-il si la boucle va plus vite que 30 ms ?


Lisez mon code : il ne se passe rien :p
On ne rentre pas dans le "if", on ne fait donc rien. On attend le prochain tour de boucle où on vérifiera à nouveau si 30 ms se seront écoulées depuis la dernière fois qu'on a fait bouger Zozor.

Ce code est court, mais il faut le comprendre ! Relisez mes explications autant de fois que nécessaire, parce que c'était ça le passage le plus important du chapitre ;)


Un changement dans la gestion des évènements



Notre code est presque bon à un détail près : la fonction SDL_WaitEvent.
Elle était très pratique jusqu'ici, puisqu'on n'avait pas à gérer le temps. Cette fonction mettait en "pause" le programme (un peu à la manière de SDL_Delay) tant qu'il n'y avait pas d'évènement.

Or ici, on n'a pas besoin d'attendre un évènement pour faire bouger Zozor ! Il doit bouger tout seul.
Vous n'allez quand même pas bouger la souris juste pour générer des évènements et donc faire sortir le programme de la fonction SDL_WaitEvent :lol:

La solution ? SDL_PollEvent.
Je vous avais déjà présenté cette fonction : contrairement à SDL_WaitEvent, elle renvoie une valeur qu'il y ait eu un évènement ou pas. On dit que la fonction n'est pas "bloquante" : elle ne met pas en pause le programme, la boucle infinie va donc tourner indéfiniment tout le temps.


Code complet



Voici le code final que vous pouvez tester :

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
int main(int argc, char *argv[])
{
    SDL_Surface *ecran = NULL, *zozor = NULL;
    SDL_Rect positionZozor;
    SDL_Event event;
    int continuer = 1;
    int tempsPrecedent = 0, tempsActuel = 0;

    SDL_Init(SDL_INIT_VIDEO);

    ecran = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
    SDL_WM_SetCaption("Gestion du temps en SDL", NULL);

    zozor = SDL_LoadBMP("zozor.bmp");
    SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor->format, 0, 0, 255));

    positionZozor.x = ecran->w / 2 - zozor->w / 2;
    positionZozor.y = ecran->h / 2 - zozor->h / 2;

    SDL_EnableKeyRepeat(10, 10);


    while (continuer)
    {
        SDL_PollEvent(&event); /* On utilise PollEvent et non WaitEvent pour ne pas bloquer le programme */
        switch(event.type)
        {
            case SDL_QUIT:
                continuer = 0;
                break;
        }

        tempsActuel = SDL_GetTicks();
        if (tempsActuel - tempsPrecedent > 30) /* Si 30 ms se sont écoulées depuis le dernier tour de boucle */
        {
            positionZozor.x++; /* On bouge Zozor */
            tempsPrecedent = tempsActuel; /* Le temps "actuel" devient le temps "precedent" pour nos futurs calculs */
        }


        SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 255, 255, 255));
        SDL_BlitSurface(zozor, NULL, ecran, &positionZozor);
        SDL_Flip(ecran);
    }

    SDL_FreeSurface(zozor);
    SDL_Quit();

    return EXIT_SUCCESS;
}


Vous devriez voir Zozor bouger tout seul sur l'écran. Il se décale vers la droite :)
Essayez de changer le temps de 30ms en 15ms par exemple : Zozor devrait se déplacer 2 fois plus vite ! En effet, il se déplacera une fois toutes les 15ms au lieu d'une fois toutes les 30ms auparavant ;)


Consommer moins de CPU



Actuellement, notre programme tourne en boucle indéfiniment à la vitesse de la lumière (enfin presque). Il consomme donc 100% du CPU.
Si vous faites CTRL + ALT + SUPPR (onglet "Processus"), vous verrez ça sous Windows :

Image utilisateur


Comme vous pouvez le voir, notre CPU est utilisé à 100% par notre programme testsdl.exe.
Je vous l'ai dit plus tôt : si vous codez un jeu (surtout un jeu plein écran), ce n'est pas grave si vous utilisez 100% du CPU. Mais si c'est un jeu dans une fenêtre par exemple, il vaut mieux qu'il utilise le moins de CPU possible pour que l'utilisateur puisse faire autre chose sans que son PC ne "rame".

La solution ? On va reprendre exactement le même code que ci-dessus mais on va lui ajouter en plus un SDL_Delay pour patienter le temps qu'il faut afin que ça fasse 30ms.

On va juste rajouter un SDL_Delay dans un "else" :

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
tempsActuel = SDL_GetTicks();
if (tempsActuel - tempsPrecedent > 30)
{
    positionZozor.x++;
    tempsPrecedent = tempsActuel;
}
else /* Si ça fait moins de 30ms depuis le dernier tour de boucle, on endort le programme le temps qu'il faut */
{
    SDL_Delay(30 - (tempsActuel - tempsPrecedent));
}


Comment ça fonctionne cette fois ?
C'est simple, il y a 2 possibilités (d'après le if) :



Rappelez-vous que SDL_Delay mettra peut-être quelques millisecondes de plus que prévu. Chez moi, comme je vous l'ai dit, c'est assez précis.


Du coup, notre programme va "dormir" la plupart du temps et donc consommer très peu de CPU. Regardez !

Image utilisateur


En moyenne, le programme utilise 0-1% de CPU... Parfois il utilise légèrement plus, mais il retombe rapidement à 0% de CPU.


Contrôler le nombre d'images par seconde



Vous vous demandez certainement comment on peut limiter (fixer) le nombre d'images par seconde (FPS) affichées par l'ordinateur.

Eh bien c'est exactement ce qu'on est en train de faire ! Ici, on affiche une nouvelle image toutes les 30ms en moyenne.
Sachant qu'une seconde vaut 1000ms, pour trouver le nombre de FPS (images par seconde), il suffit de faire une bête division : 1000 / 30 = 33 images par seconde environ.

Pour l'oeil humain, une animation est fluide si elle contient au moins 25 images / seconde. Avec 33 images / seconde, notre animation sera donc tout à fait fluide, elle n'apparaîtra pas "saccadée".

Si vous voulez plus d'images par seconde, il faut réduire la limite de temps entre 2 images. Passez de 30 à 20ms, et ça vous fera du 1000 / 20 = 50 FPS ;)

Exercices



La manipulation du temps n'est pas évidente, il serait bien de vous entraîner un peu, qu'en dites-vous ?
Voici quelques exercices justement :



Voilà, ce sont quelques petits exos comme ça qui devraient vous occuper quelques minutes et vous permettre ainsi d'améliorer le programme :)

Les Timers

L'utilisation des Timers est un peu complexe. Elle fait intervenir une notion qu'on n'a pas vue jusqu'ici : les pointeurs de fonctions.
Il n'est pas indispensable d'utiliser les Timers : si vous ne le sentez pas, vous pouvez donc passer votre chemin sans souci ;)


Les Timers sont une autre façon de réaliser ce qu'on vient de faire avec SDL_GetTicks.
C'est une technique un peu particulière. Certains la trouveront pratique, d'autres pas. Ca dépend donc des goûts du programmeur :p

Qu'est-ce qu'un Timer ?
C'est un système qui permet de demander à la SDL d'appeler une fonction toutes les X millisecondes. Vous pourriez ainsi créer une fonction bougerEnnemi() que la SDL appellerait automatiquement toutes les 50ms afin que l'ennemi se déplace à intervalles réguliers.

Comme je viens de vous le dire, cela est aussi faisable avec SDL_GetTicks en utilisant la technique qu'on a vue plus haut.
Quel avantage alors ? Eh bien disons que les Timers nous obligent à mieux structurer notre programme en fonctions.



Initialiser le système de Timers



Pour pouvoir utiliser les Timers, vous devez d'abord initialiser la SDL avec un flag spécial : SDL_INIT_TIMER.
Vous devriez donc avoir une fonction SDL_Init appelée comme ceci :

Code : C
1
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);


Voilà, la SDL est maintenant prête à utiliser les Timers :)



Ajouter un Timer



Il existe 2 fonctions permettant d'ajouter un Timer en SDL : SDL_AddTimer et SDL_SetTimer.
Quelle est la différence entre les 2 ? En fait, elles sont quasiment identiques. Cependant, SDL_SetTimer est une fonction "ancienne" qui existe toujours pour des raisons de compatibilité. Aujourd'hui, si on veut bien faire les choses, on nous recommande donc d'utiliser SDL_AddTimer :)

Alors attention, c'est là que ça se corse. Voici le prototype de SDL_AddTimer :

Code : C
1
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param);


On envoie 3 paramètres à la fonction :



QUOIII ? Un nom de fonction peut servir de paramètre ? Je croyais qu'on ne pouvait envoyer que des variables !?


En fait, les fonctions sont aussi stockées en mémoire au chargement du programme. Elles ont donc elles aussi une adresse.
Du coup, on peut créer des... pointeurs de fonctions ! Il suffit d'écrire le nom de la fonction à appeler pour indiquer l'adresse de la fonction. Ainsi, la SDL saura à quelle adresse en mémoire elle doit se rendre pour appeler votre fonction de callback.

Si vous voulez en savoir plus sur les pointeurs de fonctions, je vous conseille de lire le tuto rédigé par mleg à ce sujet.


SDL_AddTimer renvoie un numéro de Timer (un "ID"). Vous devez stocker ce résultat dans une variable de type SDL_TimerID. Cela vous permettra par la suite de désactiver le Timer : il vous suffira d'indiquer l'ID du Timer à arrêter.

La SDL vous permet d'activer plusieurs Timers en même temps. Cela explique l'intérêt de stocker un ID de Timer : on peut ainsi savoir par la suite quel est le Timer qu'on demande à arrêter :)


On va donc créer un ID de Timer :

Code : C
1
SDL_TimerID timer; /* Variable pour stocker le numéro du Timer */


... puis on va créer notre Timer :

Code : C
1
timer = SDL_AddTimer(30, bougerZozor, &positionZozor); /* Démarrage du Timer */


Ici, je crée un Timer qui a les propriétés suivantes :



Vous l'aurez compris : le rôle de la fonction bougerZozor sera de changer la position de Zozor toutes les 30ms :)


Création de la fonction de callback



Attention là il ne faut pas se planter. Votre fonction de callback doit obligatoirement avoir le prototype suivant :

Code : C
1
Uint32 nomDeLaFonction(Uint32 intervalle, void *parametre);


Pour créer le callback bougerZozor, je devrai donc écrire la fonction comme ceci :

Code : C
1
Uint32 bougerZozor(Uint32 intervalle, void *parametre);


Voici maintenant le contenu de ma fonction bougerZozor (ouvrez grand les yeux c'est très intéressant ;) ) :

Code : C
1
2
3
4
5
6
7
8
/* Fonction de callback (sera appelée toutes les 30ms) */
Uint32 bougerZozor(Uint32 intervalle, void *parametre)
{
    SDL_Rect* positionZozor = parametre; /* Conversion de void* en SDL_Rect* */
    positionZozor->x++;

    return intervalle;
}


La fonction bougerZozor sera donc automatiquement appelée toutes les 30ms par la SDL.
La SDL lui enverra toujours 2 paramètres (ni plus, ni moins) :



Le problème, c'est que ce paramètre est un pointeur de type "inconnu" (void) pour la fonction. Il va donc falloir dire à l'ordinateur que ce paramètre est un SDL_Rect* (un pointeur sur SDL_Rect).

Pour faire ça, je crée un pointeur sur SDL_Rect dans ma fonction qui prend comme valeur... le pointeur parametre.

Quel intérêt d'avoir créé un DEUXIEME pointeur qui contient la même adresse ?


L'intérêt, c'est que positionZozor est de type SDL_Rect* contrairement à parametre qui était de type void*.

Vous pourrez donc accéder à positionZozor->x et positionZozor->y.
Si vous aviez fait parametre->x ou parametre->y, le compilateur vous aurait jeté parce que le type void ne contient pas de sous-variable x et y ;)

Après, la ligne suivante est simple : on modifie la valeur de positionZozor->x pour décaler Zozor vers la droite.

Dernière chose, très importante : vous devez retourner la variable intervalle. Cela indiquera à la SDL qu'on veut continuer à ce que la fonction soit appelée toutes les 30ms.
Si vous voulez changer l'intervalle d'appel, il suffit de renvoyer une autre valeur (mais bien souvent, on ne change pas l'intervalle d'appel ^^ )


Arrêter le Timer



Pour arrêter le Timer, c'est super simple :

Code : C
1
SDL_RemoveTimer(timer); /* Arrêt du Timer */


Il suffit d'appeler SDL_RemoveTimer en indiquant l'ID du Timer à arrêter.
Ici, j'arrête le Timer juste après la boucle infinie (au même endroit que les SDL_FreeSurface quoi ;) ).


Code complet d'exemple



Allez, on résume tout ça pour mettre nos idées au clair. Toutes les lignes intéressantes sont commentées :

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
#include <stdlib.h>
#include <stdio.h>
#include <SDL/SDL.h>

/* Prototype de la fonction de callback */
Uint32 bougerZozor(Uint32 intervalle, void *parametre);

int main(int argc, char *argv[])
{
    SDL_Surface *ecran = NULL, *zozor = NULL;
    SDL_Rect positionZozor;
    SDL_Event event;
    int continuer = 1;
    SDL_TimerID timer; /* Variable pour stocker le numéro du Timer */

    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);

    ecran = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
    SDL_WM_SetCaption("Gestion du temps en SDL", NULL);

    zozor = SDL_LoadBMP("zozor.bmp");
    SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor->format, 0, 0, 255));

    positionZozor.x = ecran->w / 2 - zozor->w / 2;
    positionZozor.y = ecran->h / 2 - zozor->h / 2;

    SDL_EnableKeyRepeat(10, 10);

    timer = SDL_AddTimer(30, bougerZozor, &positionZozor); /* Démarrage du Timer */

    while (continuer)
    {
        SDL_PollEvent(&event);
        switch(event.type)
        {
            case SDL_QUIT:
                continuer = 0;
                break;
        }


        SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 255, 255, 255));
        SDL_BlitSurface(zozor, NULL, ecran, &positionZozor);
        SDL_Flip(ecran);
    }

    SDL_RemoveTimer(timer); /* Arrêt du Timer */

    SDL_FreeSurface(zozor);
    SDL_Quit();

    return EXIT_SUCCESS;
}

/* Fonction de callback (sera appelée toutes les 30ms) */
Uint32 bougerZozor(Uint32 intervalle, void *parametre)
{
    SDL_Rect* positionZozor = parametre; /* Conversion de void* en SDL_Rect* */
    positionZozor->x++;

    return intervalle;
}


Vous devriez voir Zozor bouger toutes les 30ms. Cette fois, le déplacement est géré par un Timer qui appelle une fonction de callback à intervalle de temps régulier (ouah ça fait pro cette phrase vous trouvez pas :D )

Sur un petit exemple comme ça, il peut sembler plus simple d'utiliser SDL_GetTicks. Je suis bien d'accord. Mais sur de gros programmes, vous aurez très probablement des fonctions de callback plus longues et complexes à créer, ce qui rendra l'utilisation de Timers plus intéressante ;)

Au fait... On ne peut envoyer qu'un seul paramètre à la fonction de callback ?


Oui. Comme vous le voyez, le seul paramètre qu'on peut envoyer est : void *parametre.
Ce void* peut être un pointeur sur int, sur SDL_Rect, sur ce que vous voulez quoi :)

Si vous voulez envoyer plusieurs variables à la fois, c'est tout à fait possible : créez une structure personnalisée qui contiendra les variables que vous voulez. C'est d'ailleurs ce qu'on fait avec notre positionZozor qui contient les sous-variables x et y !

Q.C.M.

Que renvoie SDL_GetTicks ?
Quel est le problème de ce code source ?




Code : C
1
2
3
4
5
6
tempsActuel = SDL_GetTicks();
if (tempsActuel - tempsPrecedent > 20)
{
    position.y++;
    position.x--;
}
Pourquoi SDL_Delay(20) ne va pas forcément "endormir" le programme exactement 20ms ?
Que doit renvoyer la fonction de callback ?

Statistiques de réponses au QCM


Il n'y a pas beaucoup de fonctions à connaître pour gérer le temps en SDL. Par contre, bien savoir les utiliser est une autre paire de manches : il va falloir que vous pratiquiez pour vous sentir à l'aise.
Grâce à ces fonctions, on peut faire bouger automatiquement des personnages à l'écran (comme des ennemis) mais aussi gérer les FPS (nombre d'images par seconde) pour optimiser son programme. Bref, on peut faire plein de choses ! A vous de bien vous organiser :)

Je vous conseille vivement de vous entraîner : je vous ai déjà donné quelques idées d'exercices plus haut, inventez-en d'autres !
Vous pourriez par exemple créer un jeu du snake maintenant. Vous savez, ce petit serpent qui doit manger des pommes dans un labyrinthe sans se toucher lui-même ^^
En fonction du délai entre 2 déplacements du serpent, la vitesse sera plus ou moins élevée !

Image utilisateur
Aperçu d'un jeu de Snake


Bon codage ;)
Chapitre précédent Sommaire Chapitre suivant
Retour en haut Retour en haut


Créé : le 29/07/2005 à 00:29:36
Modifié : le 13/09/2008 à 17:05:10
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 284 Zéros connectés | Requêtes SQL 9 requêtes | Temps de génération de la page : Total (SQL) 0.0618s (0.0439s)