IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

SDL Discussion :

[2D - C] Premier projet : Pong


Sujet :

SDL

  1. #1
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    16
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 16
    Points : 1
    Points
    1
    Par défaut [2D - C] Premier projet : Pong
    Salut, premier post.

    Je me suis lancé dans mon premier projet perso, un Pong, histoire d'avoir quelque chose de sympa à utiliser mais pas trop dur et pas trop long à faire. Comme c'est mon premier projet et que je n'ai pas une grande expérience en info, j'ai forcément des questions particulières, que j'ai trouvé plus simple de regrouper dans un seul sujet. Je me permettrai de poster des problèmes futurs dont je n'aurai pas trouvé la solution, ici, si c'est possible.

    Venons-en à mes quelques soucis du moment. Voici le code complet de mon projet :


    main.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    #include <stdlib.h>
    #include <stdio.h>
    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
     
    #include "constants.h"
    #include "game.h"
     
    int main(int argc, char *argv[]) {
        // begin : creates variables
        int continueProgram = 1;
        SDL_Surface *screen = NULL, *menu = NULL;
        SDL_Rect menuPosition;
        SDL_Event event;
        // end : creates variables
     
        // begin : SDL initialisation
        if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
            fprintf(stderr, "SDL initialisation error : %s\n", SDL_GetError());
            exit(EXIT_FAILURE);
        }
        // end : SDL initialisation
     
        // begin : sets caption and icon
        SDL_WM_SetCaption("Pong", NULL);
        SDL_WM_SetIcon(IMG_Load("icon.png"), NULL);
        // end : sets caption and icon
     
        // begin : sets video mode
        screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
        if (screen == NULL) {
            fprintf(stderr, "Unable to load video mode : %s\n", SDL_GetError());
            exit(EXIT_FAILURE);
        }
        // end : sets video mode
     
        // begin : load menu screen
        menu = IMG_Load("menu.png");
        menuPosition.x = 0;
        menuPosition.y = 0;
        // end : load menu screen
     
        // begin : menu choice
        while(continueProgram) {
            SDL_WaitEvent(&event);
            switch(event.type) {
                case SDL_QUIT:
                    continueProgram = 0;
                    break; // quits program
                case SDL_KEYDOWN:
                    switch(event.key.keysym.sym) {
                        case SDLK_ESCAPE:
                            continueProgram = 0;
                            break; // quits program
                        case SDLK_RETURN:
                            playGame(screen, &continueProgram);
                            break; // starts game
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
     
            // begin : sends menu image to screen
            SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
            SDL_BlitSurface(menu, NULL, screen, &menuPosition);
            SDL_Flip(screen);
            // end : sends menu image to screen
        }
        // end : menu choice
     
        // begin : SDL quit
        SDL_FreeSurface(menu);
        SDL_Quit();
        // end : SDL quit
     
        return EXIT_SUCCESS;
    }
    constants.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #ifndef DEF_CONSTANTS
    #define DEF_CONSTANTS
     
        #define SCREEN_WIDTH 800
        #define SCREEN_HEIGHT 600
     
        enum {UP, DOWN};
        enum {LEFT, RIGHT};
     
    #endif
    game.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #ifndef DEF_GAME
    #define DEF_GAME
     
        void playGame(SDL_Surface *screen, int *continueProgram);
     
    #endif
    game.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    #include <stdlib.h>
    #include <stdio.h>
    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
     
    #include "constants.h"
    #include "game.h"
    #include "point.h"
     
    void playGame(SDL_Surface *screen, int *continueProgram) {
        // begin : creates variables
        int continueGame = 1;
        Side upperSide = {700, 50, NULL, {0, 0}}, lowerSide = {700, 50, NULL, {0, 0}};
        Paddle leftPaddle = {10, 50, NULL, {0, 0}, UP}, rightPaddle = {10, 50, NULL, {0, 0}, UP};
        Ball ball = {6, NULL, {0, 0}, LEFT};
        Image player1Score = {NULL, {0, 0}}, hyphen = {NULL, {0, 0}}, player2Score = {NULL, {0, 0}};
        // end : creates variables
     
        // begin : creates upper and lower sides
        upperSide.surface = IMG_Load("side.png");
        upperSide.position.x = (SCREEN_WIDTH - upperSide.width)/2;
        upperSide.position.y = 0;
     
        lowerSide.surface = IMG_Load("side.png");
        lowerSide.position.x = (SCREEN_WIDTH - lowerSide.width)/2;
        lowerSide.position.y = SCREEN_HEIGHT - lowerSide.height;
        // end : creates upper and lower sides
     
        // begin : creates left and right paddles
        leftPaddle.surface = SDL_CreateRGBSurface(SDL_HWSURFACE, leftPaddle.width, leftPaddle.height, 32, 0, 0, 0, 0);
        SDL_FillRect(leftPaddle.surface, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
        leftPaddle.position.x = (SCREEN_WIDTH - upperSide.width)/2;
        leftPaddle.position.y = (SCREEN_HEIGHT - leftPaddle.height)/2;
     
        rightPaddle.surface = SDL_CreateRGBSurface(SDL_HWSURFACE, rightPaddle.width, rightPaddle.height, 32, 0, 0, 0, 0);
        SDL_FillRect(rightPaddle.surface, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
        rightPaddle.position.x = (SCREEN_WIDTH + upperSide.width)/2 - rightPaddle.width;
        rightPaddle.position.y = (SCREEN_HEIGHT - rightPaddle.height)/2;
        // end : creates left and right paddles
     
        // begin : creates ball
        ball.surface = SDL_CreateRGBSurface(SDL_HWSURFACE, ball.size, ball.size, 32, 0, 0, 0, 0);
        SDL_FillRect(ball.surface, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
        ball.position.x = (SCREEN_WIDTH - ball.size)/2;
        ball.position.y = (SCREEN_HEIGHT - ball.size)/2;
        // end : creates ball
     
        // begin : creates score
        player1Score.surface = IMG_Load("zero.png");
        player1Score.position.x = SCREEN_WIDTH/2 - 45;
        player1Score.position.y = 10;
     
        hyphen.surface = IMG_Load("hyphen.png");
        hyphen.position.x = SCREEN_WIDTH/2 - 9;
        hyphen.position.y = 10;
     
        player2Score.surface = IMG_Load("zero.png");
        player2Score.position.x = SCREEN_WIDTH/2 + 27;
        player2Score.position.y = 10;
        // end : creates score
     
        SDL_EnableKeyRepeat(10, 10); // sets key repeat
     
        // begin : game
        while(continueGame) {
            playPoint(screen, continueProgram, &continueGame, &upperSide, &lowerSide, &leftPaddle, &rightPaddle, &ball, &player1Score, &hyphen, &player2Score); // starts point
     
            // begin : sends new image to screen
            SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
     
            SDL_BlitSurface(upperSide.surface, NULL, screen, &upperSide.position);
            SDL_BlitSurface(lowerSide.surface, NULL, screen, &lowerSide.position);
     
            SDL_BlitSurface(leftPaddle.surface, NULL, screen, &leftPaddle.position);
            SDL_BlitSurface(rightPaddle.surface, NULL, screen, &rightPaddle.position);
     
            SDL_BlitSurface(ball.surface, NULL, screen, &ball.position);
     
            SDL_BlitSurface(player1Score.surface, NULL, screen, &player1Score.position);
            SDL_BlitSurface(hyphen.surface, NULL, screen, &hyphen.position);
            SDL_BlitSurface(player2Score.surface, NULL, screen, &player2Score.position);
     
            SDL_Flip(screen);
            // end : sends new image to screen
        }
        // end : game
     
        SDL_EnableKeyRepeat(0, 0); // disables key repeat
     
        // begin : frees memory
        SDL_FreeSurface(upperSide.surface);
        SDL_FreeSurface(lowerSide.surface);
        SDL_FreeSurface(leftPaddle.surface);
        SDL_FreeSurface(rightPaddle.surface);
        SDL_FreeSurface(ball.surface);
        SDL_FreeSurface(player1Score.surface);
        SDL_FreeSurface(hyphen.surface);
        SDL_FreeSurface(player2Score.surface);
        // end : frees memory
    }
    point.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    #ifndef DEF_POINT
    #define DEF_POINT
     
        typedef struct Side Side;
        struct Side {
            int width;
            int height;
            SDL_Surface *surface;
            SDL_Rect position;
        };
     
        typedef struct Paddle Paddle;
        struct Paddle {
            int width;
            int height;
            SDL_Surface *surface;
            SDL_Rect position;
            int direction;
        };
     
        typedef struct Ball Ball;
        struct Ball {
            int size;
            SDL_Surface *surface;
            SDL_Rect position;
            int direction;
        };
     
        typedef struct Image Image;
        struct Image {
            SDL_Surface *surface;
            SDL_Rect position;
        };
     
        typedef struct moveBallStruct moveBallStruct;
        struct moveBallStruct {
            int *continuePointPointer;
            Ball *ballPointer;
            Paddle *leftPaddlePointer;
            Paddle *rightPaddlePointer;
        };
     
        void playPoint(SDL_Surface *screen, int *continueProgram, int *continueGame, Side *upperSide, Side *lowerSide, Paddle *leftPaddle, Paddle *rightPaddle, Ball *ball, Image *player1Score, Image *hyphen, Image *player2Score);
        Uint32 moveBall(Uint32 timespan, void *parameter);
        void movePaddle(Paddle *paddle, int paddleNewDirection, int paddleHeight, int sidePositionY, int sideHeight);
     
    #endif
    point.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    #include <stdlib.h>
    #include <stdio.h>
    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
     
    #include "constants.h"
    #include "point.h"
     
    void playPoint(SDL_Surface *screen, int *continueProgram, int *continueGame, Side *upperSide, Side *lowerSide, Paddle *leftPaddle, Paddle *rightPaddle, Ball *ball, Image *player1Score, Image *hyphen, Image *player2Score) {
        // begin : creates variables
        int continuePoint = 1;
        moveBallStruct ballMovement = {&continuePoint, ball, leftPaddle, rightPaddle};
        SDL_Event event;
        SDL_TimerID ballTimer;
        // end : creates variables
     
        ballTimer = SDL_AddTimer(10, moveBall, &ballMovement); // sets ball timer
     
        // begin : game
        while(continuePoint) {
            SDL_PollEvent(&event);
            switch(event.type) {
                case SDL_QUIT:
                    continuePoint = 0;
                    *continueGame = 0;
                    *continueProgram = 0;
                    break; // quits program
                case SDL_KEYDOWN:
                    switch(event.key.keysym.sym) {
                        case SDLK_ESCAPE:
                            continuePoint = 0;
                            *continueGame = 0;
                            break; // ends game
                        case SDLK_r:
                            movePaddle(leftPaddle, UP, leftPaddle->height, upperSide->position.y, upperSide->height);
                            break; // moves left paddle up
                        case SDLK_f:
                            movePaddle(leftPaddle, DOWN, leftPaddle->height, lowerSide->position.y, lowerSide->height);
                            break; // moves left paddle down
                        case SDLK_UP:
                            movePaddle(rightPaddle, UP, rightPaddle->height, upperSide->position.y, upperSide->height);
                            break; // moves right paddle up
                        case SDLK_DOWN:
                            movePaddle(rightPaddle, DOWN, rightPaddle->height, lowerSide->position.y, lowerSide->height);
                            break; // moves right paddle down
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
     
            // begin : sends new image to screen
            SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
     
            SDL_BlitSurface(upperSide->surface, NULL, screen, &(upperSide->position));
            SDL_BlitSurface(lowerSide->surface, NULL, screen, &(lowerSide->position));
     
            SDL_BlitSurface(leftPaddle->surface, NULL, screen, &(leftPaddle->position));
            SDL_BlitSurface(rightPaddle->surface, NULL, screen, &(rightPaddle->position));
     
            SDL_BlitSurface(ball->surface, NULL, screen, &(ball->position));
     
            SDL_BlitSurface(player1Score->surface, NULL, screen, &(player1Score->position));
            SDL_BlitSurface(hyphen->surface, NULL, screen, &(hyphen->position));
            SDL_BlitSurface(player2Score->surface, NULL, screen, &(player2Score->position));
     
            SDL_Flip(screen);
            // end : sends new image to screen
        }
        // end : game
     
        SDL_RemoveTimer(ballTimer); // ends ball timer
    }
     
    Uint32 moveBall(Uint32 timespan, void *parameter) {
        moveBallStruct *ballMovement = parameter;
        switch(ballMovement->ballPointer->direction) {
            case LEFT:
                if(ballMovement->ballPointer->position.x == 10) {
                    *(ballMovement->continuePointPointer) = 0; // point won by Player2
                }
                else if(ballMovement->ballPointer->position.x == ballMovement->leftPaddlePointer->position.x + ballMovement->leftPaddlePointer->width && (ballMovement->ballPointer->position.y + ballMovement->ballPointer->size > ballMovement->leftPaddlePointer->position.y && ballMovement->ballPointer->position.y < ballMovement->leftPaddlePointer->position.y + ballMovement->leftPaddlePointer->height)) {
                    ballMovement->ballPointer->direction = RIGHT;
                    ballMovement->ballPointer->position.x++; // changes ball's direction to right if ball is hit by left paddle
                }
                else
                    ballMovement->ballPointer->position.x--; // moves ball left
                break;
            case RIGHT:
                if(ballMovement->ballPointer->position.x == SCREEN_WIDTH - (ballMovement->ballPointer->size + 10)) {
                    *(ballMovement->continuePointPointer) = 0; // point won by Player1
                }
                else if(ballMovement->ballPointer->position.x + ballMovement->ballPointer->size == ballMovement->rightPaddlePointer->position.x && (ballMovement->ballPointer->position.y + ballMovement->ballPointer->size > ballMovement->rightPaddlePointer->position.y && ballMovement->ballPointer->position.y < ballMovement->rightPaddlePointer->position.y + ballMovement->rightPaddlePointer->height)) {
                    ballMovement->ballPointer->direction = LEFT;
                    ballMovement->ballPointer->position.x--; // changes ball's direction to left if ball is hit by right paddle
                }
                else
                    ballMovement->ballPointer->position.x++; // moves ball right
                break;
            default:
                break;
        }
     
        return timespan;
    }
     
    void movePaddle(Paddle *paddle, int paddleNewDirection, int paddleHeight, int sidePositionY, int sideHeight) {
        switch(paddleNewDirection) {
            case UP:
                if(paddle->position.y > sidePositionY + sideHeight)
                    paddle->position.y--;
                break; // moves paddle up if not already blocked by upper side
            case DOWN:
                if(paddle->position.y + paddleHeight < sidePositionY)
                    paddle->position.y++;
                break; // moves paddle down if not already blocked by lower side
            default:
                break;
        }
    }

    Je n'en suis pas au tout début, mais pour le moment le jeu est d'une pauvreté absolue, puisqu'il ne permet que de renvoyer la balle horizontalement. Mais puisque je ne suis pas super à l'aise et que je veux bien comprendre ce que je fais, je préfère maintenant résoudre certains problèmes avant de continuer à implémenter des trucs :

    - Lorsque je déplace la souris dans tous les sens pendant quelques secondes, le programme ne répond ensuite à une entrée clavier qu'au bout d'un certain temps (en gros proportionnel au temps utilisé par la souris). J'ai l'impression que l'ordi ne s'en sort plus lors du traitement des évènements souris... Y a-t-il une optimisation du code à faire ? Je pense que oui quand-même, mais laquelle ?

    - La fonction SDL_EnableKeyRepeat() est étrange. Même en la commentant, j'arrive à déplacer ma raquette de pleins de pixels en laissant la touche enfoncée. Je pensais qu'il fallait absolument relâcher la pression sur la touche, puis réappuyer, etc... sans cette fonction ?

    - Il m'est impossible de bouger les 2 raquettes en même temps. Si j'appuie sur une autre touche pendant qu'une première est enfoncée, alors la première arrête d'agir et c'est la nouvelle touche qui prend le relai. Je sais que mon code ne gère pas spécialement ce problème, mais à mon niveau cela me dépasse un peu. Quelle solution implémenter ?

    - Le jeu est très lent ! Le déplacement des raquettes, ainsi que celui de la balle, butent sur une limite de l'ordre de 50 à 100 mouvements par seconde. Est-ce une vraie limite due à mon ordi ou au code ? Dois-je gérer des mouvements de plus d'un pixel en cas de déplacement rapide ?


    Je précise que ce code compile et ne génère aucune erreur ni warning chez moi. J'utilise Code::Blocks sous GNU/Linux, sur un Athlon 1800+ agrémenté de 256 petits Mo de RAM et d'une faiblarde GeForce2. Même si je doute que ceci constitue une limite pour un Pong !!

    Aussi, n'hésitez pas pour toutes vos remarques à propos de l'écriture du code, dans le fond comme dans la forme. Le but est surtout de progresser, pas de vous vendre un Pong. Merci d'avance à tous ceux qui se pencheront sur mon post.

  2. #2
    Membre du Club
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 58
    Points : 63
    Points
    63
    Par défaut
    Bonjour

    Sur la question de la gestion des touches, c'est un problème classique de la SDL : la solution consiste à ne pas utiliser la fonction SDL_EnableKeyRepeat() et à gérer des variables d'états indiquant si une touche est relachée ou enfoncée en utilisant les événements SDL_KEYUP et SDL_KEYDOWN. Cela permet de gérer l'appui simultané de plusieurs touches.

    Dans tout les cas l'instruction :
    SDL_EnableKeyRepeat(10, 10); // sets key repeat
    est problèmatique, tu demande une répétition des touches toutes les 10ms donc 100 fois par secondes, c'est beaucoup trop.

    Les problème de ralentissement que tu évoque sont sans doute lié à cela.

  3. #3
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Bonjour,

    Critique du code :

    main.c
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    menu = IMG_Load("menu.png");
    Il manque un test ici pour s'assurer que le fichier est bien chargé.

    Pareil, un test est nécessaire ici. Voir http://jeux.developpez.com/faq/sdl/?...NTS_repetition.

    - Parce que tu utilises SDL_WaitEvent, tu devrais plutôt mettre ton code de rendu avant l'appel SDL_WaitEvent. Sinon, il faut attendre un événement pour faire le premier affichage.

    - Je déconseille généralement ta facon de gérer le menu. Cela rend les améliorations difficiles. Par exemple, comment faire pour retourner au menu en plein milieu du jeu ? Sortir de la fonction playGame ? Mais alors toutes tes variables locales seront perdues...
    -> Il vaut mieux avoir un système d'état qui gére cela et appelle la bonne fonction de rendu.

    game.h

    - Ce fichier utilise SDL_Surface, il faut donc un #include <SDL.h>. Il faut toujours qu'un .h soit autonome.

    point.h

    - Pareil, il manque un #include <SDL.h>

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        typedef struct Image Image;
        struct Image {
            SDL_Surface *surface;
            SDL_Rect position;
        };
    peut être résumé en :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
        typedef struct Image {
            SDL_Surface *surface;
            SDL_Rect position;
        }Image;
    - la fonction movePaddle ne prend pas en compte le temps pour faire les mouvements. Cela est dommage. Du coup, dépendant du taux d'affichage, le jeu sera plus ou moins long...

    - pareil pour moveBall et cette fonction et très compliquée à comprendre, tu pourrais faire une fonction qui gére la collision avec le paddle par exemple.
    Je ne vois pas non plus le but du retour de moveBall... d'ailleurs, vu que tu ne géres pas le temps pour calculer le mouvement, ce paramètre ne sert à rien

    - la fonction playpoint :
    1) utiliser un timer pour le mouvement... pourquoi ?
    - utilise plutôt un appel à la fonction moveBall qui ignore le mouvement si le temps n'est pas nécessaire -> permettra de faire un code qui utilise timespan plus facilement.
    - il faut tester le retour de SDL_PollEvent, c'est pour cela que tu as un problème.
    - normalement, une seule boucle d'événements est conseillé et tu passes l'événement à la fonction nécessaire.

    game.c

    - Il faut tester le retour IMG_Load.
    - upperside.surface et lowerside.surface sont les mêmes images, tu devrais charger l'image une seule fois.
    - pareil pour les surfaces des paddles
    - Attention, il faut donner les masques à SDL_CreateRGBSurface, mettre 0 est une erreur (bien que je ne sais pas exactement ce qu'il fait en interne)
    -> tu ne mets jamais le score à jour ?


    En bref :

    - C'est pas mal du tout, le code est propre et t'as pensé à beaucoup de détails qui sont généralement oublié.
    -> mais il manque quelques tests importants
    - Il faudrait avoir une seule boucle événement qui gére tout et propage soit au menu, soit au jeu.

    Répondons aux problèmes :
    Citation Envoyé par kryszcztov Voir le message
    - Lorsque je déplace la souris dans tous les sens pendant quelques secondes, le programme ne répond ensuite à une entrée clavier qu'au bout d'un certain temps (en gros proportionnel au temps utilisé par la souris). J'ai l'impression que l'ordi ne s'en sort plus lors du traitement des évènements souris... Y a-t-il une optimisation du code à faire ? Je pense que oui quand-même, mais laquelle ?
    Il faut tester le retour de SDL_PollEvent, généralement on met un while d'ailleurs pour gérer tous les événements avant de faire le prochain rendu.

    - La fonction SDL_EnableKeyRepeat() est étrange. Même en la commentant, j'arrive à déplacer ma raquette de pleins de pixels en laissant la touche enfoncée. Je pensais qu'il fallait absolument relâcher la pression sur la touche, puis réappuyer, etc... sans cette fonction ?
    Aussi, cela est dû à l'oubli du test sur SDL_PollEvent.

    - Il m'est impossible de bouger les 2 raquettes en même temps. Si j'appuie sur une autre touche pendant qu'une première est enfoncée, alors la première arrête d'agir et c'est la nouvelle touche qui prend le relai. Je sais que mon code ne gère pas spécialement ce problème, mais à mon niveau cela me dépasse un peu. Quelle solution implémenter ?
    A voir si cela se résoud avec le test sur SDL_PollEvent, mais sinon se souvenir si les touches ont été appuyé et regarder cela. Je peux montrer comment faire si cela ne se résoud pas avec les tests manquant.

    - Le jeu est très lent ! Le déplacement des raquettes, ainsi que celui de la balle, butent sur une limite de l'ordre de 50 à 100 mouvements par seconde. Est-ce une vraie limite due à mon ordi ou au code ? Dois-je gérer des mouvements de plus d'un pixel en cas de déplacement rapide ?
    Dû au manque du test surement.

    Joli travail quand même, mes félicitations pour une première tentative,
    Jc

  4. #4
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    16
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 16
    Points : 1
    Points
    1
    Par défaut
    Tout d'abord, merci Bibicy pour ton post, et ça fait plaisir de voir qu'il est corroboré par fearyourself dans le sien. Ca rassure...

    J'en viens au pavé du modérateur.

    Citation Envoyé par fearyourself Voir le message
    - Parce que tu utilises SDL_WaitEvent, tu devrais plutôt mettre ton code de rendu avant l'appel SDL_WaitEvent. Sinon, il faut attendre un événement pour faire le premier affichage.
    Oui, je m'étais posé la question déjà, et il me semblait qu'en cours l'année dernière le prof nous avait dressé le schéma usuel de cette manière. Premier hint : je n'ai pas pondu mon code ex nihilo... Quoi qu'il en soit, j'imagine que le programme traite toujours un évènement juste après son lancement, car je tombe toujours directement sur l'image du menu. Probablement l'évènement du lancement lui-même, ou du résidu venant de je ne sais où...

    - Je déconseille généralement ta facon de gérer le menu. Cela rend les améliorations difficiles. Par exemple, comment faire pour retourner au menu en plein milieu du jeu ? Sortir de la fonction playGame ? Mais alors toutes tes variables locales seront perdues...
    -> Il vaut mieux avoir un système d'état qui gére cela et appelle la bonne fonction de rendu.
    Hmmm, tu as un contre-exemple simple qui permet de voir que mon implémentation n'est pas assez flexible ? J'avoue ne pas voir ce que je perds comme info importante au sortir de playGame().

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        typedef struct Image Image;
        struct Image {
            SDL_Surface *surface;
            SDL_Rect position;
        };
    peut être résumé en :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
        typedef struct Image {
            SDL_Surface *surface;
            SDL_Rect position;
        }Image;
    Ouep, encore un truc vu en cours, et deuxième hint : ce n'est pas de moi...

    - la fonction movePaddle ne prend pas en compte le temps pour faire les mouvements. Cela est dommage. Du coup, dépendant du taux d'affichage, le jeu sera plus ou moins long...
    Oui voilà, on commence à toucher à l'un de mes problèmes. C'est clair que si je bute sur une limite d'affichage (dû au temps mis par les BlitSurface ou dû à la gestion des évènements ?), alors la raquette ne bougera pas vite. Je vais y réfléchir...

    - pareil pour moveBall et cette fonction et très compliquée à comprendre, tu pourrais faire une fonction qui gére la collision avec le paddle par exemple.
    Je ne vois pas non plus le but du retour de moveBall... d'ailleurs, vu que tu ne géres pas le temps pour calculer le mouvement, ce paramètre ne sert à rien
    Ah c'est déjà compliqué comme çà ? Bon, je n'ai plus qu'à faire cette fonction de collision alors. Pour le paramètre de retour, apparemment c'est obligatoire pour cette fonction de callback ; il est censé indiquer le temps avant qu'on veuille appeler la fonction une nouvelle fois. Troisième hint : je tiens ces explications d'ailleurs... A propos, n'est-il pas mieux de se passer de cette fonction de callback ? Quoi donc utiliser à la place ?

    - la fonction playpoint :
    1) utiliser un timer pour le mouvement... pourquoi ?
    - utilise plutôt un appel à la fonction moveBall qui ignore le mouvement si le temps n'est pas nécessaire -> permettra de faire un code qui utilise timespan plus facilement.
    - il faut tester le retour de SDL_PollEvent, c'est pour cela que tu as un problème.
    - normalement, une seule boucle d'événements est conseillé et tu passes l'événement à la fonction nécessaire.
    Ca va bousculer mon code, mais si ça peut permettre de le faire marcher... Le test sur le PollEvent, pas pensé, je vais devoir me mettre à jour.

    - Attention, il faut donner les masques à SDL_CreateRGBSurface, mettre 0 est une erreur (bien que je ne sais pas exactement ce qu'il fait en interne)
    J'irai voir la doc de la SDL à ce propos, mais ça ne m'a pas généré d'erreur jusque là.

    -> tu ne mets jamais le score à jour ?
    Alors : non. Pour une raison très simple : le jeu n'est pas encore fini, voire jouable. Je préfèrais d'abord régler mes problèmes avant de rajouter plein de lignes. Mais le score est pour le moment un détail : en effet actuellement, si un joueur ne rattrappe pas la balle, alors celle-ci continue et s'arrête d'avancer juste avant le bord de la fenêtre, mais toujours en essayant d'avancer (boucle d'évènements, etc...). Donc pour résumer, les points ne s'enchaînent pas, et donc un score est inutile pour le moment. J'ajoute que la balle ne bouge qu'horizontalement ; implémenter le mouvement vertical qui va avec n'est pas ma priorité, mais viendra bientôt.

    Il faut tester le retour de SDL_PollEvent, généralement on met un while d'ailleurs pour gérer tous les événements avant de faire le prochain rendu.


    Aussi, cela est dû à l'oubli du test sur SDL_PollEvent.


    A voir si cela se résoud avec le test sur SDL_PollEvent, mais sinon se souvenir si les touches ont été appuyé et regarder cela. Je peux montrer comment faire si cela ne se résoud pas avec les tests manquant.


    Dû au manque du test surement.
    OK, je pense aussi que tous ces problèmes partagent la même cause, ou du moins ne sont pas tous indépendants les uns des autres.

    Joli travail quand même, mes félicitations pour une première tentative
    Merci. Bon, réponse du hint : j'ai basé mon code sur le squelette du tuto d'un autre site (avec un bourricot comme mascotte ), voilà pourquoi tu dois te dire que j'ai "pensé" à pas mal de détails, et aussi pourquoi j'ai "fait" tel ou tel choix. Cela dit, j'avais déjà commencé à remettre ce squelette un peu à ma sauce, mais sans tout chambouler.

    Je manque de temps pour m'y mettre sérieusement en semaine après le taff (surtout avec les chamboulements que tu préconises), mais j'essaierai de régler les points soulevés ce weekend, en espérant que ça améliore le bousin.

    Un grand merci pour ton aide très détaillée, cela fait plaisir d'être "accueilli" comme çà !

  5. #5
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Citation Envoyé par kryszcztov Voir le message
    Hmmm, tu as un contre-exemple simple qui permet de voir que mon implémentation n'est pas assez flexible ? J'avoue ne pas voir ce que je perds comme info importante au sortir de playGame().
    Toutes les positions : la balle, les raquettes, etc.

    Quand tu sortiras des fonctions il faut réinitialiser le tout et donc on perd aussi le score par exemple...


    Ah c'est déjà compliqué comme çà ? Bon, je n'ai plus qu'à faire cette fonction de collision alors. Pour le paramètre de retour, apparemment c'est obligatoire pour cette fonction de callback ; il est censé indiquer le temps avant qu'on veuille appeler la fonction une nouvelle fois. Troisième hint : je tiens ces explications d'ailleurs... A propos, n'est-il pas mieux de se passer de cette fonction de callback ? Quoi donc utiliser à la place ?
    Oui, je n'avais pas vu que c'était un timer.

    On appelle la fonction à chaque itération de la boucle et elle décide s'il faut bouger ou non. Un timer est peu pratique dans ce cas là.

    J'irai voir la doc de la SDL à ce propos, mais ça ne m'a pas généré d'erreur jusque là.
    Le plus simple est de faire quelque chose comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
     
        tmp = SDL_CreateRGBSurface(SDL_HWSURFACE, leftPaddle.width, leftPaddle.height, ,
        screen->format->BitsPerPixel,
                 screen->format->Rmask,
                 screen->format->Gmask, 
                 screen->format->Bmask,
                 screen->format->Amask);
        if(tmp != NULL) {
           leftPaddle.surface = SDL_DisplayFormat(tmp);
           SDL_FreeSurface(tmp);
        }
        else {
           leftPaddle.surface = NULL;
        }
    Le coup du DisplayFormat permet de rendre tout le programme plus rapide.

    Jc

  6. #6
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    16
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 16
    Points : 1
    Points
    1
    Par défaut
    Je suis en train de changer plein de trucs dans mon code, mais ce faisant, j'ai quelques questions sur tes remarques...

    Citation Envoyé par fearyourself Voir le message
    Pareil, un test est nécessaire ici. Voir http://jeux.developpez.com/faq/sdl/?...NTS_repetition.
    J'avoue ne pas bien comprendre l'explication donnée sur cette page, surtout cette phrase :
    S'il n'y a pas d'événements en attente, la fonction ne modifie pas la structure passé par pointeur et nous allons traiter une deuxième (ou n-ième) fois le même événement.
    Il y a un évènement ou pas alors ?? Que peut-il se passer de mal très concrètement ? Un exemple pourrait m'éclairer.

    Citation Envoyé par fearyourself Voir le message
    Le plus simple est de faire quelque chose comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
     
        tmp = SDL_CreateRGBSurface(SDL_HWSURFACE, leftPaddle.width, leftPaddle.height, ,
        screen->format->BitsPerPixel,
                 screen->format->Rmask,
                 screen->format->Gmask, 
                 screen->format->Bmask,
                 screen->format->Amask);
        if(tmp != NULL) {
           leftPaddle.surface = SDL_DisplayFormat(tmp);
           SDL_FreeSurface(tmp);
        }
        else {
           leftPaddle.surface = NULL;
        }
    Le coup du DisplayFormat permet de rendre tout le programme plus rapide.
    Là aussi j'avoue ne pas tout piger. La doc wiki (en anglais) de la SDL a l'air de dire que mettre les 4 derniers champs à 0 est possible. Par contre, je ne comprends pas ce que c'est que "a default value, based on the depth". La page en question : SDL_CreateRGBSurface.
    Du coup, je ne vois pas à quoi sert ton code, puisque tu reformattes la surface selon les paramètres video du programme. A ce propos, quels sont ces fameux paramètres qui sont gérés par la fonction SDL_DisplayFormat() ?

    Dernièrement, j'ai repris une fonction d'un tuto de ce site, en l'adaptant car il me semble qu'à la base c'est en C++, sur la page Chapitre II : Premières applications avec SDL.
    Voici mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    SDL_Surface *loadImage(char fileName[]) {
        SDL_Surface *loadedImage = NULL;
        SDL_Surface *optimizedImage = NULL;
     
        loadedImage = IMG_Load(fileName); // aussi simple que çà ?
        if(loadedImage == NULL) {
            fprintf(stderr, "Unable to load an image : %s\n", SDL_GetError());
            exit(EXIT_FAILURE);
        }
     
        optimizedImage = SDL_DisplayFormat(loadedImage);
        SDL_FreeSurface(loadedImage);
     
        return optimizedImage;
    }
    Le programme compile et tourne pareil qu'avant. Mais je voulais être sûr de ce que j'ai écrit pour la gestion du fileName, sachant que ce qui est passé en paramètre est une chaîne de caractères, comme ici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    upperSide.surface = loadImage("side.png");
    J'aimerais aussi spécifier quelle image a échoué au chargement dans le commentaire envoyé au fichier d'erreur, je suppose que c'est simple aussi ?
    A ce propos, où se trouve ce fichier d'erreur dans mon arborescence GNU/Linux ?

  7. #7
    Rédacteur

    Avatar de loka
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Novembre 2004
    Messages
    2 672
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Service public

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 672
    Points : 5 509
    Points
    5 509
    Par défaut
    Pour SDL_WaitEvent(), il te faut tester le retour de la fonction :
    Il est donc conseillé (pour ne pas dire obligatoire) de vérifier le retour de ces fonctions car, dans le cas d'un retour 0, le paramètre event reste inchangé.
    et donc faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if(SDL_WaitEvent(&event))
    Ce que tu quote, c'est pour SDL_PollEvent(), en gros ça explique que si il n'y a pas de nouveaux évènements, il va garder le dernier et le traiter à chaque tour de boucle, jusqu'à ce qu'un nouvel évènement le remplace.

    Pour le fileName en C, j'aurais plutôt tendance à utiliser :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SDL_Surface *loadImage(char *fileName) {
    Après, il me semble que c'est équivalent à ce que tu as écris, mais comme j'ai un doute, je préfère te mettre ce que je sais qui marche bien

    Pour spécifier le nom de l'image qui n'a pas bien chargé :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fprintf(stderr, "Impossible de charger l'image : %s\n", fileName);
    Sous linux, tu n'as pas de fichiers d'erreur (c'est uniquement sous windows), les erreurs s'affichent dans la console sous linux

  8. #8
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Citation Envoyé par kryszcztov Voir le message
    Là aussi j'avoue ne pas tout piger. La doc wiki (en anglais) de la SDL a l'air de dire que mettre les 4 derniers champs à 0 est possible. Par contre, je ne comprends pas ce que c'est que "a default value, based on the depth". La page en question : SDL_CreateRGBSurface.
    Du coup, je ne vois pas à quoi sert ton code, puisque tu reformattes la surface selon les paramètres video du programme.
    Effectivement, d'après le wiki, si tu mets tout à zéro, il va le gérer en interne correctement, j'avais un doute...

    A ce propos, quels sont ces fameux paramètres qui sont gérés par la fonction SDL_DisplayFormat() ?
    Ca dépend de beaucoup de choses, disons que sans tu peux avoir une grande perte de performances, c'est tout ce qui est important...

    Jc

  9. #9
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    16
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 16
    Points : 1
    Points
    1
    Par défaut
    Heureusement que j'avais vu ce sous-forum sur la SDL hier, sinon je ne sais pas si j'aurais compris où mon thread était passé... Enfin ça a plus de sens ici puisque c'est un jeu en SDL.


    Bon alors en fait je ne change pas mon code par rapport à mes 3 questions d'hier soir (enfin presque) !

    - Le test du retour de SDL_WaitEvent() et SDL_PollEvent(), je l'avais déjà intégré dans le code, mais maintenant je commence à bien comprendre l'histoire. Un truc que j'avais pas pigé, c'est le coup de l'évènement qui reste "collé" au WaitEvent ou au PollEvent si aucun autre évènement ne vient le "pousser" ; je pensais qu'une fois traité, l'évènement disparaissait... Et puis cela me paraît évident maintenant qu'il faille traiter un évènement (par un switch, par exemple) seulement s'il y en a un nouveau. Bref, compris.

    - Les masques de la fonction SDL_CreateRGBSurface(), et bien mettre 0 partout devrait faire l'affaire donc. Mais j'ai pris conscience de l'intérêt de la fonction SDL_DisplayFormat(), que j'utiliserai maintenant pour tout chargement d'image (dans ma fonction de mon précédent post). Apparemment cette fonction ne veut pas marcher sur l'icône, mais c'est sans importance puisque celle-ci est en-dehors de mon écran SDL.

    - Bien compris pour le fileName et la sortie d'erreur. J'ai testé le programme en déplaçant au préalable une image à charger, et en fait la fonction SDL_GetError() raconte ce qu'il faut à propos de l'erreur (bien sûr), donc pas besoin de spécifier l'image qui a raté. Je vais juste enlever la phrase dans le fprintf(), puisqu'elle est inutile, et mettre cette instruction et le exit(EXIT_FAILURE) dans une fonction commune à tous les problèmes.


    Bon, j'ai encore du pain sur la planche par rapport aux premières remarques que j'ai eues dans ce thread, autant essayer de tout faire avant de resposter le code.

    Sinon j'ai une question. Quand j'ai volontairement déplacé une des images à charger, le programme s'est bien sûr arrêté* (on ne peut pas dire qu'il s'est planté, n'est-ce pas ? puisque je gère l'erreur). La question est : SDL a-t-elle sagement libéré toutes les surfaces qui avaient été préalablement allouées dynamiquement ? Autrement dit : n'y a-t-il pas de fuite de mémoire ?

    * Je précise que pour le moment, j'arrête le programme dès qu'un chargement d'image ne réussit pas. Ce n'est bien sûr pas optimal, mais j'ai mon idée à ce propos pour la suite.

  10. #10
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Citation Envoyé par kryszcztov Voir le message
    Heureusement que j'avais vu ce sous-forum sur la SDL hier, sinon je ne sais pas si j'aurais compris où mon thread était passé... Enfin ça a plus de sens ici puisque c'est un jeu en SDL.
    T'as du recevoir un message à propos de ce déplacement, regarde en haut à droite de la page.

    - Le test du retour de SDL_WaitEvent() et SDL_PollEvent(), je l'avais déjà intégré dans le code, mais maintenant je commence à bien comprendre l'histoire. Un truc que j'avais pas pigé, c'est le coup de l'évènement qui reste "collé" au WaitEvent ou au PollEvent si aucun autre évènement ne vient le "pousser" ; je pensais qu'une fois traité, l'évènement disparaissait... Et puis cela me paraît évident maintenant qu'il faille traiter un évènement (par un switch, par exemple) seulement s'il y en a un nouveau. Bref, compris.
    Oui donc la meilleure solution est :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    while(SDL_PollEvent(&event)) {
      /* Gestion code */
    }
    et pareil pour le WaitEvent.

    Apparemment cette fonction ne veut pas marcher sur l'icône, mais c'est sans importance puisque celle-ci est en-dehors de mon écran SDL.
    Cela dépend, pourquoi cela ne fonctionne pas (mais je suis d'accord que cela devrait avoir une faible incidence sur les performances.

    - Bien compris pour le fileName et la sortie d'erreur. J'ai testé le programme en déplaçant au préalable une image à charger, et en fait la fonction SDL_GetError() raconte ce qu'il faut à propos de l'erreur (bien sûr), donc pas besoin de spécifier l'image qui a raté. Je vais juste enlever la phrase dans le fprintf(), puisqu'elle est inutile, et mettre cette instruction et le exit(EXIT_FAILURE) dans une fonction commune à tous les problèmes.
    Tu peux faire cela en effet.

    Sinon j'ai une question. Quand j'ai volontairement déplacé une des images à charger, le programme s'est bien sûr arrêté* (on ne peut pas dire qu'il s'est planté, n'est-ce pas ? puisque je gère l'erreur). La question est : SDL a-t-elle sagement libéré toutes les surfaces qui avaient été préalablement allouées dynamiquement ? Autrement dit : n'y a-t-il pas de fuite de mémoire ?
    Il faut définir fuite de mémoire. Est-ce que cela provoque une fuite :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    int main(void) 
    {
       int *t = malloc(sizeof(*t));
       *t = 4;
       printf("Genial ! %d\n", *t);
       return EXIT_SUCCESS;
    }
    Ce qu'il faut savoir :
    - Le système gére tout seul la désallocation de la mémoire d'un processus lors de sa terminaison.

    Mais :
    - Certains diront oui car il faut toujours désallouer une allocation dynamique même si on sort du programme.
    - Certains diront non car le programme se termine et le système s'en charge.

    Personnellement, je suis entre les deux. Cela dépend vraiment ce qu'on veut faire. Du coup, si je fais une fonction qui charge des images et qu'à la 5ème ca plante, je libére les autres et je retourne un code d'erreur.

    La fonction qui a appelé ma fonction de chargement peut décider de soit continuer l'exécution soit sortir. Dans ce dernier cas, il faudrait qu'elle libére la mémoire et retourne un code d'erreur, etc.

    Voilà comment je fais lorsque je veux quelque chose de propre (ce qui est assez souvent).

    Jc

  11. #11
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    16
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 16
    Points : 1
    Points
    1
    Par défaut
    Citation Envoyé par fearyourself Voir le message
    T'as du recevoir un message à propos de ce déplacement, regarde en haut à droite de la page.
    Si tu fais allusion aux messages privés, et bien, non, je n'en ai pas reçu. Mais ce n'est pas grave, j'avais retrouvé mon thread comme un grand.

    Cela dépend, pourquoi cela ne fonctionne pas (mais je suis d'accord que cela devrait avoir une faible incidence sur les performances.
    Je ne sais pas pourquoi ça ne marche pas. Mon icône est une image PNG que j'ai faite sous GIMP.


    Bon alors voilà, j'ai complètement restructuré mon code avec vos conseils à tous, le voici :

    main.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    #include <stdlib.h>
    #include <stdio.h>
    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
     
    #include "constants.h"
    #include "refresh.h"
    #include "surfaces.h"
    #include "movement.h"
     
    int main(int argc, char *argv[])
    {
        // variables
        int programStatus = 1, pointStatus = 1;
        SDL_Surface *icon = NULL, *screen = NULL;
        Image menu = {NULL, {0, 0}};
        Side upperSide = {700, 50, NULL, {0, 0}}, lowerSide = {700, 50, NULL, {0, 0}};
        Paddle leftPaddle = {10, 50, NULL, {0, 0}, NONE}, rightPaddle = {10, 50, NULL, {0, 0}, NONE};
        Ball ball = {6, NULL, {0, 0}, LEFT};
        Image player1Score = {NULL, {0, 0}}, hyphen = {NULL, {0, 0}}, player2Score = {NULL, {0, 0}};
        SDL_Event event;
        int previousTime = 0, currentTime = 0;
        Uint8 *keystate;
     
        // SDL initialization
        if(SDL_Init(SDL_INIT_VIDEO) == -1)
            exitOnError(icon, menu.surface, upperSide.surface, lowerSide.surface, leftPaddle.surface, rightPaddle.surface, ball.surface, player1Score.surface, hyphen.surface, player2Score.surface);
     
        // sets caption
        SDL_WM_SetCaption("Pong", NULL);
     
        // sets icon
        icon = IMG_Load("icon.png");
        if(icon == NULL)
            exitOnError(icon, menu.surface, upperSide.surface, lowerSide.surface, leftPaddle.surface, rightPaddle.surface, ball.surface, player1Score.surface, hyphen.surface, player2Score.surface);
        SDL_WM_SetIcon(icon, NULL);
     
        // sets video mode
        screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
        if(screen == NULL)
            exitOnError(icon, menu.surface, upperSide.surface, lowerSide.surface, leftPaddle.surface, rightPaddle.surface, ball.surface, player1Score.surface, hyphen.surface, player2Score.surface);
     
        // loads surfaces
        menu.surface = loadImage("menu.png", icon, menu.surface, upperSide.surface, lowerSide.surface, leftPaddle.surface, rightPaddle.surface, ball.surface, player1Score.surface, hyphen.surface, player2Score.surface);
     
        upperSide.surface = loadImage("side.png", icon, menu.surface, upperSide.surface, lowerSide.surface, leftPaddle.surface, rightPaddle.surface, ball.surface, player1Score.surface, hyphen.surface, player2Score.surface);
        lowerSide.surface = copySurface(upperSide.surface);
     
        leftPaddle.surface = SDL_CreateRGBSurface(SDL_HWSURFACE, leftPaddle.width, leftPaddle.height, 32, 0, 0, 0, 0);
        SDL_FillRect(leftPaddle.surface, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
        rightPaddle.surface = SDL_CreateRGBSurface(SDL_HWSURFACE, rightPaddle.width, rightPaddle.height, 32, 0, 0, 0, 0);
        SDL_FillRect(rightPaddle.surface, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
     
        ball.surface = SDL_CreateRGBSurface(SDL_HWSURFACE, ball.size, ball.size, 32, 0, 0, 0, 0);
        SDL_FillRect(ball.surface, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
     
        player1Score.surface = loadImage("zero.png", icon, menu.surface, upperSide.surface, lowerSide.surface, leftPaddle.surface, rightPaddle.surface, ball.surface, player1Score.surface, hyphen.surface, player2Score.surface);
        hyphen.surface = loadImage("hyphen.png", icon, menu.surface, upperSide.surface, lowerSide.surface, leftPaddle.surface, rightPaddle.surface, ball.surface, player1Score.surface, hyphen.surface, player2Score.surface);
        player2Score.surface = copySurface(player1Score.surface);
     
        setSurfaces(&menu, &upperSide, &lowerSide, &player1Score, &hyphen, &player2Score);
     
        // main loop
        while(programStatus != 0)
        {
            // refreshes screen
            switch(programStatus)
            {
                case 1:
                    refreshMenuScreen(screen, &menu);
                    break;
                case 2:
                    refreshGameScreen(screen, &upperSide, &lowerSide, &leftPaddle, &rightPaddle, &ball, &player1Score, &hyphen, &player2Score);
                    break;
                default:
                    break;
            }
            // polls for events
            while(SDL_PollEvent(&event))
            {
                switch(event.type)
                {
                    case SDL_QUIT:
                        programStatus = 0; // quits program
                        break;
                    case SDL_KEYDOWN:
                        switch(event.key.keysym.sym)
                        {
                            case SDLK_ESCAPE:
                                if(programStatus == 1 || programStatus == 2)
                                    programStatus--; // returns to menu if status is game, quits program if status is menu
                                break;
                            case SDLK_RETURN:
                                if(programStatus == 1)
                                {
                                    programStatus = 2; // starts a game
                                    resetGame(&upperSide, &leftPaddle, &rightPaddle, &ball);
                                }
                                break;
                            case SDLK_SPACE:
                                if(programStatus == 2)
                                    programStatus = 3; // starts pause
                                else if(programStatus == 3)
                                    programStatus = 2; // ends pause
                                break;
                            case SDLK_r:
                                leftPaddle.direction = UP; // up key for Player 1 is pressed
                                break;
                            case SDLK_f:
                                leftPaddle.direction = DOWN; // down key for Player 1 is pressed
                                break;
                            case SDLK_UP:
                                rightPaddle.direction = UP; // up key for Player 2 is pressed
                                break;
                            case SDLK_DOWN:
                                rightPaddle.direction = DOWN; // down key for Player 2 is pressed
                                break;
                            default:
                                break;
                        }
                        break;
                    case SDL_KEYUP:
                        keystate = SDL_GetKeyState(NULL);
                        switch(event.key.keysym.sym)
                        {
                            case SDLK_r:
                                if(keystate[SDLK_f])
                                    leftPaddle.direction = DOWN; // down key for Player 1 is still pressed
                                else
                                    leftPaddle.direction = NONE; // neither key for Player 1 is now pressed
                                break;
                            case SDLK_f:
                                if(keystate[SDLK_r])
                                    leftPaddle.direction = UP; // up key for Player 1 is still pressed
                                else
                                    leftPaddle.direction = NONE; // neither key for Player 1 is now pressed
                                break;
                            case SDLK_UP:
                                if(keystate[SDLK_DOWN])
                                    rightPaddle.direction = DOWN; // down key for Player 2 is still pressed
                                else
                                    rightPaddle.direction = NONE; // neither key for Player 2 is now pressed
                                break;
                            case SDLK_DOWN:
                                if(keystate[SDLK_UP])
                                    rightPaddle.direction = UP; // up key for Player 2 is still pressed
                                else
                                    rightPaddle.direction = NONE; // neither key for Player 2 is now pressed
                                break;
                            default:
                                break;
                        }
                        break;
                    default:
                        break;
                }
            }
            // moves paddles if matching keys are pressed
            if(programStatus == 2)
            {
                switch(leftPaddle.direction)
                {
                    case UP:
                        movePaddle(&leftPaddle, UP, &upperSide); // moves left paddle up
                        break;
                    case DOWN:
                        movePaddle(&leftPaddle, DOWN, &lowerSide); // moves left paddle down
                        break;
                    default:
                        break;
                }
                switch(rightPaddle.direction)
                {
                    case UP:
                        movePaddle(&rightPaddle, UP, &upperSide); // moves right paddle up
                        break;
                    case DOWN:
                        movePaddle(&rightPaddle, DOWN, &lowerSide); // moves right paddle down
                        break;
                    default:
                        break;
                }
            }
            // moves ball if enough time has passed already
            if(programStatus == 2)
            {
                currentTime = SDL_GetTicks();
                if(currentTime - previousTime > 10)
                {
                    moveBall(&pointStatus, &ball, &leftPaddle, &rightPaddle);
                    previousTime = currentTime;
                }
                else
                    SDL_Delay(10 - (currentTime - previousTime)); // waits enough time
            }
        }
        // frees surfaces
        SDL_FreeSurface(icon);
        SDL_FreeSurface(menu.surface);
        SDL_FreeSurface(upperSide.surface);
        SDL_FreeSurface(lowerSide.surface);
        SDL_FreeSurface(leftPaddle.surface);
        SDL_FreeSurface(rightPaddle.surface);
        SDL_FreeSurface(ball.surface);
        SDL_FreeSurface(player1Score.surface);
        SDL_FreeSurface(hyphen.surface);
        SDL_FreeSurface(player2Score.surface);
     
        // SDL quit
        SDL_Quit();
     
        return EXIT_SUCCESS;
    }
    constants.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #ifndef DEF_CONSTANTS
    #define DEF_CONSTANTS
     
        #define SCREEN_WIDTH 800
        #define SCREEN_HEIGHT 600
     
        enum {UP, NONE, DOWN};
        enum {LEFT, RIGHT};
     
    #endif
    refresh.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <SDL.h>
    #include <SDL/SDL_image.h>
     
    #include "surfaces.h"
     
    #ifndef DEF_REFRESH
    #define DEF_REFRESH
     
        void refreshMenuScreen(SDL_Surface *screen, Image *menu);
        void refreshGameScreen(SDL_Surface *screen, Side *upperSide, Side *lowerSide, Paddle *leftPaddle, Paddle *rightPaddle, Ball *ball, Image *player1Score, Image *hyphen, Image *player2Score);
     
    #endif
    refresh.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    #include <stdlib.h>
    #include <stdio.h>
    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
     
    #include "constants.h"
    #include "refresh.h"
    #include "surfaces.h"
    #include "movement.h"
     
    void refreshMenuScreen(SDL_Surface *screen, Image *menu)
    {
        SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
     
        SDL_BlitSurface(menu->surface, NULL, screen, &(menu->position));
     
        SDL_Flip(screen);
    }
     
    void refreshGameScreen(SDL_Surface *screen, Side *upperSide, Side *lowerSide, Paddle *leftPaddle, Paddle *rightPaddle, Ball *ball, Image *player1Score, Image *hyphen, Image *player2Score)
    {
        SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
     
        SDL_BlitSurface(upperSide->surface, NULL, screen, &(upperSide->position));
        SDL_BlitSurface(lowerSide->surface, NULL, screen, &(lowerSide->position));
     
        SDL_BlitSurface(leftPaddle->surface, NULL, screen, &(leftPaddle->position));
        SDL_BlitSurface(rightPaddle->surface, NULL, screen, &(rightPaddle->position));
     
        SDL_BlitSurface(ball->surface, NULL, screen, &(ball->position));
     
        SDL_BlitSurface(player1Score->surface, NULL, screen, &(player1Score->position));
        SDL_BlitSurface(hyphen->surface, NULL, screen, &(hyphen->position));
        SDL_BlitSurface(player2Score->surface, NULL, screen, &(player2Score->position));
     
        SDL_Flip(screen);
    }
    surfaces.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    #include <SDL.h>
    #include <SDL/SDL_image.h>
     
    #ifndef DEF_SURFACES
    #define DEF_SURFACES
     
        typedef struct Side
        {
            int width;
            int height;
            SDL_Surface *surface;
            SDL_Rect position;
        } Side;
     
        typedef struct Paddle
        {
            int width;
            int height;
            SDL_Surface *surface;
            SDL_Rect position;
            int direction;
        } Paddle;
     
        typedef struct Ball
        {
            int size;
            SDL_Surface *surface;
            SDL_Rect position;
            int direction;
        } Ball;
     
        typedef struct Image
        {
            SDL_Surface *surface;
            SDL_Rect position;
        } Image;
     
        SDL_Surface *loadImage(char fileName[], SDL_Surface *icon, SDL_Surface *menuSurface, SDL_Surface *upperSideSurface, SDL_Surface *lowerSideSurface, SDL_Surface *leftPaddleSurface, SDL_Surface *rightPaddleSurface, SDL_Surface *ballSurface, SDL_Surface *player1ScoreSurface, SDL_Surface *hyphenSurface, SDL_Surface *player2ScoreSurface);
        SDL_Surface *copySurface(SDL_Surface *oldSurface);
        void exitOnError(SDL_Surface *icon, SDL_Surface *menuSurface, SDL_Surface *upperSideSurface, SDL_Surface *lowerSideSurface, SDL_Surface *leftPaddleSurface, SDL_Surface *rightPaddleSurface, SDL_Surface *ballSurface, SDL_Surface *player1ScoreSurface, SDL_Surface *hyphenSurface, SDL_Surface *player2ScoreSurface);
     
    #endif
    surfaces.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    #include <stdlib.h>
    #include <stdio.h>
    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
     
    #include "constants.h"
    #include "refresh.h"
    #include "surfaces.h"
    #include "movement.h"
     
    SDL_Surface *loadImage(char fileName[], SDL_Surface *icon, SDL_Surface *menuSurface, SDL_Surface *upperSideSurface, SDL_Surface *lowerSideSurface, SDL_Surface *leftPaddleSurface, SDL_Surface *rightPaddleSurface, SDL_Surface *ballSurface, SDL_Surface *player1ScoreSurface, SDL_Surface *hyphenSurface, SDL_Surface *player2ScoreSurface)
    {
        SDL_Surface *loadedImage = NULL;
        SDL_Surface *optimizedImage = NULL;
     
        loadedImage = IMG_Load(fileName);
        if(loadedImage == NULL)
            exitOnError(icon, menuSurface, upperSideSurface, lowerSideSurface, leftPaddleSurface, rightPaddleSurface, ballSurface, player1ScoreSurface, hyphenSurface, player2ScoreSurface);
     
        optimizedImage = SDL_DisplayFormat(loadedImage);
        SDL_FreeSurface(loadedImage);
     
        return optimizedImage;
    }
     
    SDL_Surface *copySurface(SDL_Surface *oldSurface)
    {
        SDL_Surface *newSurface = NULL;
     
        newSurface = oldSurface;
        newSurface->refcount++;
     
        return newSurface;
    }
     
    void exitOnError(SDL_Surface *icon, SDL_Surface *menuSurface, SDL_Surface *upperSideSurface, SDL_Surface *lowerSideSurface, SDL_Surface *leftPaddleSurface, SDL_Surface *rightPaddleSurface, SDL_Surface *ballSurface, SDL_Surface *player1ScoreSurface, SDL_Surface *hyphenSurface, SDL_Surface *player2ScoreSurface)
    {
        fprintf(stderr, "%s\n", SDL_GetError());
     
        if(icon != NULL)
            SDL_FreeSurface(icon);
     
        if(menuSurface != NULL)
            SDL_FreeSurface(menuSurface);
     
        if(upperSideSurface != NULL)
            SDL_FreeSurface(upperSideSurface);
     
        if(lowerSideSurface != NULL)
            SDL_FreeSurface(lowerSideSurface);
     
        if(leftPaddleSurface != NULL)
            SDL_FreeSurface(leftPaddleSurface);
     
        if(rightPaddleSurface != NULL)
            SDL_FreeSurface(rightPaddleSurface);
     
        if(ballSurface != NULL)
            SDL_FreeSurface(ballSurface);
     
        if(player1ScoreSurface != NULL)
            SDL_FreeSurface(player1ScoreSurface);
     
        if(hyphenSurface != NULL)
            SDL_FreeSurface(hyphenSurface);
     
        if(player2ScoreSurface != NULL)
            SDL_FreeSurface(player2ScoreSurface);
     
        exit(EXIT_FAILURE);
    }
    movement.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <SDL.h>
    #include <SDL/SDL_image.h>
     
    #ifndef DEF_MOVEMENT
    #define DEF_MOVEMENT
     
        void setSurfaces(Image *menu, Side *upperSide, Side *lowerSide, Image *player1Score, Image *hyphen, Image *player2Score);
        void resetGame(Side *upperSide, Paddle *leftPaddle, Paddle *rightPaddle, Ball *ball);
        void moveBall(int *pointStatus, Ball *ball, Paddle *leftPaddle, Paddle *rightPaddle);
        void hitBall(Ball *ball);
        void movePaddle(Paddle *paddle, int paddleNewDirection, Side *side);
     
    #endif
    movement.c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    #include <stdlib.h>
    #include <stdio.h>
    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
     
    #include "constants.h"
    #include "refresh.h"
    #include "surfaces.h"
    #include "movement.h"
     
    void setSurfaces(Image *menu, Side *upperSide, Side *lowerSide, Image *player1Score, Image *hyphen, Image *player2Score)
    {
        menu->position.x = 0;
        menu->position.y = 0;
     
        upperSide->position.x = (SCREEN_WIDTH - upperSide->width)/2;
        upperSide->position.y = 0;
     
        lowerSide->position.x = (SCREEN_WIDTH - lowerSide->width)/2;
        lowerSide->position.y = SCREEN_HEIGHT - lowerSide->height;
     
        player1Score->position.x = SCREEN_WIDTH/2 - 45;
        player1Score->position.y = 10;
     
        hyphen->position.x = SCREEN_WIDTH/2 - 9;
        hyphen->position.y = 10;
     
        player2Score->position.x = SCREEN_WIDTH/2 + 27;
        player2Score->position.y = 10;
    }
     
    void resetGame(Side *upperSide, Paddle *leftPaddle, Paddle *rightPaddle, Ball *ball)
    {
        leftPaddle->position.x = (SCREEN_WIDTH - upperSide->width)/2;
        leftPaddle->position.y = (SCREEN_HEIGHT - leftPaddle->height)/2;
     
        rightPaddle->position.x = (SCREEN_WIDTH + upperSide->width)/2 - rightPaddle->width;
        rightPaddle->position.y = (SCREEN_HEIGHT - rightPaddle->height)/2;
     
        ball->position.x = (SCREEN_WIDTH - ball->size)/2;
        ball->position.y = (SCREEN_HEIGHT - ball->size)/2;
        ball->direction = LEFT;
    }
     
    void moveBall(int *pointStatus, Ball *ball, Paddle *leftPaddle, Paddle *rightPaddle)
    {
        switch(ball->direction)
        {
            case LEFT:
                if(ball->position.x == 10)
                    *pointStatus = 0; // point won by Player2
                else if(ball->position.x == leftPaddle->position.x + leftPaddle->width && (ball->position.y + ball->size > leftPaddle->position.y && ball->position.y < leftPaddle->position.y + leftPaddle->height))
                    hitBall(ball); // changes ball's direction to right if ball is hit by left paddle
                else
                    ball->position.x--; // moves ball left
                break;
            case RIGHT:
                if(ball->position.x == SCREEN_WIDTH - (ball->size + 10))
                    *pointStatus = 0; // point won by Player1
                else if(ball->position.x + ball->size == rightPaddle->position.x && (ball->position.y + ball->size > rightPaddle->position.y && ball->position.y < rightPaddle->position.y + rightPaddle->height))
                    hitBall(ball); // changes ball's direction to left if ball is hit by right paddle
                else
                    ball->position.x++; // moves ball right
                break;
            default:
                break;
        }
    }
     
    void hitBall(Ball *ball)
    {
        if(ball->direction == LEFT)
        {
            ball->direction = RIGHT;
            ball->position.x++;
        }
        else
        {
            ball->direction = LEFT;
            ball->position.x--;
        }
    }
     
    void movePaddle(Paddle *paddle, int paddleDirection, Side *side)
    {
        switch(paddleDirection)
        {
            case UP:
                if(paddle->position.y > side->position.y + side->height)
                    paddle->position.y--;
                break; // moves paddle up if not already blocked by upper side
            case DOWN:
                if(paddle->position.y + paddle->height < side->position.y)
                    paddle->position.y++;
                break; // moves paddle down if not already blocked by lower side
            default:
                break;
        }
    }
    J'ai répondu à une majorité des critiques constructives qui ont été faites, mais il reste un point évoqué qui me semble toujours vague, celui concernant les fonctions qui font bouger la balle et la raquette et qui devraient prendre en compte le temps... Je ne suis pas sûr de ce qu'il faudrait faire.

    Le programme est quasiment iso-fonctionnel du point de vue end-user avec ce que j'avais dans mon 1er post, mis à part que j'ai maintenant une fonction Pause, et que le déplacement simultané et continu des 2 raquettes est possible. Cela dit, le jeu n'est toujours pas rapide du tout : la balle va à la même vitesse qu'avant, et les raquettes ne se déplacent pas très rapidement non plus.

    Sinon :

    - Ma gestion des chargements d'images et de leur libération est très mauvaise. D'ailleurs, je ne sais pas si la gestion de la libération est correcte si je déplace une image à charger. Que se passe-t-il si je copie mon pointeur de surface SDL dans une fonction, et que j'y fais le SDL_FreeSurface() ? Le pointeur initial se remet-il automatiquement à NULL ? J'ai cherché à fabriquer une liste chaînée de surfaces, mais je n'ai rien trouvé de satisfaisant...

    - J'aimerais bien trouver une explication très précise de ce qui touche au preprocessing, aux inclusions de fichiers et de bibliothèques, etc... Je n'ai jamais bien compris comment relier les différents fichiers d'un programme entre eux, et pour le moment je le fais au feeling... Qu'en est-il dans mon code actuellement ? Et notamment, où devrais-je décrire mes structures idéalement ?


    N'hésitez pas à critiquer tout ce qui peut l'être. Encore une fois, je veux d'abord me faire la main sur un maximum de choses en C et en SDL avant de rajouter trop de trucs. Je pense entre autres à tout ce qui est vérification de retours de fonctions et d'allocation mémoire. Autant prendre les bonnes habitudes maintenant.


    EDIT : Oh apparemment le programme ne lagge plus du tout quand je fais n'importe quoi avec la souris, que ce soit au niveau du menu ou du jeu : une pression sur ESCAPE ou RETURN est immédiatement traitée, çà c'est une très bonne amélioration !

  12. #12
    Membre du Club
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 58
    Points : 63
    Points
    63
    Par défaut
    Que se passe-t-il si je copie mon pointeur de surface SDL dans une fonction, et que j'y fais le SDL_FreeSurface() ? Le pointeur initial se remet-il automatiquement à NULL ?
    Non part du principe que SDL_FreeSurface laisse le pointeur intact.


    Cela dit, le jeu n'est toujours pas rapide du tout : la balle va à la même vitesse qu'avant, et les raquettes ne se déplacent pas très rapidement non plus.
    Quand on regarde ton code on s'aperçoit que les raquettes ou la balle se déplacent à une vitesse de 1 pixel / 10ms soit 100 pixels par secondes. C'est effectivement pas beaucoup. D'autant plus que c'est dans le cas idéal où ton ordinnateur affiche 100 images par seconde. En pratique tu doit être largement en-dessous.
    La solution consiste à augmenter l'amplitude de chaque déplacements (passer de 1 pixel à ...plus)

    En pratique les mouvements devrait rester fluide.

    Sinon je remplacerais le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    else
           SDL_Delay(10 - (currentTime - previousTime)); // waits enough time
    Par un simple SDL_Delay(1) à chaque itération de la boucle principale.

    Voilà voila sinon rien à redire, les événements sont effectivement beaucoup mieux géré et le code est lisible.

  13. #13
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    16
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 16
    Points : 1
    Points
    1
    Par défaut
    Hello,

    Quelqu'un aurait-il une idée simple et pratique pour gérer la désallocation de mémoire (surfaces SDL) en cas d'erreur ? Mon code est très mauvais de ce point de vue, et je ne sais pas trop quoi faire...

    A part çà, il se trouve que mon clavier réagit différemment selon les touches qu'on frappe. Lorsque j'appuie simultanément sur les 2 touches qui font bouger la raquette de gauche (R et F), je ne peux pas enclencher la pause (ESPACE). Mais je peux le faire si je fais la même manip' avec les touches correspondantes à la raquette de droite (flèches HAUT et BAS). J'avais déjà entendu dire que le clavier ne pouvait gérer 3 touches en même temps, mais apparemment ce n'est pas aussi simpliste. Quelqu'un connaît-il un lien qui décrit le comportement d'un clavier (ou peut-il m'expliquer) ?

  14. #14
    Membre expérimenté
    Avatar de coyotte507
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    1 327
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 1 327
    Points : 1 452
    Points
    1 452
    Par défaut
    Citation Envoyé par kryszcztov Voir le message
    Hello,

    Quelqu'un aurait-il une idée simple et pratique pour gérer la désallocation de mémoire (surfaces SDL) en cas d'erreur ? Mon code est très mauvais de ce point de vue, et je ne sais pas trop quoi faire...
    Salut.

    En fait, ca dépend de comment tu gères les erreurs. Si ton programme quitte, alors ca ne sert à rien de libérer les surfaces...
    Sinon, je ne vois pas quel peut être le problème, ou alors peut être que tu recharges les images? Dans ce cas tu peux libérer les pointeurs avant de les recharger.

    Coyote507

  15. #15
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    16
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 16
    Points : 1
    Points
    1
    Par défaut
    Et bien, le problème, c'est que si une image n'est pas dans le dossier où elle devrait être (par exemple), alors j'ai décidé que le programme s'arrête (en attendant mieux). Mais l'allocation de mémoire des surfaces SDL est similaire à une allocation de mémoire classique en C, ie. il faut libérer la mémoire, sinon ça fait autant de RAM en moins pour le PC par la suite jusqu'au prochain reboot. Merci de me corriger si je me trompe. Donc, je voudrais écrire une fonction qui désalloue la mémoire des surfaces SDL et qui marche pour n'importe quel chargement d'image dans le programme. Plus précisément : si à un moment donné un chargement d'image ne se passe pas bien, alors le programme s'arrête après avoir utilisé cette fonction qui doit désallouer tous les blocs de mémoire qui ont été alloués jusque là.

  16. #16
    Membre expérimenté
    Avatar de coyotte507
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    1 327
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 1 327
    Points : 1 452
    Points
    1 452
    Par défaut
    Salut,
    Je crois que toute la mémoire est désallouée à la fin du programme (c'est le cas en C++), mais après il faut demander aux experts.

    Sinon tu fais une fonction comme ca:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void cleanup(void)
    {
       //on libère toutes les surfaces
       //et on quitte éventuellement SDL
       ...
    };
    et dans ton main tu fais ça:

    Alors, si jamais tu utilises la commande exit(), la fonction cleanup sera automatiquement appelée. Sinon, dans le main, il faudra aussi l'appeler avant le return 0 de fin.

  17. #17
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Citation Envoyé par coyotte507 Voir le message
    Salut,
    Je crois que toute la mémoire est désallouée à la fin du programme (c'est le cas en C++), mais après il faut demander aux experts.
    Pareil pour le C et c'est encore heureux sinon le système d'exploitation tournerait 5 minutes avant de n'avoir plus de RAM...

    Par contre, c'est bon usage de faire la désallocation soi-même et être sûr qu'il n'y a pas de fuite de mémoire.

    Jc

  18. #18
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    16
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 16
    Points : 1
    Points
    1
    Par défaut
    Justement, à propos de fuite de mémoire et de bonnes habitudes à prendre, je m'offre un joyeux interlude dans mon dev pour tenter de gérer de manière propre la mémoire allouée dynamiquement par le biais des IMG_Load()...

    J'ai lu quelques trucs sur le forum, et j'ai vu qu'un des modos avait écrit un gestionnaire de mémoire qui peut apparemment servir pour n'importe quel projet. Je me suis dit que je devrais essayer de m'en écrire un moi-même, du moins ce qu'il faut pour ce projet en particulier. Je ne vais pas y aller par 4 chemins : c'est pas un truc évident pour moi.

    Alors voilà, j'ai commencé à écrire quelques fonctions qui pourraient m'être utiles par la suite, je voudrais savoir si j'ai fait des erreurs, ou carrément si je fais fausse route. Les voici :


    Une fonction pour désallouer une surface SDL, sachant qu'apparemment il faut mettre à NULL chaque pointeur après le SDL_FreeSurface() :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /* corps de la fonction */
    SDL_Surface *freeSurface(SDL_Surface *oldSurface)
    {
        SDL_FreeSurface(oldSurface);
        oldSurface = NULL;
     
        return oldSurface;
    }
     
    /* appel de la fonction */
    surfaceToBeDeleted = freeSurface(surfaceToBeDeleted);
    Ca paraît un peu idiot de retourner le pointeur passé en paramètre, mais je n'ai pas trouvé mieux. Si je ne fais pas de return et que je passe ma fonction en void, alors mon pointeur ne passe pas à NULL, n'est-ce pas ??

    Là j'utilise un "élement de liste chaînée", élément composé d'un pointeur vers une surface SDL et d'un pointeur-suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    typedef struct SurfaceChainElement
        {
            SDL_Surface *surface;
            struct SurfaceChainElement *next;
        } SurfaceChainElement;
    J'utilise un pointeur first et un pointeur last, typiques pour une liste chaînée.

    Une fonction pour ajouter un élément à la fin de la liste :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /* corps de la fonction */
    SurfaceChainElement *addSurfaceChainElement(SurfaceChainElement *newElement, SurfaceChainElement *lastElement)
    {
        lastElement->next = newElement;
        lastElement = lastElement->next;
     
        return lastElement;
    }
     
    /* appel de la fonction */
    lastElement = addSurfaceChainElement(elementToBeAdded, lastElement);
    Là encore, il me semble qu'il faut un return pour le last, sinon celui-ci n'est pas mis à jour.

    Une fonction pour enlever un élément, et par la même occasion libérer la mémoire de l'élément ainsi enlevé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /* corps de la fonction */
    SurfaceChainElement *deleteSurfaceChainElement(SurfaceChainElement *oldElement, SurfaceChainElement *elementBrowser)
    {
        while(elementBrowser->next != oldElement)
            elementBrowser = elementBrowser->next;
        oldElement->surface = freeSurface(oldElement->surface);
        oldElement = elementBrowser;
     
        return oldElement;
    }
     
    /* appel de la fonction */
    lastElement = deleteSurfaceChainElement(elementToBeDeleted, firstElement);
    Même remarque à propos du return...


    Alors voilà, s'il y a quelque chose qui choque, ou qui cloche dans mon approche, ou si je me complique la vie... J'espère ne pas être trop à côté de la plaque, même si les pointeurs et les listes chaînées, ce n'est pas (encore) les doigts dans le nez.

  19. #19
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    16
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 16
    Points : 1
    Points
    1
    Par défaut
    J'ai continué ma pensée sur ce gestionnaire de mémoire dynamique, et j'ai trouvé plus pratique de me faire une mini phase de conception technique, que j'ai expliqué dans les 2 fichiers PDF (1 page chacun) en pièces jointes de ce post.

    Pour commenter un minimum :
    - Les structures peuvent être de différents types (Paddle, Side, Score, etc...), mais ont tous un champ de type "pointeur sur SDL_Surface".
    - La gestion de mémoire s'articule autour d'une liste chaînée composée de structures à deux pointeurs, le premier sur le pointeur de SDL_Surface cité ci-dessus, le second sur l'élément de la chaîne suivant.
    - Le premier PDF détaille l'allocation de mémoire et l'ajout d'un élément, tandis que le second explique la libération de mémoire et le retrait du dernier élément.
    - Le haut de chaque PDF montre l'état initial, le bas l'état final. Les chiffres en rouge décrivent l'ordre des étapes.

    Le but de tout ceci est de pouvoir libérer toutes les surfaces SDL allouées dynamiquement depuis le début du run, et ceci à tout moment de l'exécution du programme. Je pense en particulier à l'échec de chargement d'une image, qui provoquerait (dans un premier temps du dev) l'arrêt immédiat du programme : il faut pouvoir libérer la mémoire à ce moment-là avec un appel de fonction. Je ne sais pas encore trop comment m'y prendre, mais si quelqu'un a une idée concrète, je suis preneur.

    Je n'ai pas encore implémenté ceci dans mon code, donc je ne sais pas si ça peut faire l'affaire. J'ai juste vérifié qu'on peut bien créer un pointeur sur un autre pointeur ainsi qu'un pointeur sur un champ d'une structure en C (je combine les 2 concepts ici !). Est-ce propre ? N'y a-t-il pas plus simple ?

    Merci d'avance pour toute réponse.
    Images attachées Images attachées

  20. #20
    Membre expérimenté
    Avatar de coyotte507
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    1 327
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 1 327
    Points : 1 452
    Points
    1 452
    Par défaut
    Citation Envoyé par coyotte507 Voir le message
    Salut,
    Je crois que toute la mémoire est désallouée à la fin du programme (c'est le cas en C++), mais après il faut demander aux experts.

    Sinon tu fais une fonction comme ca:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void cleanup(void)
    {
       //on libère toutes les surfaces
       //et on quitte éventuellement SDL
       ...
    };
    et dans ton main tu fais ça:

    Alors, si jamais tu utilises la commande exit(), la fonction cleanup sera automatiquement appelée. Sinon, dans le main, il faudra aussi l'appeler avant le return 0 de fin.
    Citation Envoyé par kryszcztov Voir le message
    Je pense en particulier à l'échec de chargement d'une image, qui provoquerait (dans un premier temps du dev) l'arrêt immédiat du programme : il faut pouvoir libérer la mémoire à ce moment-là avec un appel de fonction. Je ne sais pas encore trop comment m'y prendre, mais si quelqu'un a une idée concrète, je suis preneur.

    Merci d'avance pour toute réponse.
    La création d'une fonction cleanup(), et de faire atexit(cleanup) ne te convient pas?

Discussions similaires

  1. [ affichage contacts ] Mon premier projet GWT
    Par hocinema dans le forum GWT et Vaadin
    Réponses: 5
    Dernier message: 26/08/2007, 21h15
  2. premier post, premier projet
    Par Cheorches dans le forum VB.NET
    Réponses: 9
    Dernier message: 27/07/2007, 17h27

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo