La gestion du temps dans la SDL

Fermé
Brunot9 Messages postés 6 Date d'inscription jeudi 23 août 2018 Statut Membre Dernière intervention 31 août 2018 - Modifié le 31 août 2018 à 19:21
 Brunot9 - 7 sept. 2018 à 23:31
Bonjour,

J'ai mis le déplacement du personnage dans la condition mais je n'ai pas bien compris si la fonction
SDL_GetTicks()
influence uniquement le déplacement du personnage dans cette même condition ou si elle influence tout le code ? par exemple est ce ça qui déterminera le nombre de FPS de l'ensemble du code ? si j'introduisais d'autres personnages, est ce qu'il faudrait créer d'autres conditions avec
SDL_GetTicks()
ou faut il mieux utiliser des timers comme tu en as parler précédemment ?
J'ai essayé ce que tu as dit pour le déplacement en retard du viseur mais le décalage est toujours là (si j'ai bien fait)
#include <SDL2/SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <SDL2/SDL_image.h>

#include "constante.h"

char* table[] = {
"000000000000000",
"000000000000000",
"000000000000000",
"000000000000000",
"000000000000000",
"000000000000000",
"000000000000000",
"100000000111110",
"000000000000000",
"000000000000000",
"000000000000000",
"003400022220022",
"005600000000000",
"005600000000000",
"005600000000000",
"777777777777777"};

int setWindowColor(SDL_Renderer *afficheur, SDL_Color color)
{
    if (SDL_SetRenderDrawColor(afficheur, color.r, color.g, color.b, color.a) < 0)
        return -1;
    if (SDL_RenderClear(afficheur) < 0)
        return -1;
    return 0;
}

void Afficher(SDL_Renderer* afficheur, SDL_Texture* textuTil,
              char** table, int nombre_blocs_largeur, int nombre_blocs_hauteur)
{
	int i, j;
	SDL_Rect Rect_dest;
	SDL_Rect Rect_source;
	Rect_source.w = LARGEUR_TILE;
	Rect_dest.w   = LARGEUR_TILE;
	Rect_source.h = HAUTEUR_TILE;
	Rect_dest.h   = HAUTEUR_TILE;
	for(i = 0 ; i < nombre_blocs_largeur ; i++)
	{
		for(j = 0 ; j < nombre_blocs_hauteur ; j++)
		{
			Rect_dest.x = i * LARGEUR_TILE;
			Rect_dest.y = j * HAUTEUR_TILE;
			Rect_source.x = (table[j][i] - '0') * LARGEUR_TILE;
			Rect_source.y = 0;
            SDL_RenderCopy(afficheur, textuTil, &Rect_source, &Rect_dest);
		}
	}
}

int main(int argc, char *argv[])
{
    SDL_Window *ecran = NULL;
    SDL_Renderer *afficheur = NULL;
    SDL_Surface *tileset = NULL, *image = NULL;
    SDL_Surface *imageActu = NULL, *viseur = NULL;
    SDL_Texture *texture, *textuViseur, *textuTil;

    SDL_Rect destHomer = {50, 50, 60, 90};
    SDL_Rect destViseur = {100, 100, 30, 30};

    SDL_Color orange = {255, 127, 40, 255};
    SDL_Event event;

    int continuer = 1, tempsActuel = 0, tempsPrecedent = 0, versLaDroite = 1;
    int statut = EXIT_FAILURE;


    if (SDL_Init(SDL_INIT_VIDEO) != 0)
        {
            fprintf(stderr, "Erreur SDL_Init : %s", SDL_GetError());
            goto Quit;
        }

    ecran = SDL_CreateWindow("Kill Homer !", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
                             LARGEUR_TILE*NOMBRE_BLOCS_LARGEUR, HAUTEUR_TILE*NOMBRE_BLOCS_HAUTEUR, SDL_WINDOW_SHOWN);
    if (ecran == NULL)
        {
            fprintf(stderr, "Erreur SDL_CreateTextureFromSurface : %s", SDL_GetError());
            goto Quit;
        }

    afficheur = SDL_CreateRenderer(ecran, -1, SDL_RENDERER_ACCELERATED);
    if (afficheur == NULL)
        {
            fprintf(stderr, "Erreur SDL_CreateRenderer : %s", SDL_GetError());
            goto Quit;
        }

    image = IMG_Load("homer.png");
    if (image == NULL)
        {
            fprintf(stderr, "Erreur SDL_CreateTexture : %s", SDL_GetError());
            goto Quit;
        }

    tileset = IMG_Load("tileset2.bmp");
    if (tileset == NULL)
        {
            fprintf(stderr, "Erreur SDL_CreateTexture : %s", SDL_GetError());
            goto Quit;
        }

    texture = SDL_CreateTextureFromSurface(afficheur, image);
    if (texture == NULL)
        {
            fprintf(stderr, "Erreur SDL_CreateTexture : %s", SDL_GetError());
            goto Quit;
        }

    viseur = IMG_Load("viseur.png");
    if (viseur == NULL)
        {
            fprintf(stderr, "Erreur SDL_CreateTexture viseur: %s", SDL_GetError());
            goto Quit;
        }


    textuViseur = SDL_CreateTextureFromSurface(afficheur, viseur);
    if (textuViseur == NULL)
        {
            fprintf(stderr, "Erreur SDL_CreateTexture viseur: %s", SDL_GetError());
            goto Quit;
        }

    textuTil = SDL_CreateTextureFromSurface(afficheur, tileset);
    if (textuTil == NULL)
        {
            fprintf(stderr, "Erreur SDL_CreateTexture sprite: %s", SDL_GetError());
            goto Quit;
        }

    SDL_FreeSurface(tileset);

    SDL_RenderClear(afficheur);

    setWindowColor(afficheur, orange);


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

                case SDL_MOUSEMOTION:
                    destViseur.x = event.motion.x - (viseur->w / 2);
                    destViseur.y = event.motion.y - (viseur->h / 2);
                    break;

                case SDL_KEYDOWN:
                    switch(event.key.keysym.sym)
                    {
                        case SDLK_ESCAPE:
                            continuer = 0;
                            break;
                    }
                    break;
            }
        }
    Afficher(afficheur, textuTil, table, NOMBRE_BLOCS_LARGEUR, NOMBRE_BLOCS_HAUTEUR);

    tempsActuel = SDL_GetTicks();

    if (tempsActuel - tempsPrecedent > 30)  /* si 30ms se sont écoulé depuis le dernier tour de la boucle
        plus le temps est petit plus il se deplace vite */
    {

        if (versLaDroite)
        {
            if (destHomer.x >= 790)
            {
                versLaDroite = 0;
            }
             else
             {
                 destHomer.x++;
             }
        }
        else
        {
            if (destHomer.x <= 0)
                {
                    versLaDroite = 1;
                }
            else
                {
                    destHomer.x--;
                }
        }
        tempsPrecedent = tempsActuel;  // le temps actuel devient le temps présent
    }
    else
    {
        SDL_Delay(30 - (tempsActuel - tempsPrecedent));
    }

    SDL_RenderCopy(afficheur, texture, NULL, &destHomer); // copie de image grâce au SDL_Renderer
    SDL_RenderCopy(afficheur, textuViseur, NULL, &destViseur);
    SDL_RenderPresent(afficheur); //  Affichage des textures

    }

    SDL_FreeSurface (image);
    SDL_FreeSurface (viseur);


    Quit:
        if (NULL != textuTil)
            SDL_DestroyTexture(textuTil);
        if(NULL != afficheur)
            SDL_DestroyRenderer(afficheur);
        if(NULL != ecran)
            SDL_DestroyWindow(ecran);
    SDL_Quit();
    return statut;
}



4 réponses

codeurh24 Messages postés 761 Date d'inscription samedi 29 mars 2014 Statut Membre Dernière intervention 8 septembre 2018 123
1 sept. 2018 à 15:45
Bonjour, a qui parle tu ?

SDL_GetTicks() infuance tout le code parce qu’il contrôle le nombre de rendu avec SDL_RenderPresent(afficheur);
Ton while est donc aussi réguler par SDL_GetTicks() a cause de SDL_Delay();


Tes conditions de déplacement ne devrait pas être dans la condition du temps car ça le rend peu lisible.
La boucle régule déjà le temps, il suffis donc de placer tes conditions de déplacement dans le while.

Pour créer une autre image qui se déplace tu devra créer d'autres variables qui utilise SDL_RenderCopy(afficheur, texture, NULL, &destHomer);

A un moment tu ne pourra plus gérer le code devenu trop grand, tu devra passer par de l'allocation mémoire (tableau dynamique) et simplifier ton code par des fonctions.
0
Bonjour,

Oups désolé.

Tu veux dire que je devrais mettre le
if (versLaDroite)
dans
while (SDL_PollEvent(&event))
?

Donc si je met d'autres images qui se déplacent, elles seront aussi influencé par le
SDL_GetTicks()
même si ils ne sont pas à l'intérieur du
if (tempsActuel - tempsPrecedent > 30)
, c'est bien ça ?

Je ne connais pas les timers, mais est ce que ce n'est pas mieux pour gérer le déplacement de plusieurs image ?
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié le 4 sept. 2018 à 11:48
Salut Brunot9,

Lorsque tu postes un nouveau sujet, tu t'adresses au forum :-)

 SDL_GetTicks()
n'effectue en elle-même aucune temporisation. Elle renvoie juste un Uint32 représentant le nombre de millisecondes écoulées depuis l'initialisation de la SDL : https://wiki.libsdl.org/SDL_GetTicks

Ta temporisation est gérée dans ton code :

1- par
if (tempsActuel - tempsPrecedent > 30)
qui te sert, en réalité à gérer la temporisation du déplacement de "Homer",

2- et par le
SDL_Delay
qui gère la temporisation de la boucle générale en la ralentissant pour éviter à ton programme de consommer trop de temps de calcul CPU

C'est cette dernière temporisation qui détermine le nombre de FPS de ton jeu.

Avec ton
while (SDL_PollEvent(&event))
, par rapport à ce que tu avais avant dans ton post précédent, tu as déjà dû constater une amélioration sensible du déplacement du sprite qui suit la souris, car tu traites tous les événements souris à chaque itération de la boucle.

Si tu perçois des effets de saccades sur le sprite suivant la souris, cela est simplement dû au fait que tu en rafraîchis l'affichage toutes les 30 ms, ce qui est probablement insuffisant, vu que la souris est déplacée par l'utilisateur plus vite que le déplacement du personnage.

Pour fluidifier cela, et si tu tiens à ce qu'un sprite suive le curseur de ta souris, tu n'as pas d'autre choix que d'accélérer la boucle générale.

Voilà un exemple :

    /* variables de gestion du temps */
    Uint32 tempsActuel = 0;
    Uint32 tempsPrecedentBoucle = SDL_GetTicks();
    Uint32 tempoBoucle = 10;
    Uint32 tempsPrecedentHomer = 0;
    Uint32 tempoHomer = 30;
    Uint32 tempsPrecedentBart = 0;
    Uint32 tempoBart= 20;                                                                                                                                                                                          

    while(continuer) {
        while (SDL_PollEvent(&event)) {
            switch(event.type) {
            case SDL_QUIT:
                continuer = 0;
                break;
            case SDL_MOUSEMOTION:
                destViseur.x = event.motion.x - (viseur->w / 2);
                destViseur.y = event.motion.y - (viseur->h / 2);
                break;
            case SDL_KEYDOWN:
                switch(event.key.keysym.sym) {
                case SDLK_ESCAPE:
                    continuer = 0;
                    break;
                }
                break;
            }
        }
        Afficher(afficheur, textuTil, table, NOMBRE_BLOCS_LARGEUR, NOMBRE_BLOCS_HAUTEUR);

        tempsActuel = SDL_GetTicks();

        /* homer se déplace toutes les 30 ms */
        if (tempsActuel - tempsPrecedentHomer > tempoHomer) {
            if (versLaDroite) {
                if (destHomer.x >= 790) {
                    versLaDroite = 0;
                } else {
                    destHomer.x++;
                }
            } else {
                if (destHomer.x <= 0) {
                    versLaDroite = 1;
                } else {
                    destHomer.x--;
                }
            }
            tempsPrecedentHomer = tempsActuel;  // le temps actuel devient le temps présent
        }
        /* Bart se déplace toutes les 20 ms */
        if (tempsActuel - tempsPrecedentBart > tempoBart) {
            /* faire déplacement Bart */
        }
        /* la boucle générale tourne toutes les 10 ms
         * on ralentit la boucle dans cette limite pour modérer 
         * l'utilisation du CPU, ce ralentissement affecte aussi 
         * l'affichage du sprite suivant la souris, qui sera
         * mis à jour au plus à ce rythme
         */
        if (tempsActuel - tempsPrecedentBoucle < tempoBoucle) {
            SDL_Delay(tempoBoucle - (tempsActuel - tempsPrecedentBoucle));
            tempsPrecedentBoucle = tempsActuel;
        }

        SDL_RenderCopy(afficheur, texture, NULL, &destHomer); // copie de image grâce au SDL_Renderer
        SDL_RenderCopy(afficheur, textuViseur, NULL, &destViseur);
        SDL_RenderPresent(afficheur); //  Affichage des textures

    }

Ce code te montre comment établir le temps de ta boucle générale déterminant ton nombre de FPS, et comment animer des objets à différentes allures (plus lentes ou égales à celle de la boucle générale).

Avec cet exemple, le sprite suivant le curseur de la souris devrait être bien plus fluide. C'est, bien sûr au détriment de la consommation de temps CPU par ton programme.

Le mieux, pour la souris est une gestion par la SDL de celle-ci, et d'utiliser les fonctions de la SDL permettant de changer le curseur de la souris.

C'est plus contraignant, mais tu décharges ton programme de cette tâche, tu simplifies ta boucle générale (tu ne géreras plus que les clics de souris et non les déplacements et affichage, ces derniers étant gérés par la SDL), et tu peux réserver l'utilisation de ressources CPU pour autre chose de plus constructif, ou faire revenir le nombre de FPS à quelque chose de plus lent.

Un exemple figure dans la doc de la SDL ici :

https://wiki.libsdl.org/SDL_CreateCursor

Tu pourrais coder ton "viseur" en tant que curseur de souris de cette même façon.

Le format est en réalité un format XPM, qui est un format de description d'image en ASCII destiné à en faciliter l'intégration dans du code C. Tu peux créer des images en ce format texte avec The Gimp, par exemple.

La largeur de l'image doit être un multiple de 8 et, de mémoire, il y a des limitations de taille et de couleurs. Mais si tu restes en noir en blanc et transparence et 32x32 cela devrait le faire :-)

Dal

N.B. : Comme indiqué précédemment tu peux aussi utiliser les timers, ce qui simplifiera ta boucle générale, ou modulariser en créant des fonctions pour gérer les déplacements des objets plutôt que de tout coder dans la boucle générale.
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié le 6 sept. 2018 à 13:26
Note que dans le code d'exemple sur https://wiki.libsdl.org/SDL_CreateCursor, le tableau contenant les données de l'image est complété de "0,0" qui est capturé par un sscanf pour initialiser les valeurs de hot_x et hot_y avec (hotspot de la souris).

Si tu procèdes de façon similaire en réutilisant le code d'exemple, j'imagine que, dans ton cas, tu voudras que le hotspot soit au centre de ta cible, et non au coin supérieur gauche (comme c'est le cas avec la pointe de la flèche curseur normal de la souris).

Tu devras alors y mettre "16,16" au lieu de "0,0" si ton image est bien en 32x32.
0
Bonjour,

Merci pour les explications, tout marche nickel et le viseur n'a plus de latence. Je vais regarder pour la gestion du viseur par la sdl car c'est vrai que ma façon est un peut gourmande en CPU.
0