Advertisement
thecplusplusguy

Simple sidescroller game - game.cpp

Jul 14th, 2011
2,519
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 17.76 KB | None | 0 0
  1. //This example program is created by thecplusplusuy for demonstration purposes. It's a simple mario like side-scroller game:
  2. //http://www.youtube.com/user/thecplusplusguy
  3. //Free source, modify if you want, LGPL licence (I guess), I would be happy, if you would not delete the link
  4. //so other people can see the tutorial
  5. //this file is game.cpp it's the 'main file', this put all of the files together
  6. #include "game.h"
  7.  
  8. SDL_Rect baseclass::coord; //we have to actually reserve memory for the static SDL_Rect from the baseclass
  9.  
  10. void game::showmessage(const char* c)   //shows the message to the screen for a frame
  11. {
  12.     SDL_Color color = {255,0,0}; //red color
  13.     SDL_Surface* text=TTF_RenderText_Solid(font,c,color); //just create the text
  14.     SDL_Rect tmprect={screen->w/2 - text->w/2,20}; //to the center in the x axis and 20 in the y
  15.     SDL_BlitSurface(text,NULL,screen,&tmprect); //put it into the screen
  16.     SDL_FreeSurface(text);  //we don't need it anymore, so free it
  17.     SDL_Flip(screen);   //flip the screen, else we can't see it.
  18. }
  19.  
  20.  
  21. int game::showmenu(SDL_Surface* screen) //shows he menu
  22. {
  23.     int x,y;
  24.     bool run=true;  //bool variable for the menu's for loop
  25.     const int NUMMENU=2;//we have 2 menu item
  26.     const char* array[NUMMENU] = {"Continue","Exit"}; //the label of these are these
  27.     SDL_Surface* menu[NUMMENU];//we need surface for them
  28.     bool selected[NUMMENU] = {0,0};//a boolean array for each menu item, true if it is selected
  29.     SDL_Color colors[2] = {{255,255,255},{255,0,0}};    //white and red color
  30.  
  31.     for(int i=0;i<NUMMENU;i++)
  32.         menu[i]=TTF_RenderText_Solid(font,array[i],colors[0]); //make all of the white menus
  33.  
  34.     SDL_Rect pos[NUMMENU];  //we store the position in here
  35.     for(int i=0;i<NUMMENU;i++)
  36.     {
  37.         pos[i].x=screen->clip_rect.w/2-menu[i]->clip_rect.w/2;  //put it approxemately to the center of the screen
  38.         pos[i].y=screen->clip_rect.h/2+i*(menu[i]->clip_rect.h);
  39.     }
  40.     SDL_Event menuevent;    //event
  41.  
  42.     SDL_Surface* alpha=SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,screen->w,screen->h,32,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,screen->format->Amask); //create a new layer
  43.     SDL_FillRect(alpha,&screen->clip_rect,SDL_MapRGB(screen->format,0x00,0x00,0x00)); //which we fill with black
  44.     SDL_SetAlpha(alpha,SDL_SRCALPHA,129);   //and use it for alpha blending, because it's a cool effect
  45.     SDL_BlitSurface(alpha,NULL,screen,NULL);    //then show it
  46.     SDL_FreeSurface(alpha); //we don't need it anymore
  47.     while(run)
  48.     {
  49.         SDL_WaitEvent(&menuevent); //OK, I think, that's new, we'll wait until an event happened, if there is no event
  50.                                                              //the program will sleep in this function, so less CPU power is needed, we don't need
  51.                                                              //to regulate the FPS
  52.             switch(menuevent.type)
  53.             {
  54.                 case SDL_QUIT:  //if we're exiting, than free the surfaces and exit
  55.                     for(int i=0;i<NUMMENU;i++)
  56.                         SDL_FreeSurface(menu[i]);
  57.                     return 1;
  58.                 case SDL_MOUSEMOTION:   //if the mouse is moved
  59.                     x=menuevent.motion.x;   //then get the coordinates
  60.                     y=menuevent.motion.y;
  61.                     for(int i=0;i<NUMMENU;i++)  //we go throught all of the element
  62.                     {   //and check if we are inside one of them
  63.                         if(x>=pos[i].x && x <= (pos[i].x + menu[i]->clip_rect.w) && y>=pos[i].y && y <= (pos[i].y + menu[i]->clip_rect.h))
  64.                         {
  65.                             if(!selected[i]) //if so, then we check that is it already selected
  66.                             {
  67.                                 SDL_FreeSurface(menu[i]);   //if not, then free the surface and make a red one
  68.                                 menu[i]=TTF_RenderText_Solid(font,array[i],colors[1]);
  69.                                 selected[i]=1; //and select the surface
  70.                             }
  71.                         }else{  //if we're not selecting one
  72.                             if(selected[i]) //we check, that is it selected
  73.                             {
  74.                                 SDL_FreeSurface(menu[i]); //if so, then we delete and create a white one.
  75.                                 menu[i]=TTF_RenderText_Solid(font,array[i],colors[0]);
  76.                                 selected[i]=0;  //and unselect it
  77.                             }
  78.                         }
  79.                 }
  80.                 break;
  81.                 case SDL_MOUSEBUTTONDOWN:   //if the mouse button was pressed
  82.                     x=menuevent.button.x;   //get the coordinates
  83.                     y=menuevent.button.y;
  84.                     for(int i=0;i<NUMMENU;i++)  //do the same check
  85.                     {
  86.                         if(x>=pos[i].x && x <= (pos[i].x + menu[i]->clip_rect.w) && y>=pos[i].y && y <= (pos[i].y + menu[i]->clip_rect.h))
  87.                         {
  88.                             for(int j=0;j<NUMMENU;j++)  //if we clicked one menuitem, we free the menus and return the number
  89.                                 SDL_FreeSurface(menu[j]);   //of the clicked menu
  90.                             return i;
  91.                         }
  92.                     }
  93.                     break;
  94.                 case SDL_KEYDOWN:   //if we press a key
  95.                     if(menuevent.key.keysym.sym==SDLK_ESCAPE) //which escape
  96.                     {
  97.                         for(int i=0;i<NUMMENU;i++)
  98.                             SDL_FreeSurface(menu[i]);
  99.                         return 0;   //than return to the game
  100.                     }
  101.                     break;
  102.             }
  103.         for(int i=0;i<NUMMENU;i++)
  104.             SDL_BlitSurface(menu[i],NULL,screen,&pos[i]);   //show the menu
  105.         SDL_Flip(screen);   //and actually show it in the screen
  106.     }
  107. }
  108.  
  109.  
  110. game::game()    //constructor
  111. {
  112.     SDL_Init(SDL_INIT_EVERYTHING);  //don't forget that, we have to initialize the SDL
  113.     screen=SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,32,SDL_SWSURFACE);   //init the screen, if you want fullscreen game, put
  114.     // SDL_SWSURFACE|SDL_FULLSCREEN to the place of flags (4. parameter)
  115.     //load the needed images
  116.     block=load_image("blocks.bmp");
  117.     background=load_image("endless.bmp");
  118.     bul=load_image("bullet.bmp");
  119.     ene=load_image("enemy.bmp");
  120.     TTF_Init(); //Initialize the font
  121.     font=TTF_OpenFont("air.ttf",20);    //and load it
  122.     //set the screen and the camera initialize position
  123.     baseclass::coord.x=baseclass::coord.y=camera.x=camera.y=0;
  124.     baseclass::coord.w=camera.w=SCREEN_WIDTH;
  125.     baseclass::coord.h=camera.h=SCREEN_HEIGHT;
  126.     direction[0]=direction[1]=0;    //we don't go any direction when we start the game
  127.     running=true;   //bool for the main loop
  128.     player1=new player(load_image("player.bmp"));   //we create a new player
  129.     //we set the initialize position of the finish, don't forget, if it's remain 0,0,50,50 after the loadmap than change it to the last block of the game
  130.     //we haven't done that in the tutorial, but it's easy
  131.     finish.x=0;
  132.     finish.y=0;
  133.     finish.w=50;
  134.     finish.h=50;
  135. }
  136.  
  137. game::~game()   //destructor
  138. {
  139.     //free the images
  140.     SDL_FreeSurface(block);
  141.     SDL_FreeSurface(background);
  142.     SDL_FreeSurface(bul);
  143.     delete player1; //delete the player
  144.     for(int i=0;i<bullets.size();i++)   //and the bullets
  145.         delete bullets[i];
  146.     for(int i=0;i<enemies.size();i++)//and the enemies
  147.         delete enemies[i];
  148.     //deinitialze things
  149.     TTF_CloseFont(font);
  150.     TTF_Quit();
  151.     SDL_Quit();
  152. }
  153.  
  154. SDL_Surface* game::load_image(const char* filename) //it will load an image
  155. {
  156.     SDL_Surface* tmp=SDL_LoadBMP(filename); //load the BMP to a tmp variable
  157.     SDL_Surface* tmp2=SDL_DisplayFormat(tmp);   //change it to the format of the screen
  158.     SDL_SetColorKey(tmp2,SDL_SRCCOLORKEY,SDL_MapRGB(screen->format,0x00,0xff,0xff)); //set the colorkey, so the 00ffff color is transparent
  159.     SDL_FreeSurface(tmp);   //free the tmp, we don't need it anymore
  160.     return tmp2;    //return
  161. }
  162.  
  163.  
  164. void game::handleEvents()   //it will handle all of the event that occure
  165. {
  166.     SDL_Event event;
  167.     while(SDL_PollEvent(&event))    //while there are event in the queue, we get one, and delete it from the queue
  168.     {
  169.         switch(event.type)
  170.         {
  171.             case SDL_QUIT:  //if quit, then we quit
  172.                 running=false;
  173.                 return;
  174.             case SDL_KEYDOWN:   //if a key is pressed
  175.                 switch(event.key.keysym.sym)
  176.                 {
  177.                     case SDLK_LEFT: //if it's the left key
  178.                         direction[0]=1; //then move left (now just set the boolean value)
  179.                         player1->setMoving(1);  //and set the player to move
  180.                         break;
  181.                     case SDLK_RIGHT:    //we do the same with the right key
  182.                         direction[1]=1;
  183.                         player1->setMoving(1);
  184.                         break;
  185.                     case SDLK_SPACE:    //if space, than set the player to jump
  186.                         player1->setJump();
  187.                         break;
  188.                     case SDLK_f:    //if f than shoot
  189.                         if(player1->getDirection()=='r')    //if we face to the right, than we shoot the bullet from the right of the
  190.                                                                                           //surface (the player) with positive xvel
  191.                             bullets.push_back(new bullet(bul,player1->getRect()->x+player1->getRect()->w,player1->getRect()->y+15,5,0));
  192.                         else    //else we do the opposite
  193.                             bullets.push_back(new bullet(bul,player1->getRect()->x,player1->getRect()->y+15,-5,0));
  194.                         break;
  195.                     case SDLK_ESCAPE:   //if the escape is pressed
  196.                         int h=showmenu(screen); //than we show the menu
  197.                         if(h==1)    //if we exited
  198.                             running=false;  //than exit
  199.                     break;
  200.                 }
  201.                 break;
  202.  
  203.             case SDL_KEYUP: //if we relased a key
  204.                 switch(event.key.keysym.sym)
  205.                 {
  206.                     case SDLK_LEFT: //than we do the opposite then when we press a key
  207.                         direction[0]=0;
  208.                         player1->setMoving(0);
  209.                         break;
  210.                     case SDLK_RIGHT:
  211.                         direction[1]=0;
  212.                         player1->setMoving(0);
  213.                         break;
  214.                 }
  215.                 break;
  216.         }
  217.     }
  218. }
  219.  
  220. //it is load the map from the file, in the file every number is corresponds to a 50x50 tile (image)
  221. void game::loadmap(const char* filename)
  222. {
  223.     std::ifstream in(filename); //open the file
  224.     if(!in.is_open())   //if we are not succeded
  225.     {
  226.         std::cout << "Problem with loading the file" << std::endl;  //write out end exit
  227.         return;
  228.     }
  229.     //read the width and the height from the file
  230.     int width,height;
  231.     in >> width;
  232.     in >> height;
  233.     int current;
  234.     for(int i=0;i<height;i++)   //with two for loops go throught the file
  235.     {
  236.         std::vector<int> vec;   //every time we create a vector, and later we push that vector to the end of another vector
  237.         //so it's like a 2D array (matrix)
  238.         for(int j=0;j<width;j++)
  239.         {
  240.             if(in.eof())    //if we reached the file before we end with the read in
  241.             {
  242.                 std::cout << "File end reached too soon" << std::endl;  //write out and exit
  243.                 return;            
  244.             }
  245.             in >> current;  //read the current number
  246.             if(current==-1) //if it's -1 than we put a new enemy to the position
  247.             {
  248.                 enemies.push_back(new enemy(ene,j*TILE_SIZE,i*TILE_SIZE,1,0)); //because every tile is TILE_SIZE width and height, we can calculate
  249.                 //where they start by multiplying it with the i or j
  250.                 vec.push_back(0);   //and we push back 0 to the vector (so nothing will be visible there)
  251.             }else{
  252.                 if(current>=0 && current<=7)    //if the current is greater or equal then 0 (so nothing) and less or equal than the max number of tiles
  253.                 //change the 7 to a bigger number, if you want to add more tile to the tiles.bmp image, don't forget 50x50 tiles
  254.                 {
  255.                     if(current==3)  //if the current is 3
  256.                     {
  257.                         finish.x=j*50;  //set the finish coordinate
  258.                         finish.y=i*50;
  259.                     }
  260.                     vec.push_back(current); //put the current into our matrix which represent the map in the game
  261.                 }else{
  262.                     vec.push_back(0);   //if the tile number is not known than just push 0 (so nothing) in the current position
  263.                 }
  264.             }
  265.         }
  266.         map.push_back(vec); //finally we push the vector to the end of the vector (dynamically allocated matrix :D)
  267.     }
  268.     in.close(); //close the file
  269. }
  270.  
  271. void game::showmap()    //this shows the map on the screen
  272. {
  273.     //first of all calculate the first visible tile's coordinate, it's help, if you think throught with a few number, it's a bit harder to explain
  274.     //but basically we just round down to the nearest number which can be divided by 50 and than divide it by 50 because we get the coordinate in
  275.     //pixels, but we want the coordinate in 'tiles'
  276.     int start=(baseclass::coord.x-(baseclass::coord.x%baseclass::TILE_SIZE))/baseclass::TILE_SIZE;
  277.     //than the last visible coordinate (round up to the nearest 50)
  278.     int end=(baseclass::coord.x+baseclass::coord.w+(baseclass::TILE_SIZE-(baseclass::coord.x+baseclass::coord.w)%baseclass::TILE_SIZE))/TILE_SIZE;
  279.     if(start<0) //if the start tile is smaller than 0 just set it 0
  280.         start=0;
  281.     if(end>map[0].size())   //if the end tile is greater then the size of the map, just set back
  282.         end=map[0].size();
  283.     for(int i=0;i<map.size();i++)   //we go throught all of the tiles
  284.         for(int j=start;j<end;j++)
  285.             if(map[i][j]!=0)    //if it's not nothing
  286.             {
  287.                 //we calculate the position in the blocks.bmp image
  288.                 SDL_Rect blockrect={(map[i][j]-1)*baseclass::TILE_SIZE,0,baseclass::TILE_SIZE, baseclass::TILE_SIZE};
  289.                 //and in the screen (so for example if the camera at 100px position and the tile is at 120px position, we show the tile at 20px position
  290.                 SDL_Rect destrect = {j*baseclass::TILE_SIZE-baseclass::coord.x,i*50};
  291.                 SDL_BlitSurface(block,&blockrect,screen,&destrect); //then blit it to the screen
  292.             }
  293.            
  294. }
  295.  
  296. void game::start()  //start the game
  297. {
  298.     Uint32 start;   //start time for the FPS regulation
  299.     loadmap("map.map"); //load the map
  300.     while(running)//while we not exited
  301.     {
  302.         start=SDL_GetTicks();   //get the start time
  303.         handleEvents(); //handle the events
  304.  
  305.         if(direction[0])    //if we are pressing the left arrow key
  306.         {
  307.             player1->setDirection('l'); //set the direction of the player to left
  308.             if(player1->getRect()->x > 0)   //if it's greater than zero
  309.                 player1->setXvel(-1);   //we move the player
  310.             else{
  311.                 player1->setXvel(0);    //else we move the camera instead and the player is unmoved
  312.                 camera.x--;
  313.                 baseclass::coord.x--;
  314.             }
  315.             if(camera.x<0)  //we scroll the background and if it's less then 0
  316.                 camera.x=2000-SCREEN_WIDTH; //then we set it to background width-screen width
  317.             //that's why it seems endless
  318.         }else if(direction[1])  //if the right key is pressed
  319.         {
  320.             player1->setDirection('r'); //than we basically do the same things
  321.             if(player1->getRect()->x < 80)
  322.                 player1->setXvel(1);
  323.             else
  324.             {
  325.                 player1->setXvel(0);
  326.                 camera.x++;
  327.                 baseclass::coord.x++;
  328.             }
  329.             if(camera.x>=2000-SCREEN_WIDTH)
  330.                 camera.x=0;
  331.         }else
  332.             player1->setXvel(0);    //if none of the key is pressed, we are not moving
  333.  
  334.             //calculate the start and the end coordinate (see a little bit above)
  335.             int str=(baseclass::coord.x-(baseclass::coord.x%baseclass::TILE_SIZE))/baseclass::TILE_SIZE;
  336.             int end=(baseclass::coord.x+baseclass::coord.w+(baseclass::TILE_SIZE-(baseclass::coord.x+baseclass::coord.w)%baseclass::TILE_SIZE))/50;
  337.             if(start<0)
  338.                 start=0;
  339.             if(end>map[0].size())
  340.                 end=map[0].size();
  341.             for(int i=0;i<map.size();i++)   //go throuh the map
  342.                 for(int j=str;j<end;j++)
  343.                 {
  344.                     if(map[i][j]==0)    //if it's nothing, we don't have to check collision
  345.                         continue;
  346.                     SDL_Rect destrect={j*50-baseclass::coord.x,i*50,50,50}; //calculate the relative coordinate to the screen (see above)
  347.                     for(int g=0;g<bullets.size();g++)   //go throuht the bullets
  348.                         if(collision(bullets[g]->getRect(),&destrect))  //if the bullet collide with a tile
  349.                         {
  350.                             delete bullets[g];  //than delete it
  351.                             bullets.erase(bullets.begin()+g);   //and delete from the vector as well
  352.                         }
  353.                 }
  354.  
  355.         for(int i=0;i<bullets.size();i++)   //go through the bullets
  356.             if(bullets[i]->getRect()->x >= screen->w || bullets[i]->getRect()->x <= 0) //and if it's outside of the screen
  357.             {
  358.                 delete bullets[i];  //delete them
  359.                 bullets.erase(bullets.begin()+i);
  360.             }
  361.  
  362.         for(int i=0;i<bullets.size();i++)   //go through both enemies and bullets
  363.             for(int j=0;j<enemies.size();j++)
  364.             {
  365.                 SDL_Rect tmprect={enemies[j]->getRect()->x-baseclass::coord.x,enemies[j]->getRect()->y,40,40}; //calculate relative coordinates see above
  366.                 if(collision(&tmprect,bullets[i]->getRect()))   //if one bullet collide with and enemy
  367.                 {
  368.                     delete enemies[j];  //delete the bullet and the enemy
  369.                     delete bullets[i];
  370.                     enemies.erase(enemies.begin()+j);
  371.                     bullets.erase(bullets.begin()+i);
  372.                 }
  373.             }
  374.  
  375.         for(int j=0;j<enemies.size();j++)   //go through the enemies
  376.         {
  377.             SDL_Rect tmprect={enemies[j]->getRect()->x-baseclass::coord.x,enemies[j]->getRect()->y,40,40}; //calculate relative coordinates see above
  378.             if(collision(&baseclass::coord,enemies[j]->getRect()))  //if the enemy is on the screen, (change thanks for TheAngelbrothers to point out a bug :D)
  379.             {
  380.                 if(collision(&tmprect,player1->getRect()))  //if we collide with an enemy
  381.                 {
  382.                     if(player1->getRect()->y+player1->getRect()->h>=enemies[j]->getRect()->y && player1->getRect()->y+player1->getRect()->h<=enemies[j]->getRect()->y+10)   //if we are on the 'head' of the enemy
  383.                     {
  384.                         delete enemies[j];  //kill the enemy
  385.                         enemies.erase(enemies.begin()+j);
  386.                     }else{
  387.                         player1->setHealth(player1->getHealth()-1); //else decrease the health of the player with 1
  388.                     }
  389.                 }
  390.                 enemies[j]->move(map);  //only move, when the enemy is on the screen. (change)
  391.             }
  392.         }
  393.  
  394.         //move everything
  395.         player1->move(map);
  396.         for(int i=0;i<bullets.size();i++)
  397.             bullets[i]->move();
  398.  
  399.  
  400.  
  401.  
  402.         //show everything
  403.         SDL_BlitSurface(background,&camera,screen,NULL);
  404.         showmap();
  405.         player1->show(screen);
  406.         for(int i=0;i<bullets.size();i++)
  407.             bullets[i]->show(screen);
  408.         for(int i=0;i<enemies.size();i++)
  409.             enemies[i]->show(screen);
  410.         SDL_Flip(screen);   //put it the the actual screen
  411.  
  412.         if(player1->getHealth()<=0 || player1->getRect()->y >=screen->h)    //if the health of the player is 0 or it's under the screen
  413.         {
  414.             running=false;  //kill it
  415.             //if you want things like reload the map, than just set back every variable to the initialize state, and don't set the running=false
  416.             showmessage("game over");
  417.             SDL_Delay(1000);    //we delay, so we can see the message
  418.         }
  419.         SDL_Rect tmprect = {finish.x-coord.x,finish.y,50,50};   //we have to calculate relative coordinates to the screen (see above)
  420.         if(collision(player1->getRect(),&tmprect))  //if we collide with the communist flag
  421.         {
  422.             running=false;
  423.             showmessage("You Win"); //if you want to load the next map, I recommend you, to read the maps in the constructor from game.dat
  424. //to an array of strings and make a current value, which contain the current map, and here check, that the max maps number is greater than
  425. //the current, if so, increase the current and set back the values to the initialize state, and call the load map with the current string
  426.             SDL_Delay(1000);
  427.         }
  428.  
  429.  
  430.         if(1000/30>(SDL_GetTicks()-start))  //if we are achive this point too early
  431.             SDL_Delay(1000/30-(SDL_GetTicks()-start));  //than we delay a little bit
  432.     }
  433. }
  434. //that's all :D
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement