Game States in SDL

Started by
4 comments, last by Morrandir 18 years, 4 months ago
Well, now that I have sound in my little pong app, I need to get some different game states. I posted about this some days ago, and I got a reply. I have tried to implement this into my game using a switch, but I think the location of some of my code is a bit off (or a lot off). Eventually I will be showing a scoreboard after the person presses enter, but for now, the game will just exit. (see what i mean below) The problem is that the intro screen shows, then waits for enter. When enter is pressed, the screen goes to the actual pong game image, but nothing moves and it exits right away. Also my escape key doesnt exit the game, nor does the pressing of the 'x' in the top right corner. Help is aprreciated here.

#include <stdio.h>
#include <stdlib.h>
#include "SDL/SDL.h"
#include "SDL/SDL_mixer.h"
#define AUDIO_RATE 22050
#define AUDIO_FORMAT AUDIO_S16
#define AUDIO_CHANNELS 2
#define AUDIO_BUFFERS 4096
#define GAME_STATE_INTRO 0
#define GAME_STATE_RUN 1
#define GAME_STATE_SCOREBOARD 2

using namespace std;

const int WINDOW_WIDTH = 720;
const int WINDOW_HEIGHT = 480;
const char* WINDOW_TITLE = "Pong";
float ballXVel = 1;
float ballYVel = 1;

Mix_Chunk *phaser = NULL;

int phaserChannel = -1;


int main(int argc, char *argv[])
{
	Mix_Chunk *phaser;

    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO);
	Mix_OpenAudio(AUDIO_RATE, AUDIO_FORMAT, AUDIO_CHANNELS, AUDIO_BUFFERS);

    SDL_Surface* screen = SDL_SetVideoMode( WINDOW_WIDTH, WINDOW_HEIGHT, 0, 
      SDL_HWSURFACE | SDL_DOUBLEBUF );
	SDL_WM_SetCaption(WINDOW_TITLE, 0);
	SDL_Surface* paddle1 = SDL_LoadBMP("paddle1.bmp");
	SDL_Surface* paddle2 = SDL_LoadBMP("paddle2.bmp");
	SDL_Surface* ball = SDL_LoadBMP("ball.bmp");
    SDL_Surface* intro = SDL_LoadBMP("Intro.bmp");
    SDL_SetColorKey(ball, SDL_SRCCOLORKEY, SDL_MapRGB(ball->format, 
      255, 0, 0)); 
    SDL_Rect intro_source;
    intro_source.x = 0;
    intro_source.y = 0;
    intro_source.w = 720;
    intro_source.h = 480;
    
    SDL_Rect intro_destination;
    intro_destination.x = 0;
    intro_destination.y = 0;
    intro_destination.w = 720;
    intro_destination.h = 480;
    
    SDL_Rect paddle1_source;
	paddle1_source.x = 0;
	paddle1_source.y = 0;
	paddle1_source.w = 20;
	paddle1_source.h = 100;
	
	SDL_Rect paddle1_destination;
    paddle1_destination.x = 75;
    paddle1_destination.y = 240;
    paddle1_destination.w = 20;
    paddle1_destination.h = 100;
    
    SDL_Rect paddle2_source;
    paddle2_source.x = 0;
    paddle2_source.y = 0;
    paddle2_source.w = 20;
    paddle2_source.h = 100;
    
    SDL_Rect paddle2_destination;
    paddle2_destination.x = 645;
    paddle2_destination.y = 240;
    paddle2_destination.w = 20;
    paddle2_destination.h = 100;
    
    SDL_Rect ball_source;
    ball_source.x = 0;
    ball_source.y = 0;
    ball_source.w = 25;
    ball_source.h = 25;
    
    SDL_Rect ball_position;
    ball_position.x = WINDOW_WIDTH/2;
    ball_position.y = WINDOW_HEIGHT/2;
    ball_position.w = 25;
    ball_position.h = 25;
    
    phaser = Mix_LoadWAV("phaser.wav");

int game_state = GAME_STATE_INTRO;

             

SDL_Event event;

	bool gameRunning = true;
	bool keysHeld[323] ={false};
	while (gameRunning)
	{
		
        
        
		while (game_state < 3)
              {//while
              if (SDL_PollEvent(&event))
		{
			if (event.type == SDL_QUIT)
			{
				gameRunning = false;
			}
			if (event.type == SDL_KEYDOWN)
			{
				keysHeld[event.key.keysym.sym] = true;
			}
			if (event.type == SDL_KEYUP)
			{
				keysHeld[event.key.keysym.sym] = false;
			}	
		}
        if(keysHeld[SDLK_SPACE])
        game_state++;
        
        switch(game_state)
        {
             case GAME_STATE_INTRO:
             SDL_BlitSurface(intro, &intro_source, screen, &intro_destination);
             if (keysHeld[SDLK_ESCAPE])
		     {
			gameRunning = false;
             }
             break;
             
             case GAME_STATE_RUN:
		
        if (keysHeld[SDLK_ESCAPE])
		{
			gameRunning = false;
		}
        if (keysHeld[SDLK_UP])
        {
            paddle1_destination.y -= 2;
        }
        if (keysHeld[SDLK_DOWN])
        {
            paddle1_destination.y += 2;
            if (paddle1_destination.y + paddle1_destination.h > WINDOW_HEIGHT)
            {
                paddle1_destination.y = 480 - paddle1_destination.h;
              
            }
        }
        if (keysHeld[SDLK_KP2])
        {
            paddle2_destination.y += 2;
            if (paddle2_destination.y + paddle2_destination.h > WINDOW_HEIGHT)
            {
                paddle2_destination.y = 480 - paddle2_destination.h;
               
            }
        }
        if (keysHeld[SDLK_KP8])
        {
            paddle2_destination.y -= 2;
        }
        
        
        ball_position.x += ballXVel;
        ball_position.y += ballYVel;


        
        if (ball_position.x  + ball_position.w >  WINDOW_WIDTH)
        {
             gameRunning = false;
        }
        if (ball_position.x  < 0)
        {
             gameRunning = false;
        }
        if (ball_position.y  + ball_position.h >  WINDOW_HEIGHT)
        {
             Mix_PlayChannel(-1, phaser, 0);
             ballYVel *= -1;
             

        }
        if (ball_position.y  <0  )
        {
             Mix_PlayChannel(-1, phaser, 0);
             ballYVel *= -1;
             

        }
        if (ball_position.x   == paddle1_destination.x + paddle1_destination.w && ball_position.y + ball_position.h >= paddle1_destination.y )
        {
             Mix_PlayChannel(-1, phaser, 0);
             ballXVel *= -1.0;  

        }
        if (ball_position.x +ball_position.w  == paddle2_destination.x  && ball_position.y + ball_position.h  >= paddle2_destination.y && ball_position.y <= paddle2_destination.y + paddle2_destination.h)
        {
             Mix_PlayChannel(-1, phaser, 0);
             ballXVel *= -1.0;
        }
        SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 0));
        SDL_BlitSurface(paddle1, &paddle1_source, screen, &paddle1_destination);
        SDL_BlitSurface(paddle2, &paddle2_source, screen, &paddle2_destination);
        SDL_BlitSurface(ball, &ball_source, screen, &ball_position);
        break;
        case GAME_STATE_SCOREBOARD:
        gameRunning = false;
        break;           
        }
        SDL_Flip(screen);
        }
    
    }
    
    SDL_FreeSurface(paddle1); 
    SDL_FreeSurface(paddle2);
    SDL_FreeSurface(ball);
    Mix_HaltChannel(-1);    //halt all of the playing music
    Mix_FreeChunk(phaser);

    Mix_CloseAudio();

    SDL_Quit();
	return 0;
}


Advertisement
Hello,

It appears to me that the game state is advanced by pressing SPACE not ENTER: "if(keysHeld[SDLK_SPACE]) game_state++;"

But that's not the point...

Your game loop runs very fast in your game, so you most likely won't be able to release a button for several loops. That way 'space' in your key pressed buffer stays set to true and the game states are advanced too quickly. At least that's what I think happens :)
Try setting 'keysHeld[SDLK_SPACE]' to false after detecting space was pressed. This should be done with every key you're using.

P.S.: You should consider creating some functions thus structuring your code. It's always easier to look for errors in well-separated bits of code...
Change the following code
if(keysHeld[SDLK_SPACE]) game_state++;
to
if(event.key.keysym.sym == SDLK_SPACE) game_state++;
And move it inside the "if ( event.type == SDL_KEYDOWN )" conditional statement. It checks single key presses here, so you can hold down the key as long as you want, but the game state will only change once.
Thanks guys! It's working perfectly. I was wondering if 'Anonymous Poster' or whoever really, could give me some pointers as to how to structure this beast of a program to make it easier to read. I know that organization is important, and if this game gets any bigger, I am going to have quite the mess on my hands!
thanks again
I do something similar to AP's suggestion. I create a base object that I derive new game states from, then just update function pointers as the state changes.

//base objectclass IAppState { public:   virtual bool init();   virtual void update();   virtual void render();};//derive it to make your main menuclass MainMenuState : public IAppState {  public:   bool init();   void update();   void render();};//then in your app you would have an IAppState pointer with the current//game stateIAppState* m_pCurrentState;MainMenuState* m_pMainMenuState;//let's say we switch the game state to our main menu one//like we hit ESC or somethingm_pCurrentState = m_pMainMenuState;//then in your main game loop area...m_pCurrentState->update();m_pCurrentState->render();This makes debugging much easier (IMHO) as you can isolate any events in your game, etc..hth,
The Anonymous Poster was me, I just forgot my password and was lazy to get a new one :)

TheOther is completely right there, although those aren't fuction pointers he is using, but pointers to objects. It is of course also all right to use function pointers, that's the way I would do it. It's a bit complicated though...

So if you aren't sure you can handle OO code or function pointers, you can do it the 'old way' (it's the only way to start), by creating a neat little function for all pieces of code that you think could function as a separate whole (eg. Initialize(), Render(), Update() ) and in main() only use these functions and the code that directs game flow. So first you Initialize, then start the game loop, Update and Render than check for Input and repeat this until you want to exit. You can also use the return values of these functions to direct game flow (eg. while Updating you can find out that the ball is out of bounds, so the player has lost, return false or 0 or anything you like and exit).

God I hope I made some sense there...

I also hope that your tab characters and blank lines strayed while cut&pasting, otherwise you should improve your formatting style a bit...

This topic is closed to new replies.

Advertisement