Partage
  • Partager sur Facebook
  • Partager sur Twitter

[SDL] Pong : Meilleure gestion de déplacement de la balle

    12 octobre 2010 à 21:47:00

    Salut ! :D

    J'ai fait un Pong en C avec la SDL en suivant le cours de M@theo21, en utilisant ce que je savais déja, et en piochant quelques trucs sur le Net pour savoir vite fait comment faire rebondir une balle.
    Mais voilà : tout marche, mais la gestion de rebond sur les raquettes n'est vraiment pas terrible, des fois ça bug, et le mouvement de la balle n'est pas très fluide :(

    Mon programme utilise SDL_Image ainsi que SDL_ttf.

    Voici les différents fichiers (il n'y sont pas tous) :
    le "jeu.c" :
    /* "jeu.c" : C'est le jeu lui même. Les fonctions de ce fichier sont dans un fichier à part ; "fn_jeu.c"
     * Et les prototypes de ces deux fichiers sont dans "jeu.h".
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
    #include <SDL/SDL_ttf.h>
    
    #include "jeu.h"
    #include "initialisations.h"
    
    void jouer2Joueurs(SDL_Surface *ecran)
    {
        SDL_Surface *fond = NULL, *imgScoreJ1 = NULL, *imgScoreJ2 = NULL; // Pointeurs
        SDL_Surface *raquette1 = NULL, *raquette2 = NULL, *balle = NULL;  // de surface
        SDL_Surface *victoireJ1 = NULL, *victoireJ2 = NULL;               // de la librairie SDl
        SDL_Rect posRaquette1, posRaquette2, posBalle;          // Positions
        SDL_Rect posFond, posScoreJ1, posScoreJ2, posVictoire;  // de la SDL aussi
        SDL_Event event;        // Evenement
        SDL_Color couleurRouge = {255, 50, 50}; // Une couleur est composée de RVB, c'est à dire RougeVertBleu et le maximum c'est 255 (minimum 0)
        SDL_Color couleurVerte = {80, 210, 20};
        TTF_Font *police = NULL; // Une police trouvée sur le site Dafont je crois
        int continuer = 1, depart = 0, vx = 0, vy = 0;                                                      // Variables
        long scoreJ1Chiffres = 0, scoreJ2Chiffres = 0, tempsActuel = 0, tempsPrecedent = 0, pleinEcran = 1; //
        char scoreJ1[50] = "0", scoreJ2[50] = "0";                                                          // classiques.
    
        initPos(&posRaquette1, &posRaquette2, &posBalle, &posFond, &posScoreJ1, &posScoreJ2, &posVictoire); // Initialisation des positions
        chargerSprites(&raquette1, &raquette2, &balle, &fond, &victoireJ1, &victoireJ2);                    // Chargement
    
        TTF_Init(); // Initialisation de SDL_ttf
        police = TTF_OpenFont("police.ttf", 70); // Ouverture de la police qui sera stockée dans "police" (en taille 70 pixels)
    
        while(continuer) /* La Boucle Principale =) */
        {
            tempsActuel = SDL_GetTicks();                               // Ici, on compte le temps qui se passe entre chaque boucle
            if (depart && tempsActuel - tempsPrecedent > 20)            // Une condition que si en gros il s'est passé 20 milisecondes...
            {
                bougeBalle(&posBalle, &posRaquette1, &posRaquette2, &vx, &vy, NULL); // ...Alors on bouge la balle
                tempsPrecedent = tempsActuel;                                           // Comme ça la balle bouge tout les 20 ms environ
            }
    
    
            else if (tempsActuel - tempsPrecedent < 20)                             // Sinon ben on met en pause le programme pour éviter de bouffer tout le CPU
                SDL_Delay(20 - (tempsActuel - tempsPrecedent));
    
    
            gereTouches(&posRaquette1, &posRaquette2, &posBalle, &continuer, &depart, &vx, &vy); // Fonction située dans .... fn_jeu.c !!!
    
            SDL_PollEvent(&event); // On attend un évenement
            switch(event.type)     // Tri des évenements
            {
                case SDL_KEYDOWN: // Si on presse une touche
                    switch(event.key.keysym.sym) //etc.
                    {
                        case SDLK_SPACE: // Et si c'est la barre d'espace ;
                            pleinEcran = modeEcran(pleinEcran, ecran); //   Alors on met ou en enlève le plein écran
                            break;
                        default:  // Pour le reste...
                            break; // On fait rien (on passe)
                    }
                    break;
    
                case SDL_QUIT: // Si on clique sur la croix de la fenêtre...
                    continuer = 0; // (D'accord c'est pas très logique) ...on revient au menu principal 
                    break;
                default:
                    break;
            }
    
            gestionScore(ecran, victoireJ1, victoireJ2, posVictoire, &posRaquette1, &posRaquette2, &posBalle,
                            &scoreJ1Chiffres, &scoreJ2Chiffres, scoreJ1, scoreJ2, &depart, &continuer);        // Fonction qui gèèèère les scoooores
    
            SDL_FreeSurface(imgScoreJ1); // Comme à chaque tour de boucle on alloue
            SDL_FreeSurface(imgScoreJ2); // a chaque fois une nouvelle police alors faut pas oublier de la libérer ben ouai
    
            imgScoreJ1 = TTF_RenderText_Blended(police, scoreJ1, couleurRouge); // Maintenant on "remplit" la surface de la police avec les caractères qui sont dans la chaîne de caractères "scoreJ1"
            imgScoreJ2 = TTF_RenderText_Blended(police, scoreJ2, couleurVerte); // Pareil pour l'autre.
    
            EFFACE_ECRAN;                               // ON EFFACE L'ECRAN!!!!!!!!! AAAAHH !! YA PU D'ECRAN!!!!!!
            blitMoi(&fond, posFond, ecran);             // Mais HEURESEMENT après on affiche d'abord le fond...
            blitMoi(&imgScoreJ1, posScoreJ1, ecran);    // ...puis l'imgdu score J1...
            blitMoi(&imgScoreJ2, posScoreJ2, ecran);    // ...celle du J2
            blitMoi(&raquette1, posRaquette1, ecran);   // La raquette n°1...
            blitMoi(&raquette2, posRaquette2, ecran);   // ... Et la n°2 !
            blitMoi(&balle, posBalle, ecran);           // Sans oublier la balle 
            FLIP;                                       // On met à jour l'écran.
        }
    
        // Fin de la boucle principale ; maintenant faut enlever le mode plein écran pour des raisons d'affichage de curseur...
    
        pleinEcran = 0;
        pleinEcran = modeEcran(pleinEcran, ecran);
    
        // Ca, c'est fait
    
        TTF_CloseFont(police); // On ferme la police qu'on a ouverte au début...
        TTF_Quit();             // On quitte SDL_ttf...
    
        SDL_FreeSurface(raquette1); //     LIBERATION
        SDL_FreeSurface(raquette2);
        SDL_FreeSurface(balle);     //        DES
        SDL_FreeSurface(fond);
        SDL_FreeSurface(imgScoreJ1); // !!             !!
        SDL_FreeSurface(imgScoreJ2); //  !! SURFACES !!!
        SDL_FreeSurface(victoireJ1); // !!!!!!!!!!!!!!!!!
        SDL_FreeSurface(victoireJ2);// !!!!!!!!!!!!!!!!!!!
    }          // Retour au menu principal...
    


    Le "fn_jeu.c" :
    /* "fn_jeu" : ce fichier contient les fonctions de "jeu.c" */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
    #include <SDL/SDL_ttf.h>
    
    #include "jeu.h"
    
    void bougeBalle(SDL_Rect *posBalle, SDL_Rect *posRaquette1, SDL_Rect *posRaquette2, int *vx, int *vy, long *compteur) // Fonction foireuse... A revoir éventuellement.
    {
        posBalle->x += *vx; // On bouge la balle
        posBalle->y += *vy; // dans les abscisses et ordonnées
    
        if (posBalle->x > posRaquette1->x && posBalle->x <= posRaquette1->x + RAQUETTE_X // Si la baballe touche un raquette
         && posBalle->y > posRaquette1->y && posBalle->y <= posRaquette1->y + RAQUETTE_Y)
            *vx = -*vx;     // On inverse le vecteur de la longueur ce qui donne un EFFET REBONDISSANT!!!
    
        if( posBalle->x > posRaquette2->x-TAILLE_BALLE && posBalle->x <= posRaquette2->x-TAILLE_BALLE + RAQUETTE_X // la balle touche l'autre raquette
         && posBalle->y > posRaquette2->y && posBalle->y <= posRaquette2->y + RAQUETTE_Y)
            *vx = -*vx; // rebond pareil
    
        if ( (posBalle->y < 0 && *vy < 0) || (posBalle->x >= 0 && posBalle->y == 0) ) // rebond haut
            *vy = -*vy; // On inverse le vecteur de la hauteur (rebond)...
    
        if (posBalle->y+TAILLE_BALLE > 600 && *vy > 0) // rebond bas
            *vy = -*vy; // Pareil...
    
    }
    
    void blitMoi(SDL_Surface **surface, SDL_Rect pos, SDL_Surface *ecran) /* Ne sert à rien c'était juste pour m'amuser (et m'entrainer) */
    {
        SDL_BlitSurface(*surface, NULL, ecran, &pos);
    }
    
    void gereTouches(SDL_Rect *posRaquette1, SDL_Rect *posRaquette2, SDL_Rect *posBalle, int *continuer, int *depart, int *vx, int *vy) // Fonction qui... gère les touches !!!!
    {
        Uint8 *touches = SDL_GetKeyState(NULL); // L'ordi analyse si on a appuyé sur une touche
    
        if (touches[SDLK_UP] && posRaquette2->y > 0) // Si c'est la touche fléchée vers le haut et que on est pas déja tout en haut
                posRaquette2->y--;                   // On monte la raquette de 8 pixels (trop lent sinon)
    
        if (touches[SDLK_DOWN] && posRaquette2->y < 600-RAQUETTE_Y) // Si c'est vers le bas et que on est pas déja tout en bas
                posRaquette2->y++;                   // On descend
    
    
        if (touches[SDLK_w] && posRaquette1->y > 0) // Normalement c'est fait pour les claviers querty donc il faut pas marquer z mais w
            posRaquette1->y--;                   // Tout est pareil mais pour l'autre raquette.
    
        if (touches[SDLK_s] && posRaquette1->y < 600-RAQUETTE_Y) // ...
            posRaquette1->y++;                       // ...
    
    
        if (touches[SDLK_ESCAPE]) // Touche Echap :
            *continuer = 0;         // Retour menu principal
    
        if (touches[SDLK_RETURN] && posBalle->x == 25 && posBalle->y == 260) // Et la touche Entrée (retour charriot) : Elle donne le départ à la balle !!
        {
            *depart = 1;
            *vx = 1;  // Vecteur x (je devrais peut etre tirer un nombre au hasard pour avoir un effet de lancement de la balle aléatoire...
            *vy = 2;
        }
    }
    
    void gestionScore(SDL_Surface *ecran, SDL_Surface *victoireJ1, SDL_Surface *victoireJ2, SDL_Rect posVictoire, SDL_Rect *posRaquette1,
                      SDL_Rect *posRaquette2, SDL_Rect *posBalle, long *scoreJ1Chiffres, long *scoreJ2Chiffres,
                      char scoreJ1[], char scoreJ2[], int *depart, int *continuer) // Fonction qui gèèèère le scooooore et si on a perdu ou pas
    {
        if (posBalle->x > 800) // Si on perd du coté droit
        {
            *scoreJ1Chiffres += 1; // C'est le J1 qui marque un point !!
            sprintf(scoreJ1, "%ld", *scoreJ1Chiffres); // Il faut le marquer pour pas l'oublier
                                                         // (on le marque une chaine de caractère qui va après être "écrite" dans une surface.
                                                           // Il faudra juste après la coller a l'écran pour l'afficher.
    
            posRaquette1->x = 3;  // On réinitialise
            posRaquette1->y = 200; //  les
    
            posRaquette2->x = 775; // deux raquettes...
            posRaquette2->y = 200;
    
            posBalle->x = 25;       // Et la balle...
            posBalle->y = 260;      // ... A leur place initiale,
    
            *depart = 0;           // et on est met le départ de la balle sur "ne pas partir"
        }
    
        if (posBalle->x < 0-TAILLE_BALLE) // Si on perd du coté gauche
        {
    
            *scoreJ2Chiffres += 1; // Pareil mais pour l'autre joueur...
            sprintf(scoreJ2, "%ld", *scoreJ2Chiffres); // ...
    
    
            posRaquette1->x = 3; // Etc.
            posRaquette1->y = 200;
    
            posRaquette2->x = 775;
            posRaquette2->y = 200;
    
    
            posBalle->x = 25;
            posBalle->y = 260;
    
            *depart = 0;  // Etc.
        }
    
        if (*scoreJ1Chiffres == 10) // Maintenant si le J1 a marqué 10 points,
        {
            blitMoi(&victoireJ1, posVictoire, ecran); // On affiche l'image de la victoire du Joueur 1
            FLIP;                     // MAJ de l'écran
            SDL_Delay(3500);         // On met en pause le programme (pendant 3.5 sec) pour que l'utilisateur voit qui a gagné
            *continuer = 0;         // Et retour au menu principal !
        }
    
        if (*scoreJ2Chiffres == 10) // Pareil mais pour le J2
        {
            blitMoi(&victoireJ2, posVictoire, ecran); // Affichage
            FLIP;                           // MAJ
            SDL_Delay(3500);        // Attendre
            *continuer = 0;     // Menu
        }
    
    }
    
    int modeEcran(long pleinEcran, SDL_Surface *ecran) // Petite fonction qui sert a mettre ou enlever le mode plein écran de la SDL
    {
        if (pleinEcran) // Si la variable de type long qui nous sert de booléenne pleinEcran est vrai...
        {
            ecran = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_FULLSCREEN); //... Alors on met l'écran en plein écran
            SDL_ShowCursor(SDL_DISABLE);                                                             // Et on enlève l'affichage du curseur
            pleinEcran = 0;                                                                            // Sans oublier qu'il faut mettre pleinEcran sur 0 pour que la prochaine fois qu'on
        }                                                                                                // appuieras sur Espace les instructions seront celle du else.
    
        else // Sinon
        {
            ecran = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE | SDL_DOUBLEBUF); // On remet normal
            SDL_ShowCursor(SDL_ENABLE);                                             // Et on affiche le curseur
            pleinEcran = 1;                                                           // Tout en remettant la variable sur 1 (booléen positif)
        }
    
        return pleinEcran; // Et la fonction retourne cette variable
    }
    


    Et le "jeu.h" :
    #ifndef JEU_H
    #define JEU_H
    
    /* Prototypes : */
    
    void blitMoi(SDL_Surface **surface, SDL_Rect pos, SDL_Surface *ecran);
    
    
    void gereTouches(SDL_Rect *posRaquette1, SDL_Rect *posRaquette2, SDL_Rect *posBalle, int *continuer, int *depart, int *vx, int *vy);
    
    void bougeBalle(SDL_Rect *posBalle, SDL_Rect *posRaquette1, SDL_Rect *posRaquette2, int *vx, int *vy, long *compteur);
    
    void gestionScore(SDL_Surface *ecran, SDL_Surface *victoireJ1, SDL_Surface *victoireJ2, SDL_Rect posVictoire, SDL_Rect *posRaquette1,
                      SDL_Rect *posRaquette2, SDL_Rect *posBalle, long *scoreJ1Chiffres, long *scoreJ2Chiffres,
                      char scoreJ1[], char scoreJ2[], int *depart, int *continuer);
    
    
    int modeEcran(long pleinEcran, SDL_Surface *ecran);
    
    
    /* Defines : */
    
    #define RAQUETTE_X      22
    #define RAQUETTE_Y      162
    
    #define TAILLE_BALLE    38
    
    #define EFFACE_ECRAN    SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 0, 0, 0));
    #define FLIP            SDL_Flip(ecran);
    
    
    #endif
    


    Que faut-il que je fasse (j'ai 15 ans, je connais rien aux vecteurs etc ^^ ) pour que le rebond de la balle sur la raquette dépende de l'endroit où celle-ci tape ? Afin que plus le rebond est en bas du centre de la raquette, et plus la balle va vers le bas et inversement. J'aimerais aussi savoir comment obtenir un meilleur déplacement de la balle (sprites de moins bonne qualité donc plus d'images par seconde ?? Ou optimiser le code?)


    En espérant avoir une ou des réponses ; je suis ouvert à toutes les suggestions d'optimisations/améliorations de mon code qui est trèès loin d'être parfait - d'ailleurs, un code parfait n'existe pas :p
    • Partager sur Facebook
    • Partager sur Twitter
      12 octobre 2010 à 23:06:37

      je t'explique ce que j'avais pour la balle.
      J'ai commencer par la faire se déplacer horizontalement entre les deux pong pour gérer la collision et apres lors de la collision je testai pour savoir quel coté du pong était touché et j'appliquais un angle aléatoire positif ou négatif selon le résultat
      • Partager sur Facebook
      • Partager sur Twitter
        12 octobre 2010 à 23:37:47

        Salut ! :)

        Bon ben déjà le code est très bien organisé et commenté, ça me donnerait presque envie de coder un pong ;)

        Pour la fluidité, tu peux essayer de bouger la balle toutes les 10 ms mais c'est vrai que j'ai aussi souvent eu ce genre de problème avec la SDL :(

        Si tu veux que la trajectoire de ta balle au rebond dépende de sa position sur la raquette, tu pourrais créer une fonction qui te donne la position de la balle par rapport au milieu de la raquette. Après, (c'est juste une idée, c'est sûrement pas the méthode :-° ) tu pourrais jouer sur la fréquence avec laquelle tu fais bouger ta balle en abscisse et ordonnée. Actuellement, tu rafraîchis la position toutes les 20ms. eh ben à la place tu peux définir 2 temps de rafraichissement, 1 pour x et l'autre pour y :)

        Par exemple si tu veux qu'elle se déplace plus vite en ordonnée qu'en abscisse, tu la déplace en abscisse de vx toutes les 30ms et en ordonnée de vy toutes les 15ms.

        L'autre solution serait de faire varier la différence entre vx et vy mais cela nous laisse moins de possibilités ;)

        Bonne chance !! ^^
        • Partager sur Facebook
        • Partager sur Twitter
          13 octobre 2010 à 6:13:30

          Bonjour,

          D'abord un conseil :
          Avant de mettre les paillettes(SDL_TTF, charger des surfaces, etc...), je pense que tu devrais tester tes idées avec de simple SDL_FillRect en guise de balle et de raquette.

          J'ai fais rapidement(moins rapidement que je ne le pensais :-° ), un petit exemple, avec une raquette et une balle.

          Les déplacement de la balle sont obtenus par un couple de vitesse sur les axes x et y, et 1 coefficient.
          Chaque fois qu'une des 2 vitesses change, on recalcule le coeff pour avoir une vitesse combinée(norme du vecteur en fait) constante.

          La position de la balle par rapport au centre de la raquette ainsi que la vitesse de la raquette influent sur l'angle donné à la balle.

          Ce qui t'intéresse se trouve dans la fonctions Ball_update. En changeant les #define, tu dois pouvoir obtenir le déplacement que tu désires.

          #include <SDL/SDL.h>
          #include <math.h>
          
          /* 50 images secondes */
          #define TICK_INTERVAL       20
          
          /* Vitesse maxi de la raquette */
          #define PAD_MAX_SPEED       20.
          /* Vitesse de la balle sur les 2 axes combinés */
          #define BALL_SPEED          8.
          /* Vitesse maxi en x(pour eviter les trajectoires trops horizontales */
          #define BALL_MAX_SPEED      5
          /* Vitesse de départ de la balle sur chaque axe */
          #define BALL_START_X        -0.5
          #define BALL_START_Y        1
          /* Influence de la position de la balle sur la raquette sur l'angle */
          #define COEFF_DX            0.03
          /* Influence de la vitesse de la raquette sur l'angle de la balle */
          #define COEFF_SPEED         0.1
          
          
          
          #define ERR_CHECK(cond, fun)                                \
              do                                                      \
              {                                                       \
                  if(!(cond))                                         \
                  {                                                   \
                      fprintf(stderr, "%s %s %u",                     \
                              fun(),                                  \
                              __FILE__,                               \
                              __LINE__);                              \
                      exit(EXIT_FAILURE);                             \
                  }                                                   \
              }while(0)
          
          /* -------------------------------------------------------------------------- */
          typedef struct pongtag Pong;
          typedef struct paddletag Paddle;
          typedef struct balltag Ball;
          
          struct paddletag
          {
              SDL_Surface *gfx;
              double x, y;
              int w, h;
              double speed;
          };
          
          struct balltag
          {
              SDL_Surface *gfx;
              double ox, oy;
              double x, y;
              int w, h;
              double xspeed, yspeed;
              double coeff;
          };
          
          struct pongtag
          {
              SDL_Rect lim;
              Paddle pad;
              Ball ball;
              Uint8 *keys;
              Uint8 done;
          };
          
          /* Fonctions utilitaires ---------------------------------------------------- */
          Uint32 time_left( Uint32 nxtTime )
          {
              Uint32 now = SDL_GetTicks();
              return nxtTime <= now ? 0 : nxtTime - now;
          }
          
          void blit(SDL_Surface *src, int x, int y)
          {
              SDL_Rect rct;
              SDL_Surface *scr = SDL_GetVideoSurface();
          
              rct.x = x;
              rct.y = y;
              SDL_BlitSurface(src, NULL, scr, &rct);
          }
          
          /* Paddle ------------------------------------------------------------------- */
          void Paddle_init(Paddle *pad, SDL_Rect *rct)
          {
              SDL_Surface *tmp = SDL_CreateRGBSurface(SDL_SWSURFACE,
                                                      rct->w, rct->h, 32, 0, 0, 0, 0);
              ERR_CHECK(tmp != NULL, SDL_GetError);
          
              SDL_FillRect(tmp, NULL, SDL_MapRGB(tmp->format, 255, 255, 255));
              pad->gfx = SDL_DisplayFormat(tmp);
              SDL_FreeSurface(tmp), tmp = NULL;
              ERR_CHECK(pad->gfx != NULL, SDL_GetError);
          
              pad->x = rct->x;
              pad->y = rct->y;
              pad->w = rct->w;
              pad->h = rct->h;
              pad->speed = 0;
          }
          
          
          void Paddle_update(Paddle *pad, SDL_Rect *lim)
          {
              /* Limitation de vitesse  */
              if(pad->speed < -PAD_MAX_SPEED)
                  pad->speed = -PAD_MAX_SPEED;
              else if(pad->speed > PAD_MAX_SPEED)
                  pad->speed = PAD_MAX_SPEED;
          
              /* On actualise la positions de la raquette */
              pad->x += pad->speed;
          
              /* On reste dans les limites */
              if(pad->x < 0)
              {
                  /* Bloque à gauche */
                  pad->x = 0;
                  pad->speed = 0;
              }
              else if(pad->x + pad->w >= lim->x + lim->w)
              {
                  /* Bloque à droite */
                  pad->x = lim->w - pad->w;
                  pad->speed = 0;
              }
          }
          
          /* Inertie dans le déplacement de la raquette */
          void Paddle_slowdown(Paddle *pad)
          {
              pad->speed += pad->speed > 0 ? -1 : 1;
          }
          
          
          
          void Paddle_display(Paddle *pad)
          {
              blit(pad->gfx, (int)pad->x, (int)pad->y);
          }
          
          
          void Paddle_clear(Paddle *pad)
          {
              SDL_FreeSurface(pad->gfx), pad->gfx = NULL;
          }
          /* Ball --------------------------------------------------------------------- */
          /* On conserve une vitesse constante */
          void Ball_ensureSpeed(Ball *ball)
          {
              /* Vitesse combinée = coeff * norme du vecteur */
              double speed = ball->coeff * sqrt(ball->xspeed * ball->xspeed +
                                                ball->yspeed * ball->yspeed);
              ball->coeff *= (BALL_SPEED / speed);
          }
          
          
          
          void Ball_init(Ball *ball, SDL_Rect *rct)
          {
              SDL_Surface *tmp = SDL_CreateRGBSurface(SDL_SWSURFACE,
                                                      rct->w, rct->h, 32, 0, 0, 0, 0);
              ERR_CHECK(tmp != NULL, SDL_GetError);
          
              SDL_FillRect(tmp, NULL, SDL_MapRGB(tmp->format, 255, 255, 255));
              ball->gfx = SDL_DisplayFormat(tmp);
              SDL_FreeSurface(tmp), tmp = NULL;
              ERR_CHECK(ball->gfx != NULL, SDL_GetError);
          
              ball->xspeed = BALL_START_X;
              ball->yspeed = BALL_START_Y;
          
              /* On calcule le coeff à appliquer pour conserver la vitesse désirée */
              Ball_ensureSpeed(ball);
              /* Coordonnées de départ de la ball */
              ball->ox = rct->x;
              ball->oy = rct->y;
          
              ball->x = rct->x;
              ball->y = rct->y;
              ball->w = rct->w;
              ball->h = rct->h;
          }
          
          
          void Ball_update(Ball *ball, Paddle *pad, SDL_Rect *lim)
          {
              ball->x += ball->xspeed * ball->coeff;
              ball->y += ball->yspeed * ball->coeff;
          
              if(ball->x < 0)
              {
                  ball->xspeed *= -1;
                  ball->x = 0;
              }
              else if(ball->x + ball->w >= lim->w)
              {
                  ball->xspeed *= -1;
                  ball->x = lim->w - ball->w;
              }
              if(ball->y < 0)
              {
                  ball->yspeed *= -1;
                  ball->y = 0;
              }
              else if(ball->y + ball->h >= lim->h)
              {
                  /* On replace la balle au centre */
                  ball->x = ball->ox;
                  ball->y = ball->oy;
          
                  ball->xspeed = BALL_START_X;
                  ball->yspeed = BALL_START_Y;
                  Ball_ensureSpeed(ball);
              }
          
              if(ball->x + ball->w >= pad->x &&
                      ball->x <= pad->x + pad->w &&
                      ball->y + ball->h >= pad->y &&
                      ball->y <= pad->y + pad->h)
              {
                  /* Collision balle raquette */
                  /* distance de l'impact par rapport au centre de la raquette */
                  double dx = pad->x + pad->w / 2. - ball->x;
                  /* On inverse la direction de la ball en y */
                  ball->yspeed *= -1;
                  /* On ajuste la vitesse de la balle en x en fonction de la position de
                   * l'impact et de la vitesse de raquette au moment de la collision */
                  ball->xspeed = -dx * COEFF_DX + pad->speed * COEFF_SPEED;
                  /* On s'arrange pour que la trajectoire ne soit pas trop horizontale */
                  if(ball->xspeed > BALL_MAX_SPEED)
                      ball->xspeed = BALL_MAX_SPEED;
                  if(ball->xspeed < -BALL_MAX_SPEED)
                      ball->xspeed = -BALL_MAX_SPEED;
          
                  Ball_ensureSpeed(ball);
                  /* On place la balle au sommet de la raquette */
                  ball->y = pad->y - ball->h;
          
              }
          }
          
          
          void Ball_display(Ball *ball)
          {
              blit(ball->gfx, ball->x, ball->y);
          }
          
          
          void Ball_clear(Ball *ball)
          {
              SDL_FreeSurface(ball->gfx), ball->gfx = NULL;
          }
          
          
          
          /* Pong --------------------------------------------------------------------- */
          void Pong_init(Pong *p)
          {
              SDL_Rect rct;
              SDL_Surface *scr = SDL_GetVideoSurface();
          
              p->keys = SDL_GetKeyState(NULL);
              p->done = 0;
          
              p->lim.x = 0;
              p->lim.w = scr->w;
              p->lim.y = 0;
              p->lim.h = scr->h;
          
              rct.w = 64;
              rct.h = 8;
              rct.x = p->lim.w / 2 - rct.w;
              rct.y = p->lim.h - rct.h * 2;
              Paddle_init(&p->pad, &rct);
          
              rct.w = 8;
              rct.h = 8;
              rct.x = p->lim.w / 2 - rct.w / 2;
              rct.y = p->lim.h / 2 - rct.h / 2;
              Ball_init(&p->ball, &rct);
          }
          
          
          void Pong_clear(Pong *p)
          {
              Paddle_clear(&p->pad);
              Ball_clear(&p->ball);
          }
          
          
          
          void Pong_update(Pong *p)
          {
              SDL_Event ev;
              while(SDL_PollEvent(&ev))
              {
                  if(ev.type == SDL_QUIT)
                      p->done = 1;
              }
          
              if(p->keys[SDLK_LEFT])
                  p->pad.speed--;
              else if(p->keys[SDLK_RIGHT])
                  p->pad.speed++;
              else if(p->pad.speed)
                  Paddle_slowdown(&p->pad);
          
              Paddle_update(&p->pad, &p->lim);
              Ball_update(&p->ball, &p->pad, &p->lim);
          }
          
          
          void Pong_display(Pong *p)
          {
              SDL_Surface *scr = SDL_GetVideoSurface();
              /* On efface l'écran */
              SDL_FillRect(scr, NULL, 0);
              /* On reblitte tout */
              Paddle_display(&p->pad);
              Ball_display(&p->ball);
          }
          
          
          void Pong_run(Pong *p)
          {
              Uint32 nextTime;
              Uint32 currentFrame = 0;
              Uint32 startTime;
              SDL_Surface *scr = SDL_GetVideoSurface();
          
              Pong_init(p);
          
              startTime = SDL_GetTicks();
              nextTime = SDL_GetTicks() + TICK_INTERVAL;
              while(!p->done)
              {
                  Pong_update(p);
                  Pong_display(p);
          
                  SDL_Delay(time_left(nextTime));
                  SDL_Flip(scr);
          
                  nextTime += TICK_INTERVAL;
                  currentFrame++;
              }
              Pong_clear(p);
          }
          
          int main(int argc, char *argv[])
          {
              Pong p;
              ERR_CHECK(SDL_Init(SDL_INIT_VIDEO) == 0, SDL_GetError);
              ERR_CHECK(SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE) != NULL,
                        SDL_GetError);
          
              Pong_run(&p);
          
              SDL_Quit();
          
              return EXIT_SUCCESS;
          }
          


          Une solution pour avoir une impression de fluidité sur un ordi moderne, c'est de tourner sans SDL_Delay, mais calculer les déplacements en fonction du temps écoulé depuis le dernier frame.(100% d'usage CPU garanti)
          Perso, je trouve que réguler les fps suffit dans un jeu 2d.

          Une autre solution pour le déplacement de la balle c'est d'utiliser un angle en radian et utiliser cos et sin, mais je trouve ça moins sympa.
          On peu aussi utiliser des tables de sin et cos précalculées, mais comme ces fonctions seraient employés 1 fois par frame, je pense que ce n'est pas justifié.
          • Partager sur Facebook
          • Partager sur Twitter
          Zeste de Savoir, le site qui en a dans le citron !
            13 octobre 2010 à 11:50:12

            Bonjour,

            @GurneyH :
            Ton code est vraiment super propre et clair ! Bravo ! :)

            Je soutiens en tous cas cette gestion du framerate et j'encourage tout le monde à regarder comment GurneyH a fait (lignes 336 à 348).

            @traficouillon :
            Ta gestion du temps est presque bien.
            Mais au lieu de faire ça :

            while(continuer)
            {
               tempsActuel = SDL_GetTicks();
               if (depart && tempsActuel - tempsPrecedent > 20)
               {
                  bougeBalle(&posBalle, &posRaquette1, &posRaquette2, &vx, &vy, NULL);
                  tempsPrecedent = tempsActuel;
                }
                else if (tempsActuel - tempsPrecedent < 20)
                    SDL_Delay(20 - (tempsActuel - tempsPrecedent));
            ...
            


            Tu devrais faire un truc du genre (non testé, c'est juste pour montrer l'idée) :

            while(continuer)
            {
               tempsActuel = SDL_GetTicks();
               if (tempsActuel - tempsPrecedent < 20)
                    SDL_Delay(20 - (tempsActuel - tempsPrecedent));
               tempsPrecedent = tempsActuel;
            
               bougeBalle(&posBalle, &posRaquette1, &posRaquette2, &vx, &vy, NULL);
            ...
            



            De cette façon, tu attends (plus ou moins longtemps), puis tu passes dans ta gestion, tes affichages et enfin ton flip une seule fois. (Bon, ensuite, je te dirais de tester cette attente juste avant le flip, comme l'a fait GurneyH dans son exemple et pas en début de boucle).

            Clément.

            Edit : Orthographe.
            • Partager sur Facebook
            • Partager sur Twitter
              13 octobre 2010 à 11:55:40

              Merci Joe78, mais il y a encore du boulot pour concurrencer ton Breaker. :lol:
              • Partager sur Facebook
              • Partager sur Twitter
              Zeste de Savoir, le site qui en a dans le citron !
                13 octobre 2010 à 12:09:18

                Merci m'sieur ! :)
                Mais quand je vois ton code, c'est clair que les miens contiennent de vraies horreurs... (Mieux cachées, parce que plus longs !).

                Allez, on arrête de se jeter des fleurs en public ! :lol:
                • Partager sur Facebook
                • Partager sur Twitter
                  14 octobre 2010 à 13:43:05

                  Merci a tous pour vos réponses :)

                  @joe78 : J'ai testé ce que tu m'as dis de faire en mettant en plus tout juste avant le flip ou pas ; je préfère juste avant le flip. La balle se déplace plus fluidement mais j'ai l'impression qu'elle va moins vite (j'ai augmenté les variables vx et vy pour voir) :) Sinon oui, ça marche beaucoup mieux avec ta solution ;) !

                  @GurneyH : Pas encore eu le temps de bien regarder ton code (suis interne, et ya cours), je l'ai juste survolée pour l'instant mais ça m'as l'air super intéressant ^^

                  [EDIT] : Je viens de compiler le code de GurneyH, et j'adore le mouvement de déplacement de la raquette :p !
                  • Partager sur Facebook
                  • Partager sur Twitter

                  [SDL] Pong : Meilleure gestion de déplacement de la balle

                  × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                  × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                  • Editeur
                  • Markdown