mirror of
				https://github.com/lov3b/Pong.git
				synced 2025-11-03 23:00:19 +01:00 
			
		
		
		
	Merge all text elements + endscreen working
This commit is contained in:
		@@ -32,10 +32,10 @@ add_executable(Pong src/main.cpp
 | 
			
		||||
        src/Vec2d/Bump.h
 | 
			
		||||
        src/VisibleObjects/PlayerPaddle.h
 | 
			
		||||
        src/VisibleObjects/Side.h
 | 
			
		||||
        src/VisibleObjects/Score.h
 | 
			
		||||
        src/TextScreen.h
 | 
			
		||||
        src/text/TextScreen.h
 | 
			
		||||
        src/defaultfont.h
 | 
			
		||||
        src/OptionScreen.h)
 | 
			
		||||
        src/text/OptionScreen.h
 | 
			
		||||
        src/text/Score.h)
 | 
			
		||||
 | 
			
		||||
# Now link the libraries to the target
 | 
			
		||||
target_link_libraries(Pong ${SDL2_LIBRARIES} ${SDL2_GFX_LIBRARY} ${SDL2_TTF_LIBRARY})
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								src/Game.h
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								src/Game.h
									
									
									
									
									
								
							@@ -8,9 +8,10 @@
 | 
			
		||||
#include "SDL.h"
 | 
			
		||||
#include "VisibleObjects/Ball.h"
 | 
			
		||||
#include "VisibleObjects/PlayerPaddle.h"
 | 
			
		||||
#include "VisibleObjects/Score.h"
 | 
			
		||||
#include "TextScreen.h"
 | 
			
		||||
#include "OptionScreen.h"
 | 
			
		||||
#include "text/Score.h"
 | 
			
		||||
#include "text/TextScreen.h"
 | 
			
		||||
#include "text/OptionScreen.h"
 | 
			
		||||
#include "text/Score.h"
 | 
			
		||||
 | 
			
		||||
enum class GameState {
 | 
			
		||||
    START_SCREEN, GAME, END_SCREEN
 | 
			
		||||
@@ -36,7 +37,7 @@ public:
 | 
			
		||||
            std::cout << "Player " << player << " won" << std::endl;
 | 
			
		||||
            this->running = false;
 | 
			
		||||
        };
 | 
			
		||||
        score = new Score(5, &this->screenSize, func);
 | 
			
		||||
        score = new Score(&this->screenSize, 5, func);
 | 
			
		||||
        ball = new Ball(&this->screenSize, leftPaddle, rightPaddle, score);
 | 
			
		||||
        startScreen = new OptionScreen("Welcome to Pong!\nPress any key to get started...", &this->screenSize, 4);
 | 
			
		||||
        endScreen = nullptr;
 | 
			
		||||
@@ -67,6 +68,7 @@ public:
 | 
			
		||||
                rightPaddle->draw(renderer);
 | 
			
		||||
                break;
 | 
			
		||||
            case GameState::END_SCREEN:
 | 
			
		||||
                endScreen->draw(renderer);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -87,13 +89,18 @@ public:
 | 
			
		||||
                leftPaddle->update();
 | 
			
		||||
                rightPaddle->update();
 | 
			
		||||
                score->update();
 | 
			
		||||
 | 
			
		||||
                if (score->sideWon().has_value()) {
 | 
			
		||||
                    const char *player = score->sideWon().value() == Side::LEFT ? "left" : "right";
 | 
			
		||||
                    std::stringstream ss;
 | 
			
		||||
                    ss << "The " << player << " player won with " << std::to_string(score->leftScore) << " - " << std::to_string(score->rightScore)
 | 
			
		||||
                       << "\nWould you like to play again?" << "\nIf so, press any button...";
 | 
			
		||||
                    score->resetScore();
 | 
			
		||||
                    endScreen = new OptionScreen(ss.str(), &screenSize, 4);
 | 
			
		||||
                    gameState = GameState::END_SCREEN;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case GameState::END_SCREEN:
 | 
			
		||||
                if (endScreen == nullptr) {
 | 
			
		||||
                    std::stringstream ss;
 | 
			
		||||
                    ss << "Player " << " won" << "\nWould you like to play again?" << "\nIf so, press any button...";
 | 
			
		||||
                    endScreen = new OptionScreen(ss.str(), &screenSize, 4);
 | 
			
		||||
                }
 | 
			
		||||
                endScreen->update();
 | 
			
		||||
                if (endScreen->isDone()) {
 | 
			
		||||
                    gameState = GameState::GAME;
 | 
			
		||||
@@ -115,14 +122,15 @@ public:
 | 
			
		||||
                    if (event.type == SDL_KEYDOWN && !startScreen->hasStartedCounting())
 | 
			
		||||
                        startScreen->startCountDown();
 | 
			
		||||
 | 
			
		||||
                    return true;
 | 
			
		||||
                    break;
 | 
			
		||||
                case GameState::GAME:
 | 
			
		||||
                    handleGameEvent(event);
 | 
			
		||||
                    return true;
 | 
			
		||||
                    break;
 | 
			
		||||
                case GameState::END_SCREEN:
 | 
			
		||||
                    if (event.type == SDL_KEYDOWN && !endScreen->hasStartedCounting())
 | 
			
		||||
                        endScreen->startCountDown();
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,8 @@
 | 
			
		||||
#include <SDL2/SDL2_gfxPrimitives.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include "optional"
 | 
			
		||||
#include "Score.h"
 | 
			
		||||
#include "../text/Score.h"
 | 
			
		||||
#include "../text/Score.h"
 | 
			
		||||
 | 
			
		||||
class Ball {
 | 
			
		||||
private:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,128 +0,0 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by love on 2024-01-19.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <SDL_render.h>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include "Side.h"
 | 
			
		||||
#include "SDL_ttf.h"
 | 
			
		||||
#include "../defaultfont.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <SDL_ttf.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Score {
 | 
			
		||||
private:
 | 
			
		||||
    uint8_t leftScore, rightScore;
 | 
			
		||||
    const uint8_t MAX_SCORE;
 | 
			
		||||
    const std::function<void(Side)> whenWon;
 | 
			
		||||
    TTF_Font *font;
 | 
			
		||||
    bool hasIncremented = false;
 | 
			
		||||
 | 
			
		||||
    // Regular
 | 
			
		||||
    SDL_Rect position;
 | 
			
		||||
    SDL_Color color = {243, 156, 18, 255};
 | 
			
		||||
    SDL_Surface *surface = nullptr;
 | 
			
		||||
 | 
			
		||||
    // Shadow
 | 
			
		||||
    const SDL_Color shadowColor = {243, 156, 18, 100};
 | 
			
		||||
    SDL_Surface *shadowSurface = nullptr;
 | 
			
		||||
    const int shadowOffset = 3;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit Score(uint8_t max_score, SDL_Point *screenSize, const std::function<void(Side)> &whenWon) : MAX_SCORE(
 | 
			
		||||
            max_score), whenWon(whenWon) {
 | 
			
		||||
        resetScore();
 | 
			
		||||
        if (defaultFontPath == nullptr) {
 | 
			
		||||
            std::cerr << "Font path is not set for this platform (null)" << std::endl;
 | 
			
		||||
            exit(-1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this->font = TTF_OpenFont(defaultFontPath, 42);
 | 
			
		||||
        if (font == nullptr) {
 | 
			
		||||
            std::cerr << "Failed to load font: " << TTF_GetError() << std::endl;
 | 
			
		||||
            exit(-1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this->position = SDL_Rect{screenSize->x / 2 - 50, 10, 100, 50};
 | 
			
		||||
        this->rightScore = this->leftScore = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~Score() {
 | 
			
		||||
        if (font)
 | 
			
		||||
            TTF_CloseFont(font);
 | 
			
		||||
        SDL_FreeSurface(surface);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void update() {
 | 
			
		||||
        if (!hasIncremented && surface != nullptr && shadowSurface != nullptr) return;
 | 
			
		||||
 | 
			
		||||
        SDL_FreeSurface(surface);
 | 
			
		||||
        SDL_FreeSurface(shadowSurface);
 | 
			
		||||
 | 
			
		||||
        hasIncremented = false;
 | 
			
		||||
        char score_text[8];
 | 
			
		||||
        sprintf(score_text, "%d - %d", leftScore, rightScore);
 | 
			
		||||
 | 
			
		||||
        // Create shadow surface
 | 
			
		||||
        shadowSurface = TTF_RenderText_Solid(font, score_text, shadowColor);
 | 
			
		||||
        if (shadowSurface == nullptr) {
 | 
			
		||||
            std::cerr << "Failed to create shadow text surface: " << TTF_GetError() << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Create main text surface
 | 
			
		||||
        surface = TTF_RenderText_Solid(font, score_text, color);
 | 
			
		||||
        if (surface == nullptr) {
 | 
			
		||||
            std::cerr << "Failed to create text surface: " << TTF_GetError() << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void draw(SDL_Renderer *renderer) {
 | 
			
		||||
        // Draw shadow
 | 
			
		||||
        if (shadowSurface != nullptr) {
 | 
			
		||||
            SDL_Texture *shadowTexture = SDL_CreateTextureFromSurface(renderer, shadowSurface);
 | 
			
		||||
            if (shadowTexture != nullptr) {
 | 
			
		||||
                SDL_Rect shadowPosition = {position.x + shadowOffset, position.y + shadowOffset, position.w,
 | 
			
		||||
                                           position.h};
 | 
			
		||||
                SDL_RenderCopy(renderer, shadowTexture, nullptr, &shadowPosition);
 | 
			
		||||
                SDL_DestroyTexture(shadowTexture);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Draw text
 | 
			
		||||
        if (surface != nullptr) {
 | 
			
		||||
            SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
 | 
			
		||||
            if (texture != nullptr) {
 | 
			
		||||
                SDL_RenderCopy(renderer, texture, nullptr, &position);
 | 
			
		||||
                SDL_DestroyTexture(texture);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resetScore() {
 | 
			
		||||
        leftScore = rightScore = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void incrementScore(const Side side) {
 | 
			
		||||
        hasIncremented = true;
 | 
			
		||||
        uint8_t temp;
 | 
			
		||||
        switch (side) {
 | 
			
		||||
            case Side::LEFT:
 | 
			
		||||
                temp = ++leftScore;
 | 
			
		||||
                break;
 | 
			
		||||
            case Side::RIGHT:
 | 
			
		||||
                temp = ++rightScore;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (temp > MAX_SCORE)
 | 
			
		||||
            whenWon(side);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
@@ -22,6 +22,7 @@ private:
 | 
			
		||||
    int_least64_t nextMsEpoch = 0;
 | 
			
		||||
    int stepsToDo, stepsDone = 0;
 | 
			
		||||
    bool isDone_ = false;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    [[nodiscard]] const bool &isDone() const {
 | 
			
		||||
        return isDone_;
 | 
			
		||||
@@ -32,8 +33,9 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    OptionScreen(const std::string &text, SDL_Point *screenSize, int seconds)
 | 
			
		||||
            : TextScreen(text, screenSize), stepsToDo(seconds) {
 | 
			
		||||
    OptionScreen(const std::string &text, SDL_Point *screenSize, int seconds) : TextScreen(text, screenSize,
 | 
			
		||||
                                                                                           std::nullopt),
 | 
			
		||||
                                                                                stepsToDo(seconds) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void update() override {
 | 
			
		||||
							
								
								
									
										71
									
								
								src/text/Score.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/text/Score.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by Love on 2024-01-29.
 | 
			
		||||
//
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "TextScreen.h"
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <SDL_render.h>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include "../VisibleObjects/Side.h"
 | 
			
		||||
#include "SDL_ttf.h"
 | 
			
		||||
#include "../defaultfont.h"
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <optional>
 | 
			
		||||
 | 
			
		||||
class Score : public TextScreen {
 | 
			
		||||
private:
 | 
			
		||||
    const uint8_t MAX_SCORE;
 | 
			
		||||
    const std::function<void(Side)> whenWon;
 | 
			
		||||
    std::optional<Side> sideWon_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    uint8_t leftScore, rightScore;
 | 
			
		||||
 | 
			
		||||
    [[nodiscard]] const std::optional<Side> &sideWon() const {
 | 
			
		||||
        return sideWon_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit Score(SDL_Point *screenSize, uint8_t max_score, const std::function<void(Side)> &whenWon) : MAX_SCORE(
 | 
			
		||||
            max_score), whenWon(whenWon), leftScore(0), rightScore(0), TextScreen("", screenSize, std::make_optional(
 | 
			
		||||
            SDL_Point{screenSize->x / 2 - 50, 10})) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void update() override {
 | 
			
		||||
        if (hasUpdated) return;
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << std::to_string(leftScore) << " - " << std::to_string(rightScore);
 | 
			
		||||
        setText(ss.str());
 | 
			
		||||
        TextScreen::update();
 | 
			
		||||
 | 
			
		||||
        hasUpdated = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resetScore() {
 | 
			
		||||
        leftScore = rightScore = 0;
 | 
			
		||||
        sideWon_ = std::nullopt;
 | 
			
		||||
        hasUpdated = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void incrementScore(const Side side) {
 | 
			
		||||
        hasUpdated = false;
 | 
			
		||||
        uint8_t incrementedScore;
 | 
			
		||||
        switch (side) {
 | 
			
		||||
            case Side::LEFT:
 | 
			
		||||
                incrementedScore = ++leftScore;
 | 
			
		||||
                break;
 | 
			
		||||
            case Side::RIGHT:
 | 
			
		||||
                incrementedScore = ++rightScore;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (incrementedScore > MAX_SCORE)
 | 
			
		||||
            sideWon_ = side;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -8,28 +8,30 @@
 | 
			
		||||
#include <SDL_ttf.h>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "defaultfont.h"
 | 
			
		||||
#include "../defaultfont.h"
 | 
			
		||||
#include "iostream"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TextScreen {
 | 
			
		||||
private:
 | 
			
		||||
    std::vector<std::string> lines;
 | 
			
		||||
    std::vector<SDL_Surface *> surfaces;
 | 
			
		||||
    std::vector<SDL_Surface *> shadowSurfaces;
 | 
			
		||||
    TTF_Font *font;
 | 
			
		||||
    SDL_Point *screenSize;
 | 
			
		||||
    std::optional<SDL_Point> basePosition;
 | 
			
		||||
 | 
			
		||||
    // Regular
 | 
			
		||||
    std::vector<SDL_Rect> positions;
 | 
			
		||||
    SDL_Color color = {243, 156, 18, 255};
 | 
			
		||||
    std::vector<SDL_Surface *> surfaces;
 | 
			
		||||
 | 
			
		||||
    // Shadow
 | 
			
		||||
    std::vector<SDL_Rect> shadowPositions;
 | 
			
		||||
    const SDL_Color shadowColor = {243, 156, 18, 100};
 | 
			
		||||
    std::vector<SDL_Surface *> shadowSurfaces;
 | 
			
		||||
    const int shadowOffset = 3;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    std::vector<std::string> lines;
 | 
			
		||||
    bool hasUpdated;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@@ -38,7 +40,8 @@ public:
 | 
			
		||||
     * @param text This class takes care of freeing text
 | 
			
		||||
     * @param screenSize This won't be freed by this class
 | 
			
		||||
     */
 | 
			
		||||
    TextScreen(const std::string &text, SDL_Point *screenSize) : hasUpdated(false), screenSize(screenSize) {
 | 
			
		||||
    TextScreen(const std::string &text, SDL_Point *screenSize, std::optional<SDL_Point> basePosition) : hasUpdated(
 | 
			
		||||
            false), screenSize(screenSize), basePosition(basePosition) {
 | 
			
		||||
        if (defaultFontPath == nullptr) {
 | 
			
		||||
            std::cerr << "Font path is not set for this platform (null)" << std::endl;
 | 
			
		||||
            exit(-1);
 | 
			
		||||
@@ -69,13 +72,12 @@ private:
 | 
			
		||||
            int textWidth, textHeight;
 | 
			
		||||
            TTF_SizeText(font, lines[i].c_str(), &textWidth, &textHeight);
 | 
			
		||||
 | 
			
		||||
            int baseX = (screenSize->x - textWidth) / 2, baseY = (screenSize->y - textHeight * (lines.size())) / 2;
 | 
			
		||||
            SDL_Rect regularPosition = {baseX,
 | 
			
		||||
                                        baseY + textHeight * i,
 | 
			
		||||
                                        textWidth, textHeight};
 | 
			
		||||
            SDL_Rect shadowPosition = {baseX + shadowOffset,
 | 
			
		||||
                                       baseY + textHeight * i + shadowOffset,
 | 
			
		||||
                                       textWidth,
 | 
			
		||||
            SDL_Point base = basePosition.has_value() ? basePosition.value() : SDL_Point{
 | 
			
		||||
                    (screenSize->x - textWidth) / 2,
 | 
			
		||||
                    static_cast<int>((screenSize->y - textHeight * (lines.size())) / 2)};
 | 
			
		||||
 | 
			
		||||
            SDL_Rect regularPosition = {base.x, base.y + textHeight * i, textWidth, textHeight};
 | 
			
		||||
            SDL_Rect shadowPosition = {base.x + shadowOffset, base.y + textHeight * i + shadowOffset, textWidth,
 | 
			
		||||
                                       textHeight};
 | 
			
		||||
            positions.push_back(regularPosition);
 | 
			
		||||
            shadowPositions.push_back(shadowPosition);
 | 
			
		||||
		Reference in New Issue
	
	Block a user