106a40e63df472eda5051ffe8f7779fd93eb8b65
[pong.git] / game.cpp
1 /*This source code copyrighted by Lazy Foo' Productions (2004-2020)
2
3 and may not be redistributed without written permission.*/
4
5 //Using SDL, SDL_image, standard IO, vectors, and strings
6 #include <SDL2/SDL.h>
7 #include <SDL2/SDL_image.h>
8 #include <stdio.h>
9 #include <string>
10
11 //Screen dimension constants
12 const int SCREEN_WIDTH = 640;
13 const int SCREEN_HEIGHT = 480;
14
15 //Texture wrapper class
16 class LTexture
17 {
18         public:
19                 //Initializes variables
20                 LTexture();
21
22                 //Deallocates memory
23                 ~LTexture();
24
25                 //Loads image at specified path
26                 bool loadFromFile( std::string path );
27                 
28                 #if defined(_SDL_TTF_H) || defined(SDL_TTF_H)
29                 //Creates image from font string
30                 bool loadFromRenderedText( std::string textureText, SDL_Color textColor );
31                 #endif
32
33                 //Deallocates texture
34                 void free();
35
36                 //Set color modulation
37                 void setColor( Uint8 red, Uint8 green, Uint8 blue );
38
39                 //Set blending
40                 void setBlendMode( SDL_BlendMode blending );
41
42                 //Set alpha modulation
43                 void setAlpha( Uint8 alpha );
44                 
45                 //Renders texture at given point
46                 void render( int x, int y, SDL_Rect* clip = NULL, double angle = 0.0, SDL_Point* center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE );
47                 //Gets image dimensions
48                 int getWidth();
49                 int getHeight();
50
51         private:
52                 //The actual hardware texture
53                 SDL_Texture* mTexture;
54
55                 //Image dimensions
56                 int mWidth;
57                 int mHeight;
58 };
59
60 //A circle stucture
61 struct Circle
62 {
63   int x, y;
64   int r;
65 };
66
67 //The dot that will move around on the screen
68 class Dot
69 {
70     public:
71
72                 //Maximum axis velocity of the dot
73                 static const int DOT_VEL = 10;
74
75                 //Initializes the variables
76     Dot(Uint16 x, Uint16 y, int8_t velX, int8_t velY, Uint8 w, Uint8 h);
77
78                 //Moves the dot
79                 void move( SDL_Rect& paddle1 );
80
81                 //Shows the dot on the screen
82                 void render();
83
84                 Circle& getCollider();
85
86     private:
87                 //The X and Y offsets of the dot
88                 int mPosX, mPosY;
89
90                 //The velocity of the dot
91                 int mVelX, mVelY;
92                 //The dimensions of the dot
93                 
94           int mWidth;
95           int mHeight;
96
97           Circle mCollider;
98
99           void shiftColliders();
100 };
101
102 class Paddle
103 {
104     public:
105
106                 //Maximum axis velocity of the dot
107                 static const Uint8 PADDLE_VEL = 10;
108
109                 //Initializes the variables
110     Paddle(Uint16 x, Uint16 y, Uint8 velX, Uint8 velY, Uint8 w, Uint8 h);
111
112                 //Takes key presses and adjusts the dot's velocity
113                 void handleEvent( SDL_Event& e );
114
115                 void move();
116                 void render();
117
118                 SDL_Rect& getCollider();
119
120     private:
121     
122           SDL_Rect p;
123           
124           int8_t mVelX;
125           int8_t mVelY;
126 };
127 //Starts up SDL and creates window
128 bool init();
129
130 //Loads media
131 bool loadMedia();
132
133 //Frees media and shuts down SDL
134 void close();
135
136 //The window we'll be rendering to
137 SDL_Window* gWindow = NULL;
138
139 //The window renderer
140 SDL_Renderer* gRenderer = NULL;
141
142 //Circle/Box collision detector
143 bool checkCollision( Circle& a, SDL_Rect& b );
144
145 //Scene textures
146 LTexture gDotTexture;
147
148 LTexture::LTexture()
149 {
150         //Initialize
151         mTexture = NULL;
152         mWidth = 0;
153         mHeight = 0;
154 }
155
156 LTexture::~LTexture()
157 {
158         //Deallocate
159         free();
160 }
161
162 bool LTexture::loadFromFile( std::string path )
163 {
164         //Get rid of preexisting texture
165         free();
166
167         //The final texture
168         SDL_Texture* newTexture = NULL;
169
170         //Load image at specified path
171         SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
172         if( loadedSurface == NULL )
173         {
174                 printf( "Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError() );
175         }
176         else
177         {
178                 //Color key image
179                 SDL_SetColorKey( loadedSurface, SDL_TRUE, SDL_MapRGB( loadedSurface->format, 0, 0xFF, 0xFF ) );
180
181                 //Create texture from surface pixels
182         newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
183                 if( newTexture == NULL )
184                 {
185                         printf( "Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
186                 }
187                 else
188                 {
189                         //Get image dimensions
190                         mWidth = loadedSurface->w;
191                         mHeight = loadedSurface->h;
192                 }
193
194                 //Get rid of old loaded surface
195                 SDL_FreeSurface( loadedSurface );
196         }
197
198         //Return success
199         mTexture = newTexture;
200         return mTexture != NULL;
201 }
202
203 #if defined(_SDL_TTF_H) || defined(SDL_TTF_H)
204 bool LTexture::loadFromRenderedText( std::string textureText, SDL_Color textColor )
205 {
206         //Get rid of preexisting texture
207         free();
208
209         //Render text surface
210         SDL_Surface* textSurface = TTF_RenderText_Solid( gFont, textureText.c_str(), textColor );
211         if( textSurface != NULL )
212         {
213                 //Create texture from surface pixels
214         mTexture = SDL_CreateTextureFromSurface( gRenderer, textSurface );
215                 if( mTexture == NULL )
216                 {
217                         printf( "Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError() );
218                 }
219                 else
220                 {
221                         //Get image dimensions
222                         mWidth = textSurface->w;
223                         mHeight = textSurface->h;
224                 }
225
226                 //Get rid of old surface
227                 SDL_FreeSurface( textSurface );
228         }
229         else
230         {
231                 printf( "Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError() );
232         }
233
234         
235         //Return success
236         return mTexture != NULL;
237 }
238 #endif
239
240 void LTexture::free()
241 {
242         //Free texture if it exists
243         if( mTexture != NULL )
244         {
245                 SDL_DestroyTexture( mTexture );
246                 mTexture = NULL;
247                 mWidth = 0;
248                 mHeight = 0;
249         }
250 }
251
252 void LTexture::setColor( Uint8 red, Uint8 green, Uint8 blue )
253 {
254         //Modulate texture rgb
255         if(SDL_SetTextureColorMod( mTexture, red, green, blue ) < 0)
256         {
257           printf("Error setting dolor for texture: %s", SDL_GetError());
258         }
259 }
260
261 void LTexture::setBlendMode( SDL_BlendMode blending )
262 {
263         //Set blending function
264         SDL_SetTextureBlendMode( mTexture, blending );
265 }
266                 
267 void LTexture::setAlpha( Uint8 alpha )
268 {
269         //Modulate texture alpha
270         SDL_SetTextureAlphaMod( mTexture, alpha );
271 }
272
273 void LTexture::render( int x, int y, SDL_Rect* clip, double angle, SDL_Point* center, SDL_RendererFlip flip )
274 {
275         //Set rendering space and render to screen
276         SDL_Rect renderQuad = { x, y, mWidth, mHeight };
277
278         //Set clip rendering dimensions
279         if( clip != NULL )
280         {
281                 renderQuad.w = clip->w;
282                 renderQuad.h = clip->h;
283         }
284
285         //Render to screen
286         SDL_RenderCopyEx( gRenderer, mTexture, clip, &renderQuad, angle, center, flip );
287 }
288
289 int LTexture::getWidth()
290 {
291         return mWidth;
292 }
293
294 int LTexture::getHeight()
295 {
296         return mHeight;
297 }
298
299 Dot::Dot(Uint16 x, Uint16 y, int8_t velX, int8_t velY, Uint8 w, Uint8 h)
300 {
301     //Initialize the offsets
302     mPosX = x;
303     mPosY = y;
304
305     //Initialize the velocity
306     mVelX = velX;
307     mVelY = velY;
308
309     mCollider.r = w / 2;
310
311     mWidth = w;
312     mHeight = h;
313
314     shiftColliders();
315 }
316
317 void Dot::move( SDL_Rect& paddle1)
318 {
319   //Move the dot left or right
320   mPosX += mVelX;
321   shiftColliders();
322
323   //If the dot collided or went too far to the left or right
324   if( ( mPosX - mCollider.r < 0 ) || ( mPosX + mCollider.r > SCREEN_WIDTH ) || checkCollision( mCollider, paddle1 ))
325   {
326     //Move back
327     mPosX -= mVelX;
328     shiftColliders();
329   }
330
331   //Move the dot up or down
332   mPosY += mVelY;
333   shiftColliders();
334
335   //If the dot collided or went too far up or down
336   if( ( mPosY - mCollider.r < 0 ) || ( mPosY + mCollider.r > SCREEN_HEIGHT ) || checkCollision( mCollider, paddle1 ))
337   {
338     //Move back
339     mPosY -= mVelY;
340     shiftColliders();
341   }
342 }
343
344 void Dot::render()
345 {
346   //Show the dot
347   gDotTexture.render( mPosX - mCollider.r, mPosY - mCollider.r );
348 }
349
350 void Dot::shiftColliders()
351 {
352         //Align collider to center of dot
353         mCollider.x = mPosX;
354         mCollider.y = mPosY;
355 }
356
357 double distanceSquared( int x1, int y1, int x2, int y2 )
358 {
359   int deltaX = x2 - x1;
360   int deltaY = y2 - y1;
361   return deltaX*deltaX + deltaY*deltaY;
362 }
363
364 bool checkCollision( Circle& a, SDL_Rect& b )
365 {
366   //Closest point on collision box
367   int cX, cY;
368
369   //Find closest x offset
370   if( a.x < b.x )
371   {
372     cX = b.x;
373   }
374   else if( a.x > b.x + b.w )
375   {
376     cX = b.x + b.w;
377   }
378   else
379   {
380     cX = a.x;
381   }
382
383   //Find closest y offset
384   if( a.y < b.y )
385   {
386     cY = b.y;
387   }
388   else if( a.y > b.y + b.h )
389   {
390     cY = b.y + b.h;
391   }
392   else
393   {
394     cY = a.y;
395   }
396
397   //If the closest point is inside the circle
398   if( distanceSquared( a.x, a.y, cX, cY ) < a.r * a.r )
399   {
400     //This box and the circle have collided
401     return true;
402   }
403
404   //If the shapes have not collided
405   return false;
406 }
407
408 Paddle::Paddle(Uint16 x, Uint16 y, Uint8 velX, Uint8 velY, Uint8 w, Uint8 h)
409 {
410     //Initialize the offsets
411     p.x = x;
412     p.y = y;
413     p.w = w;
414     p.h = h;
415
416     //Initialize the velocity
417     mVelX = velX;
418     mVelY = velY;
419 }
420
421 SDL_Rect& Paddle::getCollider()
422 {
423   return p;
424 }
425
426 void Paddle::handleEvent( SDL_Event& e )
427 {
428     //If a key was pressed
429         if( e.type == SDL_KEYDOWN && e.key.repeat == 0 )
430     {
431         //Adjust the velocity
432         switch( e.key.keysym.sym )
433         {
434             case SDLK_UP: {
435               mVelY -= PADDLE_VEL;
436               break;
437             }
438             case SDLK_DOWN: {
439               mVelY += PADDLE_VEL; 
440               break;
441             }
442         }
443     }
444     //If a key was released
445     else if( e.type == SDL_KEYUP && e.key.repeat == 0 )
446     {
447         //Adjust the velocity
448         switch( e.key.keysym.sym )
449         {
450             case SDLK_UP: mVelY += PADDLE_VEL; break;
451             case SDLK_DOWN: mVelY -= PADDLE_VEL; break;
452         }
453     }
454 }
455
456 void Paddle::move()
457 {
458     //Move the dot up or down
459     p.y += mVelY;
460
461     //If the dot went too far up or down
462     if( ( p.y < 0 ) || ( p.y + p.h > SCREEN_HEIGHT ) )
463     {
464         //Move back
465         p.y -= mVelY;
466     }
467 }
468
469 void Paddle::render()
470 {
471     //Show the dot
472     SDL_RenderFillRect(gRenderer, &p);
473 }
474
475 bool init()
476 {
477         //Initialization flag
478         bool success = true;
479
480         //Initialize SDL
481         if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
482         {
483                 printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
484                 success = false;
485         }
486         else
487         {
488                 //Set texture filtering to linear
489                 if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
490                 {
491                         printf( "Warning: Linear texture filtering not enabled!" );
492                 }
493
494                 //Create window
495                 gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
496                 if( gWindow == NULL )
497                 {
498                         printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
499                         success = false;
500                 }
501                 else
502                 {
503                         //Create vsynced renderer for window
504                         gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
505                         if( gRenderer == NULL )
506                         {
507                                 printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
508                                 success = false;
509                         }
510                         else
511                         {
512                                 //Initialize renderer color
513                                 SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
514                                 //Initialize PNG loading
515                                 int imgFlags = IMG_INIT_PNG;
516                                 if( !( IMG_Init( imgFlags ) & imgFlags ) )
517                                 {
518                                         printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
519                                         success = false;
520                                 }
521                         }
522                 }
523         }
524
525         return success;
526 }
527
528 bool loadMedia()
529 {
530         //Loading success flag
531         bool success = true;
532
533         //Load dot texture
534         if( !gDotTexture.loadFromFile( "dot.bmp" ) )
535         {
536                 printf( "Failed to load dot texture!\n" );
537                 success = false;
538         }
539
540         return success;
541 }
542
543 void close()
544 {
545         //Free loaded images
546         gDotTexture.free();
547
548         //Destroy window        
549         SDL_DestroyRenderer( gRenderer );
550         SDL_DestroyWindow( gWindow );
551         gWindow = NULL;
552         gRenderer = NULL;
553
554         //Quit SDL subsystems
555         IMG_Quit();
556         SDL_Quit();
557 }
558
559 int main( int argc, char* args[] )
560 {
561         //Start up SDL and create window
562         if( !init() )
563         {
564                 printf( "Failed to initialize!\n" );
565         }
566         else
567         {
568                 //Load media
569                 if( !loadMedia() )
570                 {
571                         printf( "Failed to load media!\n" );
572                 }
573                 else
574                 {       
575                         //Main loop flag
576                         bool quit = false;
577
578                         //Event handler
579                         SDL_Event e;
580
581                         //The dot that will be moving around on the screen
582                         Paddle paddle = Paddle(10, SCREEN_HEIGHT/2, 0, 0, 8, 40);
583
584                         Dot ball = Dot(SCREEN_WIDTH/2, SCREEN_HEIGHT/2, -1, 1, 10, 10);
585                         gDotTexture.setColor(0xFF, 0xFF, 0xFF);
586
587                         //While application is running
588                         while( !quit )
589                         {
590                                 //Handle events on queue
591                                 while( SDL_PollEvent( &e ) != 0 )
592                                 {
593                                         //User requests quit
594                                         if( e.type == SDL_QUIT )
595                                         {
596                                                 quit = true;
597                                         }
598
599                                         //Handle input for the dot
600                                         paddle.handleEvent( e );
601                                 }
602
603                                 //Move the dot
604                                 paddle.move();
605                                 ball.move(paddle.getCollider());
606
607                                 //Clear screen
608                                 SDL_SetRenderDrawColor( gRenderer, 0x00, 0x00, 0x00, 0xFF );
609                                 SDL_RenderClear( gRenderer );
610                 
611                                 //Render middle line
612                                 SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
613                                 SDL_RenderDrawLine(gRenderer, SCREEN_WIDTH/2, 0, SCREEN_WIDTH/2, SCREEN_HEIGHT);
614
615                                 //Render objects
616                                 paddle.render();
617                                 ball.render();
618
619                                 //Update screen
620                                 SDL_RenderPresent( gRenderer );
621                         }
622                 }
623         }
624
625         //Free resources and close SDL
626         close();
627
628         return 0;
629 }