Maintenant le fichier sdl_application.cpp :
1
2
3
|
#include "sdl_application.h"
#include <assert.h>
using namespace std;
|
Pourquoi on ne met pas le #include dans le .h ?
On pourrait, mais cette une convention. Si vous voyez un autre include que en plus de celui attendu dans un .cpp (ou un .c) c'est que l'on a besoin de ses fonctions que dans le .cpp et pas de le .h, quand on a pas besoin de variables particulières. C'est pour cette raison que vous verrez souvent un #include dans un .cpp et pas dans un .h.
Nous aurons aussi besoin de cas deux constantes :
1
2
|
const float DTFACTOR = 0.01;
const unsigned int TIME_MAX = 10;
|
Ces deux constantes se révéleront utile dans quelques lignes. DTFACTOR permet de ne pas avoir un dt trop grand (car getTick donne la durée en millisecondes), et TIME_MAX définit le temps qu'il faut pour faire un cycle complet du programme (calcul, affichage), cela permet de ne pas faire de cycle pour rien, vous pouvez changer la valeur de cette variable pour voir ce que ça fait.
Les fonctions publiques
Le constructeur le plus simple :
1
2
3
4
5
|
SDL_Application::SDL_Application(unsigned int width, unsigned int height){
this->initialisation_SDL(width, height);
Uint32 color = 0;
this->initialisation("SDL_Application", NULL, color, "", 0);
}
|
ATTENTION : il ne faut pas mettre les paramètres par défaut ils ne doivent apparaître que dans le .h
1
2
3
4
5
|
SDL_Application::SDL_Application(unsigned int width, unsigned int height, const std::string & titre, const std::string & icon){
this->initialisation_SDL(width, height);
Uint32 color = 0;
this->initialisation(titre, icon, color, "", 0);
}
|
Ici on appel la fonction initialisation_SDL pour créer la fenêtre et la fonction initialisation pour initialiser ce qui ne l'est pas encore. On complète les paramètres que l'on a pas par des paramètres par défaut.
Encore un constructeur :
1
2
3
4
|
SDL_Application::SDL_Application(unsigned int width, unsigned int height, const std::string & titre, const std::string & icon, const std::string & backgroundFile, const std::string & police, int taille){
this->initialisation_SDL(width, height);
this->initialisation(titre, icon, backgroundFile, police, taille);
}
|
Maintenant on a tout ce qu'il faut. Mais que pour avoir un fichier image pour le fond d'écran.
On fait les deux autres constructeurs :
1
2
3
4
5
6
7
8
9
|
SDL_Application::SDL_Application(unsigned int width, unsigned int height, const std::string & titre, const std::string & icon, SDL_Surface* background, const std::string & police, int taille){
this->initialisation_SDL(width, height);
this->initialisation(titre, icon, background, police, taille);
}
SDL_Application::SDL_Application(unsigned int width, unsigned int height, const std::string & titre, const std::string & icon, Uint32 backgroundColor, std::string police, int taille){
this->initialisation_SDL(width, height);
this->initialisation(titre, icon, backgroundColor, police, taille);
}
|
Encore le constructeur de copie et on a fini les constructeurs :
1
2
3
|
SDL_Application::SDL_Application(const SDL_Application & app){
this->copySDL_Application(app);
}
|
Ouf, maintenant le destructeur :
1
2
3
4
5
6
7
8
9
10
11
12
13
|
SDL_Application::~SDL_Application(){
if(this->icon != NULL && isMyIcon){
SDL_FreeSurface(this->icon);
}
if(this->background != NULL && isMyBackground){
SDL_FreeSurface(this->background);
}
if(this->font != NULL && isMyFont){
TTF_CloseFont(this->font);
}
TTF_Quit();
SDL_Quit();
}
|
Voilà l'utilité de tout ces booléens que je vous ai fait rajouter dans le .h. Il faut être sur de ne pas libérer une SDL_Surface* qui pourrait être utilisée après, sinon vous allez voir votre programme s'arrêter d'un coup et il sera écrit dans la console segmentation fault, en gros vous avez voulu lire dans la mémoire à un endroit où vous n'avez pas le droit (comme quand on essai d'effacer un pointeur NULL).
La fonction tant attendue et tant évoquées (et réciproquement) :
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
|
int SDL_Application::executer(){
SDL_Event event;
if(this->ecran != NULL){
if(this->ecran->w > 0 && this->ecran->h > 0){
SDL_Rect pos;
pos.x = 0;
pos.y = 0;
this->run = true;
this->initVar();
while(this->run){
this->start_time = SDL_GetTicks(); while(SDL_PollEvent(&event)){ if(event.key.keysym.sym == SDLK_ESCAPE || event.type == SDL_QUIT){this->run = false;}
this->getEvent(&event);
}
if(this->background != NULL){
SDL_BlitSurface(this->background, NULL, this->ecran, &pos); }else{
SDL_FillRect(ecran, NULL, this->backgroundColor); }
this->current_time = SDL_GetTicks(); this->ellapsed_time = this->current_time - this->last_time; this->last_time = this->current_time;
this->update(this->ellapsed_time); this->draw(ecran);
SDL_Flip(ecran); this->ellapsed_time = SDL_GetTicks() - this->start_time;
if(this->ellapsed_time < TIME_MAX){SDL_Delay(TIME_MAX - this->ellapsed_time);}
}
return 0;
}else{return 1;}
}else{return 1;}
}
|
Pourquoi on ne met pas les deux conditions dans un seul if ?
Car si l'écran est NULL vous vous retrouvé à demander la longueur et la largeur d'un pointeur NULL (et ça aussi c'est mal).
Faisons tout cela dans l'ordre :
- on créé un SDL_Event : pour gérer les événements
- on vérifie que l'écran n'est pas NULL
- sinon on arrête et on retourne 1 (erreur)
- on regarde si l'écran à une taille correcte
- sinon on arrête et on retourne 1 (erreur)
- On définit la position du fond d'écran
- on met run à true (pour que l'application s'exécute)
- On appel la fonction initVar (si l'utilisateur veut initialiser de choses a ce moment là)
- Tan que run == true on boucle (l'application s'exécute)
- On se rappel de l'heure au début de la boucle
- tant qu'il y a un événement on le gère (ce qui permet de gérer plusieurs événements simultanément (enfin presque)
- Si le fond d'écran n'est pas NULL on l'affiche
- Sinon on met un couleur à sa place (noir par défaut)
- On regarde à nouveau l'heure
- on calcul le temps mis
- le temps courant devient le temps passé
- calcul , mise à jour des variables
- affichage
- on regarde combien de temps on à mis
- Si on a mis moins de temps que prévu (c'est préférable), on attend ce qu'il faut pour ne pas aller trop vite
- on recommence
Les fonctions protected :
Toutes les fonctions d'initialisation :
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
|
void SDL_Application::setTitle(const std::string & titre){
SDL_WM_SetCaption(titre.c_str(), NULL);
}
void SDL_Application::setIcon(SDL_Surface* icon){
this->isMyIcon = false;
this->icon = icon;
if(this->icon != NULL) SDL_WM_SetIcon(this->icon, NULL);
}
void SDL_Application::setIcon(const std::string & iconFile){
this->icon = IMG_Load(iconFile.c_str());
if(this->icon != NULL) SDL_WM_SetIcon(this->icon, NULL);
this->isMyIcon = true;
}
void SDL_Application::setBackground(SDL_Surface* background){
this->background = background;
this->isMyBackground = false;
}
void SDL_Application::setBackground(const std::string & backgrounsFile){
this->isMyBackground = true;
this->background = IMG_Load(backgrounsFile.c_str());
}
void SDL_Application::setBackgroundColor(Uint32 color){
this->backgroundColor = color;
}
void SDL_Application::setBackgroundColor(Uint8 r, Uint8 g, Uint8 b){
if(this->ecran != NULL){
this->backgroundColor = SDL_MapRGB(ecran->format, r, g, b);
}
}
void SDL_Application::setFont(const std::string & police, int taille){
this->font = TTF_OpenFont(police.c_str(), taille);
this->isMyFont = true;
}
void SDL_Application::setFont(TTF_Font* font){
this->font = font;
this->isMyFont = false;
}
|
Elles sont assez simple à comprendre, et je vous en ai déjà parlé.
Maintenant l'opérateur = :
1
2
3
4
|
SDL_Application & SDL_Application::operator = (const SDL_Application & app){
this->copySDL_Application(app);
return *this;
}
|
Mais pourquoi on veut à tout prix redéfinir l'opérateur = alors que de toute façon on n'aura pas le droit de s'en servir ?
C'est justement pour cela qu'on le redéfinit c'est pour interdire son utilisation.
Et où est-elle cette interdiction ?
Elle sera dans copySDL_Application.
Les fonctions protégées :
1
2
|
void SDL_Application::initVar(){}
void SDL_Application::stop(){this->run = false;}
|
Vous reconnaissez ici la fonction que l'on a implémenté (vide mais quand même) pour le futur utilisateur de la classe (vous en fait, pour le moment). Et la fonction d'arrêt de l'application.
Ajout et réalisation d'un événement :
1
2
3
4
5
6
7
8
|
void SDL_Application::addKeyEvent(const std::string action, SDLKey key, bool initValue){
this->keyconf[action] = key;
this->keystates[this->keyconf[action]] = initValue;
}
bool SDL_Application::keyEventMade(const std::string action){
return this->keystates[this->keyconf[action]];
}
|
Toutes les fonctions d'écriture :
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
|
void SDL_Application::drawText_Solid(int x, int y, const std::string & str, SDL_Color color){
SDL_Rect pos;
pos.x = x;
pos.y = y;
if(this->font != NULL){
SDL_Surface* texte = TTF_RenderText_Solid(this->font, str.c_str(), color);
SDL_BlitSurface(texte, 0, this->ecran, &pos); SDL_FreeSurface(texte); }
}
void SDL_Application::drawText_Shaded(int x, int y, const std::string & str, SDL_Color color, SDL_Color fond){
SDL_Rect pos;
pos.x = x;
pos.y = y;
if(this->font != NULL){
SDL_Surface* texte = TTF_RenderText_Shaded(this->font, str.c_str(), color, fond);
SDL_BlitSurface(texte, 0, this->ecran, &pos); SDL_FreeSurface(texte); }
}
void SDL_Application::drawText_Blended(int x, int y, const std::string & str, SDL_Color color){
SDL_Rect pos;
pos.x = x;
pos.y = y;
if(this->font != NULL){
SDL_Surface* texte = TTF_RenderText_Blended(this->font, str.c_str(), color);
SDL_BlitSurface(texte, 0, this->ecran, &pos); SDL_FreeSurface(texte); }
}
|
ATTENTION : il ne faut encore pas oublier SDL_FreeSurface.
Les fonctions privées :
Aller, les fonctions privées et on a finit :
Les trois fonctions d'initialisation :
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
|
void SDL_Application::initialisation(const std::string & titre, const std::string & icon, const std::string & backgroundFile, const std::string & police, int taille){
this->backgroundColor = 0;
this->setTitle(titre);
this->setIcon(icon);
this->setBackground(backgroundFile);
this->setFont(police, taille);
}
void SDL_Application::initialisation(const std::string & titre, const std::string & icon, SDL_Surface* background, const std::string & police, int taille){
this->backgroundColor = 0;
this->setTitle(titre);
this->setIcon(icon);
this->setBackground(background);
this->setFont(police, taille);
}
void SDL_Application::initialisation(const std::string & titre, const std::string & icon, Uint32 backgroundColor, const std::string & police, int taille){
this->background = NULL;
this->setTitle(titre);
this->setIcon(icon);
this->setFont(police, taille);
}
|
La fonction qui initialise SDL :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
void SDL_Application::initialisation_SDL(unsigned int width, unsigned int height){
static bool initSDL = false;
if(!initSDL){
time_t t = time(NULL);
srand(t); SDL_Init(SDL_INIT_VIDEO); this->ecran = SDL_SetVideoMode(width, height, 32,SDL_HWSURFACE | SDL_DOUBLEBUF);
cout << "init SDL_INIT_VIDEO" << endl;
TTF_Init(); cout << "init SDL_ttf" << endl;
initSDL = true;
this->lastTick = 0;
this->font = NULL;
this->background = NULL;
this->backgroundColor = 0;
}
}
|
- On créé un booléen initSDL (static veut dire qu'on le définira qu'une fois, même si on rappel la fonction)
- Si initSDL est false on entre dans le if
- On regarde l'heure
- On initialise une variable aléatoire (ça peut toujours servir dans un jeu)
- On initialise SDL (vous vous souvenez)
- On initialise le mode vidéo de SDL avec l'écran
- On initialise SDL_ttf
- On met initSDL à true
- On met le dernier tick à 0
- On met font a NULL (par défaut un pointeur vaut n'importe quoi, mais pas NULL)
- On met l'image de fond à NULL
- On met la couleur de fond à 0 (noir)
On empêche la copie de l'application :
1
2
3
|
void SDL_Application::copySDL_Application(const SDL_Application & app){
assert(false);
}
|
C'EST TOUT !!!?
Ben, oui, ça suffit.
En fait assert est une fonction qui stoppe l'exécution d'un programme si ce qu'on lui passe en paramètre est false. L'avantage c'est que cette méthode disparaît quand on compile de manière optimisée, et c'est très utile quand on développe, bien sur ça ne fait pas tout.
Bon, si vous voulez que je vous dégoutte encore un peut, sachez que vous pouvez réglé le problème de la copie non désirée en mettant le constructeur de copie de la classe en private, et là c'est le compilateur qui râlera, et pas le programme.
Mais il est bon de connaître les deux méthodes.
Ensuite la fonction qui récupère les touches que l'on a demander :
1
2
3
4
5
6
7
8
9
10
|
void SDL_Application::getEvent(SDL_Event* event){
for (KeyStates::iterator it = this->keystates.begin();it != this->keystates.end();it++){
if (event->key.keysym.sym == it->first){
it->second = (event->motion.type == SDL_KEYDOWN); break;
}
}
}
|
Et enfin la fonction qui met a jour les ticks :
1
2
3
4
5
6
|
float SDL_Application::updateTicks(){
Uint32 tick = SDL_GetTicks();
float dt = ((float)(tick - this->lastTick));
this->lastTick = tick;
return dt*DTFACTOR;
}
|
Et voilà. Ça fait beaucoup de choses d'un coup je l'admet. Aussi, je vous recommande de commenter votre code source (si vous l'avez recopier), c'est un bon exercice et c'est une bonne habitude à prendre. De ce point de vue là tout le monde est à la même enseigne, on est tous étrange a notre programme si on le laisse de côté pendant deux semaines, et plus tout à fait dans le coup quand on le laisse pendant une semaine.
Pour résumer, ça donne ça :
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
|
#include "sdl_application.h"
#include <assert.h>
using namespace std;
const float DTFACTOR = 0.01; const unsigned int TIME_MAX = 10;
SDL_Application::SDL_Application(unsigned int width, unsigned int height){
this->initialisation_SDL(width, height);
Uint32 color = 0;
this->initialisation("SDL_Application", NULL, color, "", 0);
}
SDL_Application::SDL_Application(unsigned int width, unsigned int height, const std::string & titre, std::string icon){
this->initialisation_SDL(width, height);
Uint32 color = 0;
this->initialisation(titre, icon, color, "", 0);
}
SDL_Application::SDL_Application(unsigned int width, unsigned int height, const std::string & titre, const std::string & icon, const std::string & backgroundFile, const std::string & police, int taille){
this->initialisation_SDL(width, height);
this->initialisation(titre, icon, backgroundFile, police, taille);
}
SDL_Application::SDL_Application(unsigned int width, unsigned int height, const std::string & titre, const std::string & icon, SDL_Surface* background, const std::string & police, int taille){
this->initialisation_SDL(width, height);
this->initialisation(titre, icon, background, police, taille);
}
SDL_Application::SDL_Application(unsigned int width, unsigned int height, const std::string & titre, const std::string & icon, Uint32 backgroundColor, const std::string & police, int taille){
this->initialisation_SDL(width, height);
this->initialisation(titre, icon, backgroundColor, police, taille);
}
SDL_Application::SDL_Application(const SDL_Application & app){
this->copySDL_Application(app);
}
SDL_Application::~SDL_Application(){
if(this->icon != NULL && isMyIcon){
SDL_FreeSurface(this->icon);
}
if(this->background != NULL && isMyBackground){
SDL_FreeSurface(this->background);
}
if(this->font != NULL && isMyFont){
TTF_CloseFont(this->font);
}
TTF_Quit();
SDL_Quit();
}
int SDL_Application::executer(){ SDL_Event event;
if(this->ecran != NULL){
if(this->ecran->w > 0 && this->ecran->h > 0){
SDL_Rect pos;
pos.x = 0;
pos.y = 0;
this->run = true;
this->initVar();
while(this->run){
this->start_time = SDL_GetTicks();
while(SDL_PollEvent(&event)){
if(event.key.keysym.sym == SDLK_ESCAPE || event.type == SDL_QUIT){this->run = false;}
this->getEvent(&event);
}
if(this->background != NULL){
SDL_BlitSurface(this->background, NULL, this->ecran, &pos); }else{
SDL_FillRect(ecran, NULL, this->backgroundColor); }
this->current_time = SDL_GetTicks();
this->ellapsed_time = this->current_time - this->last_time;
this->last_time = this->current_time;
this->update(this->ellapsed_time); this->draw(ecran); SDL_Flip(ecran);
this->ellapsed_time = SDL_GetTicks() - this->start_time;
if(this->ellapsed_time < TIME_MAX){SDL_Delay(TIME_MAX - this->ellapsed_time);}
}
return 0;
}else{return 1;}
}else{return 1;}
}
void SDL_Application::setTitle(const std::string & titre){ SDL_WM_SetCaption(titre.c_str(), NULL);
}
void SDL_Application::setIcon(SDL_Surface* icon){ this->isMyIcon = false;
this->icon = icon;
if(this->icon != NULL) SDL_WM_SetIcon(this->icon, NULL);
}
void SDL_Application::setIcon(const std::string & iconFile){ this->icon = IMG_Load(iconFile.c_str());
if(this->icon != NULL) SDL_WM_SetIcon(this->icon, NULL);
this->isMyIcon = true;
}
void SDL_Application::setBackground(SDL_Surface* background){ this->background = background;
this->isMyBackground = false;
}
void SDL_Application::setBackground(const std::string & backgrounsFile){ this->isMyBackground = true;
this->background = IMG_Load(backgrounsFile.c_str());
}
void SDL_Application::setBackgroundColor(Uint32 color){ this->backgroundColor = color;
}
void SDL_Application::setBackgroundColor(Uint8 r, Uint8 g, Uint8 b){ if(this->ecran != NULL){
this->backgroundColor = SDL_MapRGB(ecran->format, r, g, b);
}
}
void SDL_Application::setFont(const std::string & police, int taille){ this->font = TTF_OpenFont(police.c_str(), taille);
this->isMyFont = true;
}
void SDL_Application::setFont(TTF_Font* font){ this->font = font;
this->isMyFont = false;
}
SDL_Application & SDL_Application::operator = (const SDL_Application & app){
this->copySDL_Application(app);
return *this;
}
void SDL_Application::initVar(){}
void SDL_Application::stop(){this->run = false;}
void SDL_Application::addKeyEvent(const std::string action, SDLKey key, bool initValue){
this->keyconf[action] = key;
this->keystates[this->keyconf[action]] = initValue;
}
bool SDL_Application::keyEventMade(const std::string action){
return this->keystates[this->keyconf[action]];
}
void SDL_Application::drawText_Solid(int x, int y, const std::string & str, SDL_Color color){
SDL_Rect pos;
pos.x = x;
pos.y = y;
if(this->font != NULL){ SDL_Surface* texte = TTF_RenderText_Solid(this->font, str.c_str(), color);
SDL_BlitSurface(texte, 0, this->ecran, &pos); SDL_FreeSurface(texte); }
}
void SDL_Application::drawText_Shaded(int x, int y, const std::string & str, SDL_Color color, SDL_Color fond){
SDL_Rect pos;
pos.x = x;
pos.y = y;
if(this->font != NULL){ SDL_Surface* texte = TTF_RenderText_Shaded(this->font, str.c_str(), color, fond);
SDL_BlitSurface(texte, 0, this->ecran, &pos); SDL_FreeSurface(texte); }
}
void SDL_Application::drawText_Blended(int x, int y, const std::string & str, SDL_Color color){
SDL_Rect pos;
pos.x = x;
pos.y = y;
if(this->font != NULL){ SDL_Surface* texte = TTF_RenderText_Blended(this->font, str.c_str(), color);
SDL_BlitSurface(texte, 0, this->ecran, &pos); SDL_FreeSurface(texte); }
}
void SDL_Application::initialisation(const std::string & titre, const std::string & icon, const std::string & backgroundFile, const std::string & police, int taille){
this->backgroundColor = 0;
this->setTitle(titre);
this->setIcon(icon);
this->setBackground(backgroundFile);
this->setFont(police, taille);
}
void SDL_Application::initialisation(const std::string & titre, const std::string & icon, SDL_Surface* background, const std::string & police, int taille){
this->backgroundColor = 0;
this->setTitle(titre);
this->setIcon(icon);
this->setBackground(background);
this->setFont(police, taille);
}
void SDL_Application::initialisation(const std::string & titre, const std::string & icon, Uint32 backgroundColor, const std::string & police, int taille){
this->background = NULL;
this->setTitle(titre);
this->setIcon(icon);
this->setFont(police, taille);
}
void SDL_Application::initialisation_SDL(unsigned int width, unsigned int height){
static bool initSDL = false;
if(!initSDL){
time_t t = time(NULL);
srand(t); SDL_Init(SDL_INIT_VIDEO); this->ecran = SDL_SetVideoMode(width, height, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
cout << "init SDL_INIT_VIDEO" << endl;
TTF_Init(); cout << "init SDL_ttf" << endl;
initSDL = true;
this->lastTick = 0;
this->font = NULL;
this->background = NULL;
this->backgroundColor = 0;
}
}
void SDL_Application::copySDL_Application(const SDL_Application & app){
assert(false);
}
void SDL_Application::getEvent(SDL_Event* event){
for (KeyStates::iterator it = this->keystates.begin();it != this->keystates.end();it++){
if (event->key.keysym.sym == it->first){
it->second = (event->motion.type == SDL_KEYDOWN); break; }
}
}
float SDL_Application::updateTicks(){
Uint32 tick = SDL_GetTicks();
float dt = ((float)(tick - this->lastTick));
this->lastTick = tick;
return dt*DTFACTOR;
}
|
Ouf, ça, c'est fait.
|