Search code examples
c++game-developmentraylib

Preventing the ball from moving after restarting the game in Raylib C++


I'm encountering an issue with my Brick Breaker game project. When the game ends, that is, when the number of clicks exceeds 10, a button appears and I exit. However, when I restart the game after exiting, the ball starts moving again according to the mouse position where I clicked the button or the position of the last click. However, this movement is not counted as a click.

Below, I've provided a summary including relevant code snippets from main.cpp and ball.cpp files. In the updateBall() function in ball.cpp, the position of the ball is updated based on mouse input. However, even when isGameOver is true, the position of the ball continues to be updated, leading to unexpected behavior.

If it's not enough here is Github Repo: https://github.com/OnurSevkiOkan/breakout-raylib (Im not sure is it okay to share github link, im new at stackoverflow :) )

main.cpp =>

#include <raylib.h>
#include <iostream>
#include <variables.h>
#include "ball.h"
#include "bricks.h"
#include "title_screen.h"
#include "gameplay_screen.h"

Ball ball;
Bricks brick;
TITLE_SCREEN title_screen;
GAMEPLAY_SCREEN gameplay_screen;


typedef enum GameScreen { LOGO = 0, TITLE, GAMEPLAY, ENDING } GameScreen;

int main(void)
{
    GameScreen currentScreen = TITLE;
    int framesCounter = 0;
    bool isPlayButtonInitialized = false;
    bool isExitButtonInitialized = false;
    bool isGameOver = false;

    InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Brick Breaker"); // Defines a window.
    SetTargetFPS(target_FPS); // Sets the FPS to 60.


    // Ininitalize bricks.
    brick.InitializeBricks();
    

    while (!WindowShouldClose())
    {
        switch (currentScreen)
        {
        case TITLE:
        {
            int button_state = title_screen.getBtnState();
            
            if (button_state == 2)
            {
                currentScreen = GAMEPLAY;
                ball.setBallPosition();
                isGameOver = false;
            }

        } break;
        case GAMEPLAY:
        {
            if (click_counter > 10)
            {
                isGameOver = true;
            }

            int ExitButton_State = gameplay_screen.getExitBtnState();

            if (isExitButtonInitialized == true)
            {
                gameplay_screen.drawExitButton();
            }


            if (ExitButton_State == 2)
            {
                currentScreen = ENDING;
                ball.setBallPosition();
                brick.resetBricks();
            }
        } break;
        case ENDING:
        {
            if (IsKeyPressed(KEY_ENTER) || IsGestureDetected(GESTURE_TAP))
            {
                currentScreen = TITLE;
            }
        } break;
        default: break;
        }

        BeginDrawing();
        ClearBackground(RAYWHITE);

        switch (currentScreen)
        {
        case TITLE:
        {
            title_screen.drawPlayButton();
            title_screen.drawTexts();

            // Load play button texture when entering the title screen
            if (!isPlayButtonInitialized && currentScreen == TITLE)
            {
                title_screen.initPlayButton();
                isPlayButtonInitialized = true;
            }
        } break;

        case GAMEPLAY:
        {
            brick.create_brick(ball);
            
            ball.drawBall();

            // To prevent the ball position change when the button is clicked
            if (click_counter <= 10 && isGameOver == false && brick.isAllBricksGone() == false)
            {
                ball.updateBall();
            }
            
            ball.drawScore();

            if (brick.isAllBricksGone() == true)
            {
                ball.setBallPosition();

                DrawText("CONGRATULATIONS!", GetScreenWidth() / 2 - 150, GetScreenHeight() / 2 - 50, 30, WHITE);

                if (!isExitButtonInitialized && currentScreen == GAMEPLAY)
                {
                    gameplay_screen.initExitButton();
                    isExitButtonInitialized = true;
                }
            }

            if (click_counter > 10)
            {
                isGameOver = true;
                ball.setBallPosition();
                DrawText("HaHaHaHA LOOOSER B!TCHES", GetScreenWidth() / 2 - 200, GetScreenHeight() / 2 - 50, 30, WHITE);

                if (!isExitButtonInitialized && currentScreen == GAMEPLAY)
                {
                    gameplay_screen.initExitButton();
                    isExitButtonInitialized = true;
                }
            }

        } break;
        case ENDING:
        {
            if (isExitButtonInitialized && currentScreen == ENDING)
            {
                gameplay_screen.unloadExitButtonTexture();
                isExitButtonInitialized = false;
                click_counter = 0;
            }

            DrawRectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, BLUE);
            DrawText("ENDING SCREEN", 20, 20, 40, DARKBLUE);
            DrawText("PRESS ENTER or TAP to RETURN to TITLE SCREEN", 120, 220, 20, DARKBLUE);
        } break;
        default: break;
        }

        EndDrawing();
    }

    // Unload resources

    return 0;
}

ball.cpp =>

#include "ball.h"
#include <raylib.h>
#include <math.h>
#include "variables.h"
#include "title_screen.h"
#include <string>
#include <bricks.h>

TITLE_SCREEN ts;

Vector2 ball_position = { 300, 750 }; // Initial ball position.
Texture2D ball_texture; // Texture for the ball.
Bricks bricks;
Vector2 cursorPos;

// Velocity vector for ball movement
Vector2 ball_velocity = { 0, 0 };

void Ball::InitializeTexture()
{
    // Load the ball texture.
    ball_texture = LoadTexture("resources/ball.png");
}

Rectangle buttonBounds = ts.getButtonBounds();

void Ball::drawBall()
{
    ClearBackground(BLACK);
    DrawLine(0, 760, GetScreenWidth(), 760, RED);
    /*DrawTexturePro(ball_texture, Rectangle{0, 0, (float)ball_texture.width, (float)ball_texture.height},
        Rectangle{ ball_position.x, ball_position.y, 30, 30 }, // Adjust the size as needed
        Vector2{ 15, 15 }, 90, WHITE); // Apply rotation
        */
    DrawCircle(ball_position.x, ball_position.y, 10, WHITE);
}
void Ball::updateBall()
{
    static bool clicked = false;
 
    // Update ball position
    if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && !clicked)
    {
        Vector2 cursorPos = GetMousePosition();
        clicked = true;


        ball_velocity.x = cursorPos.x - ball_position.x;
        ball_velocity.y = cursorPos.y - ball_position.y;

        float magnitude = sqrt(ball_velocity.x * ball_velocity.x + ball_velocity.y * ball_velocity.y);
        if (magnitude != 0) {
            ball_velocity.x /= magnitude;
            ball_velocity.y /= magnitude;
        }

        // Set the velocity based on the normalized direction
        ball_velocity.x *= BALL_SPEED;
        ball_velocity.y *= BALL_SPEED;

        click_counter++;
    }

    if (clicked)
    {
        // Update the ball position with the velocity
        ball_position.x += ball_velocity.x * GetFrameTime();
        ball_position.y += ball_velocity.y * GetFrameTime();

        // Check collision with vertical edges of the screen and reverse direction if needed
        if (ball_position.x >= GetScreenWidth() - ball_texture.width || ball_position.x <= 0)
        {
            ball_velocity.x *= -1;
        }

        // Check collision with horizontal edges of the screen and reverse direction if needed
        if (ball_position.y <= 0)
        {
            ball_velocity.y *= -1;
        }
        if (ball_position.y >= GetScreenHeight() - ball_texture.height - 150)
        {
            ball_position = {300,750};
            clicked = false;
        }

    }
}

//These functions used to control ball from bricks.cpp


Vector2 Ball::getPosition()
{
    return ball_position;
}

Vector2 Ball::getSpeed() {
    return ball_velocity;
}

float Ball::getSpeedX()
{
    return ball_velocity.x;
}

float Ball::getSpeedY()
{
    return ball_velocity.y;
}

void Ball::setSpeed(Vector2 speed) // Used to change direction when there is a collision.
{
    ball_velocity = speed;
}

void Ball::deleteTexture() // Unloads texture if you use one.
{
    UnloadTexture(ball_texture);
}

void Ball::drawScore()
{
    std::string scoreText = "Click Time: ";
    // std::string scoreText = std::to_string(click_counter);
    scoreText.append(std::to_string(click_counter));
    DrawText(scoreText.c_str(), GetScreenWidth() / 2 - 65, GetScreenHeight() / 2 - 420, 20, WHITE);
}

void Ball::setBallPosition()
{
    ball_position = { 300,750 };
}

I tried adding isGameOver checks but i still can't fix the problem when player reachs the maximum click amount(10).


Solution

  • I solved the problem with changing the velocity of ball to {0,0}.

    I created a function that resets the ball and called it when isGameOver is equal to true:

    void Ball::resetBall() 
    {
        ball_position = { 300, 750 }; // Reset ball position
        ball_velocity = { 0, 0 }; // Reset ball velocity
        clicked = false; // Reset click flag
    }