Need help with menus in my game


#1

I’m new to SDL. I made a pong clone using tutorials. The game works fine, but when I tried to implement a menu system, it crashes, or when I or the AI wins, the menu appears again, but the game still runs in the background, and crashes.

Here’s my code, I left it untouched since the last crash…

#include<SDL2/SDL.h>
#include<SDL2/SDL_ttf.h>
#include<SDL2/SDL_mixer.h>
#include<stdlib.h>
#include<sstream>
#include<string.h>
#include<ctime>
#include<iostream>

using namespace std;

SDL_Window *window = NULL; //Window pointer
SDL_Surface *surface = NULL;    //Surface pointer

TTF_Font *font = NULL;

SDL_Event ev,we;

SDL_Rect player_paddle; //Player paddle object
SDL_Rect ai_paddle; //AI_paddle object
SDL_Rect ball;  //Ball object

SDL_Rect divider;

SDL_Rect player_score, ai_score, titlerect;

Uint32 white;

SDL_Color whit = {255,255,255};

const int BALL_X = 390;
const int BALL_Y = 290;

int PAD_SPEED = 4;
int PAD_SPEED_AI = 2;
int BALL_SPEED = 3;

int playerscore;
int aiscore;

int choice = 1;

float xVel,yVel;

Mix_Chunk *hit = NULL;
Mix_Chunk *wall = NULL;

bool PointInRect(int x, int y, SDL_Rect rec)
{
    if(x > rec.x && y > rec.y && x < rec.x + rec.w && y < rec.y + rec.h)
    {
        return true;
    }
    return false;
}

bool CheckCollision(SDL_Rect r1, SDL_Rect r2)
{
    if(PointInRect(r1.x,r1.y,r2) == true ||
    PointInRect(r1.x+r1.w, r1.y, r2) == true ||
    PointInRect(r1.x,r1.y+r1.h,r2) == true ||
    PointInRect(r1.x+r1.w,r1.y+r1.h,r2) == true)
    {
        return true;
    }

    return false;
}

int GetRandomNumber(int high, int low)
{
    return rand()% high + low;
}

void close();

void ResetBall()
{
    SDL_Delay(500);
    ball.x = BALL_X;
    ball.y = BALL_Y;
    yVel = GetRandomNumber(BALL_SPEED,-BALL_SPEED);
}

void loadGame()
{
    SDL_Init(SDL_INIT_EVERYTHING);
    TTF_Init();
    window = SDL_CreateWindow("Pong",0,0,800,600,SDL_WINDOW_SHOWN);
    surface = SDL_GetWindowSurface( window );


    //SDL_Surface * player_score_surface = TTF_RenderText_Solid(8bit,player_score.str(), white);
    //SDL_Surface * ai_score_surface = TTF_RenderText_Solid(8bit,player_score.str(), white);

    Mix_OpenAudio( 44100, MIX_DEFAULT_FORMAT, 2, 2048 );

    hit = Mix_LoadWAV("pong_sounds/hit.wav");
    wall = Mix_LoadWAV("pong_sounds/wall.wav");

    white = SDL_MapRGB(surface->format, 255,255,255);

    font = TTF_OpenFont("8bit.ttf", 28);

    srand(int(time(0)));
}

void loadSpr()
{
    player_paddle.x = 20;
    player_paddle.y = 10;
    player_paddle.h = 45;
    player_paddle.w = 10;

    ai_paddle.x = 760;
    ai_paddle.y = 10;
    ai_paddle.h = 45;
    ai_paddle.w = 10;

    ball.w = 7;
    ball.h = 7;
    ball.x = BALL_X;
    ball.y = BALL_Y;

    divider.w = 5;
    divider.h = 10;
    divider.x = 400;
    divider.y = 300;

    

    xVel = 3;
    yVel = GetRandomNumber(BALL_SPEED,-BALL_SPEED);

    player_score.x = 50;
    player_score.y = 10;
    ai_score.x = 700;
    ai_score.y = 10;
}

int logic()
{
    const Uint8 *keystates=SDL_GetKeyboardState(NULL);

    if(keystates[SDL_SCANCODE_UP])
    {
        player_paddle.y -= PAD_SPEED;
    }

    if(keystates[SDL_SCANCODE_DOWN])
    {
        player_paddle.y += PAD_SPEED;
    }

    if(keystates[SDL_SCANCODE_ESCAPE])
    {
        return 1;
    }

    if(player_paddle.y + player_paddle.h > 599)
    {
        player_paddle.y = 599 - player_paddle.h;
    }

    if(player_paddle.y < 1)
    {
        player_paddle.y = 1;
    }

    ball.x += xVel;
    ball.y += yVel;

    SDL_Delay(5);

    if(ball.y < 1)
    {
        yVel = -yVel;
        Mix_PlayChannel( -1, wall, 0 );
    }
    if(ball.y + ball.h> 600)
    {
        yVel = -yVel;
        Mix_PlayChannel( -1, wall, 0 );
    }

    if(ball.x + ball.h < -3)
    {
        ResetBall();
        aiscore++;
    }

    if(ball.x + ball.w > 805)
    {
        ResetBall();
        playerscore++;
    }

    if(ai_paddle.y + 0.5 * ai_paddle.h > ball.y + 0.5 * ball.h)
    {
        ai_paddle.y -= PAD_SPEED_AI;
    }

    if(ai_paddle.y + 0.5 * ai_paddle.h < ball.y + 0.5 * ball.h)
    {
        ai_paddle.y += PAD_SPEED_AI;
    }

    if(CheckCollision(ball, player_paddle) == true)
    {
        xVel = -xVel;

        if(ball.y - player_paddle.y <= 9)
        {
            yVel = -3;
        }

        if(ball.y - player_paddle.y  > 9 && ball.y - player_paddle.y  <= 18)
        {
            yVel = -(3/0.8);
        }

        if(ball.y - player_paddle.y  > 18 && ball.y - player_paddle.y  <= 27)
        {
            yVel = 0;
        }

        if(ball.y - player_paddle.y  > 27 && ball.y - player_paddle.y <= 36)
        {
            yVel = 3/0.8;
        }

        if(ball.y - player_paddle.y  > 36 && ball.y - player_paddle.y  <= 45)
        {
            yVel = 3;
        }
        //yVel = -yVel;
        //yVel = GetRandomNumber(BALL_SPEED,-BALL_SPEED);

        Mix_PlayChannel( -1, hit, 0 );
    }

    if(CheckCollision(ball, ai_paddle) == true)
    {
        xVel = -xVel;
        
        if(ball.y - ai_paddle.y <= 9)
        {
            yVel = -3;
        }

        if(ball.y - ai_paddle.y  > 9 && ball.y - ai_paddle.y  <= 18)
        {
            yVel = -(3/0.8);
        }

        if(ball.y - ai_paddle.y  > 18 && ball.y - ai_paddle.y  <= 27)
        {
            yVel = 0;
        }

        if(ball.y - ai_paddle.y  > 27 && ball.y - ai_paddle.y <= 36)
        {
            yVel = 3/0.8;
        }

        if(ball.y - ai_paddle.y  > 36 && ball.y - ai_paddle.y  <= 45)
        {
            yVel = 3;
        }

       
        //yVel = -yVel;
        //yVel = GetRandomNumber(BALL_SPEED,-BALL_SPEED);

        Mix_PlayChannel( -1, hit, 0 );
    }
}

int main_menu()
{

    const Uint8 *keystate=SDL_GetKeyboardState(NULL);

    SDL_FillRect(surface, NULL, 0);

    SDL_Surface *title = SDL_LoadBMP("title.bmp");

    SDL_BlitSurface(title, NULL, surface, NULL);

    SDL_UpdateWindowSurface(window);

    
    while(1)
    {
        SDL_PollEvent(&we);    
        if(we.type == SDLK_1)
        {
            SDL_FreeSurface(surface);
            break;
        }
    }

    return 1;
}

int op_drawScreen()
{

    SDL_FillRect(surface, NULL, 0);

    SDL_FillRect(surface, &player_paddle, white);
    SDL_FillRect(surface, &ai_paddle, white);
    SDL_FillRect(surface, &ball, white);

    stringstream Pscore;
    stringstream Ascore;

    Pscore << playerscore;
    Ascore << aiscore;

    SDL_Surface *PlayerScoreSurface = TTF_RenderText_Solid(font, Pscore.str().c_str(),whit);
    SDL_Surface *AIScoreSurface = TTF_RenderText_Solid(font, Ascore.str().c_str(),whit);

    SDL_BlitSurface(PlayerScoreSurface,NULL,surface, &player_score);
    SDL_BlitSurface(AIScoreSurface,NULL,surface, &ai_score);

    SDL_FreeSurface(PlayerScoreSurface);
    SDL_FreeSurface(AIScoreSurface);

    if(playerscore == 10)
    {
        
        SDL_Delay(50);
        SDL_FreeSurface(surface);
        surface = NULL;

        choice = 3;
    }
    if(aiscore == 10)
    {
        
        SDL_Delay(50);
        SDL_FreeSurface(surface);
        surface = NULL;

        choice = 3;
    }
    
    SDL_UpdateWindowSurface(window);
    

}

void close()            //Close the SDL framework
{
    SDL_DestroyWindow(window);
    TTF_Quit();
    SDL_Quit();
}

int main(int argc, char* args[])
{
    loadGame();
    bool quit = false;

    bool running = true;

    if(main_menu == 0)
    {
        running = false;
    }
    
    while(running)
    {
        SDL_PollEvent(&ev);

        if(ev.type == SDL_QUIT)
        {
            main_menu();
        }

        logic();
        op_drawScreen();
    }
    close();
    return 0;
}

Any help will be appreciated


#2

That is all I can do for you atm.

Hope it helps,
-Cass


#3

Just one more thing - I know the game states are bugging you and I come back to you, but meanwhile - check
https://wiki.libsdl.org/SDL_HasIntersection and https://wiki.libsdl.org/SDL_PointInRect

And I think you should fork my project and push your changes to your fork.

-Cass


#4

Thanks man, appreciate the help.


#5

Check if that helps to implement your states.
I saw that I forgot to render your divider =)
-Cass


How to create multiple SDL_Rects from one
#6

In general, it’s of course better when you learn a GUI toolkit, such as mine below, to know the general principles how the GUI is made. It is only 2500 lines of code, i tried to make it as simple as possible, but it makes no sense to make a simpler one, as it is not generic then and cannot do much anything. I made it for the beginners, but now a number of advanced programmers are using it.
-----------------------
kiss_sdl - Simple generic GUI widget toolkit for SDL2 https://github.com/actsl/kiss_sdl


#7

From https://github.com/Acry/simple-state-pattern/blob/master/example.c

int gameState;
enum gameState {IDLE, PLAYING, PAUSED, GAME_OVER};

gameState = IDLE;

while(running){
	SDL_RenderClear(Renderer);
	gameState_check();
	SDL_RenderPresent(Renderer);
}

IDLE is just a name for the enum, you can call it Menu or whatever. With numbers you will end up in a mess if you cross 2k lines of code.

//BEGIN FUNCTIONS
void gameState_check(void)
{
		switch(gameState)
		{
		case IDLE:
			IdleGo();
			break;
		case PLAYING:
			PlayingGo();
			break;
		case PAUSED:
			PausedGo();
			break;
		case GAME_OVER:
			Game_overGo();
			break;
		default:
		break;
    		}
}

The checker Function calls the driver function.
And this functions drives the state.

//BEGIN GAME STATE FUNCTIONS
//BEGIN IDLE
void IdleGo(void)
{
	IdleEvents();
	IdleUpdate();
	IdleDraw();
}

from http://gameprogrammingpatterns.com/game-loop.html

Cheers
-Cass


#8

For further Pong-Issues I added some ressources in my readme.