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 :
constants.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
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; }
game.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.c :
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
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
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.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 #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
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.
Partager