mirror of
				https://github.com/lov3b/Pong.git
				synced 2025-11-04 07:10:16 +01:00 
			
		
		
		
	start screen text
This commit is contained in:
		
							
								
								
									
										91
									
								
								src/Game.h
									
									
									
									
									
								
							
							
						
						
									
										91
									
								
								src/Game.h
									
									
									
									
									
								
							@@ -9,6 +9,7 @@
 | 
			
		||||
#include "VisibleObjects/Ball.h"
 | 
			
		||||
#include "VisibleObjects/PlayerPaddle.h"
 | 
			
		||||
#include "VisibleObjects/Score.h"
 | 
			
		||||
#include "TextScreen.h"
 | 
			
		||||
 | 
			
		||||
enum class GameState {
 | 
			
		||||
    START_SCREEN, GAME, END_SCREEN
 | 
			
		||||
@@ -19,6 +20,7 @@ private:
 | 
			
		||||
    Ball *ball;
 | 
			
		||||
    Score *score;
 | 
			
		||||
    PlayerPaddle *leftPaddle, *rightPaddle;
 | 
			
		||||
    TextScreen *startScreen, *endScreen;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    GameState gameState;
 | 
			
		||||
@@ -35,6 +37,8 @@ public:
 | 
			
		||||
        };
 | 
			
		||||
        score = new Score(5, &this->screenSize, func);
 | 
			
		||||
        ball = new Ball(&this->screenSize, leftPaddle, rightPaddle, score);
 | 
			
		||||
        startScreen = new TextScreen("Welcome to Pong!\nPress any key to get started...", &this->screenSize);
 | 
			
		||||
        endScreen = new TextScreen("", &this->screenSize);
 | 
			
		||||
        gameState = GameState::START_SCREEN;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -53,6 +57,7 @@ public:
 | 
			
		||||
 | 
			
		||||
        switch (gameState) {
 | 
			
		||||
            case GameState::START_SCREEN:
 | 
			
		||||
                startScreen->draw(renderer);
 | 
			
		||||
                break;
 | 
			
		||||
            case GameState::GAME:
 | 
			
		||||
                ball->draw(renderer);
 | 
			
		||||
@@ -67,9 +72,10 @@ public:
 | 
			
		||||
        SDL_RenderPresent(renderer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool update() override {
 | 
			
		||||
    void update() override {
 | 
			
		||||
        switch (gameState) {
 | 
			
		||||
            case GameState::START_SCREEN:
 | 
			
		||||
                startScreen->update();
 | 
			
		||||
                break;
 | 
			
		||||
            case GameState::GAME:
 | 
			
		||||
                ball->update();
 | 
			
		||||
@@ -80,52 +86,67 @@ public:
 | 
			
		||||
            case GameState::END_SCREEN:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool handleEvents() override {
 | 
			
		||||
        SDL_Event event;
 | 
			
		||||
        while (SDL_PollEvent(&event) != 0) {
 | 
			
		||||
 | 
			
		||||
        while (SDL_PollEvent(&event)) {
 | 
			
		||||
            if (event.type == SDL_QUIT)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            if (event.type == SDL_KEYDOWN) {
 | 
			
		||||
                switch (event.key.keysym.sym) {
 | 
			
		||||
                    case SDLK_w:
 | 
			
		||||
                        leftPaddle->startMoving(true);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case SDLK_s:
 | 
			
		||||
                        leftPaddle->startMoving(false);
 | 
			
		||||
                        break;
 | 
			
		||||
            switch (gameState) {
 | 
			
		||||
                case GameState::START_SCREEN:
 | 
			
		||||
                    if (event.type == SDL_KEYDOWN)
 | 
			
		||||
                        gameState = GameState::GAME;
 | 
			
		||||
                    return true;
 | 
			
		||||
                case GameState::GAME:
 | 
			
		||||
                    handleGameEvent(event);
 | 
			
		||||
                    return true;
 | 
			
		||||
                case GameState::END_SCREEN:
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                    case SDLK_UP:
 | 
			
		||||
                        rightPaddle->startMoving(true);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case SDLK_DOWN:
 | 
			
		||||
                        rightPaddle->startMoving(false);
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (event.type == SDL_KEYUP) {
 | 
			
		||||
                switch (event.key.keysym.sym) {
 | 
			
		||||
                    case SDLK_w:
 | 
			
		||||
                        leftPaddle->stopMoving(true);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case SDLK_s:
 | 
			
		||||
                        leftPaddle->stopMoving(false);
 | 
			
		||||
                        break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
                    case SDLK_UP:
 | 
			
		||||
                        rightPaddle->stopMoving(true);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case SDLK_DOWN:
 | 
			
		||||
                        rightPaddle->stopMoving(false);
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handleGameEvent(SDL_Event &event) {
 | 
			
		||||
        if (event.type == SDL_KEYDOWN) {
 | 
			
		||||
            switch (event.key.keysym.sym) {
 | 
			
		||||
                case SDLK_w:
 | 
			
		||||
                    leftPaddle->startMoving(true);
 | 
			
		||||
                    break;
 | 
			
		||||
                case SDLK_s:
 | 
			
		||||
                    leftPaddle->startMoving(false);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case SDLK_UP:
 | 
			
		||||
                    rightPaddle->startMoving(true);
 | 
			
		||||
                    break;
 | 
			
		||||
                case SDLK_DOWN:
 | 
			
		||||
                    rightPaddle->startMoving(false);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.type == SDL_KEYUP) {
 | 
			
		||||
            switch (event.key.keysym.sym) {
 | 
			
		||||
                case SDLK_w:
 | 
			
		||||
                    leftPaddle->stopMoving(true);
 | 
			
		||||
                    break;
 | 
			
		||||
                case SDLK_s:
 | 
			
		||||
                    leftPaddle->stopMoving(false);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case SDLK_UP:
 | 
			
		||||
                    rightPaddle->stopMoving(true);
 | 
			
		||||
                    break;
 | 
			
		||||
                case SDLK_DOWN:
 | 
			
		||||
                    rightPaddle->stopMoving(false);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -53,14 +53,15 @@ public:
 | 
			
		||||
 | 
			
		||||
    virtual void draw(SDL_Renderer *renderer) = 0;
 | 
			
		||||
 | 
			
		||||
    virtual bool update() = 0;
 | 
			
		||||
    virtual void update() = 0;
 | 
			
		||||
 | 
			
		||||
    virtual bool handleEvents() = 0;
 | 
			
		||||
 | 
			
		||||
    int loop() {
 | 
			
		||||
        while (running) {
 | 
			
		||||
            if (!handleEvents() || !update())
 | 
			
		||||
            if (!handleEvents())
 | 
			
		||||
                break;
 | 
			
		||||
            update();
 | 
			
		||||
            draw(renderer);
 | 
			
		||||
            SDL_Delay(1000 / fps);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										128
									
								
								src/TextScreen.h
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								src/TextScreen.h
									
									
									
									
									
								
							@@ -6,23 +6,27 @@
 | 
			
		||||
 | 
			
		||||
#include <SDL_render.h>
 | 
			
		||||
#include <SDL_ttf.h>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "defaultfont.h"
 | 
			
		||||
#include "iostream"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TextScreen {
 | 
			
		||||
private:
 | 
			
		||||
    std::string *text;
 | 
			
		||||
    std::vector<std::string> lines;
 | 
			
		||||
    TTF_Font *font;
 | 
			
		||||
    bool hasUpdated;
 | 
			
		||||
 | 
			
		||||
    // Regular
 | 
			
		||||
    SDL_Rect position;
 | 
			
		||||
    std::vector<SDL_Rect> positions;
 | 
			
		||||
    SDL_Color color = {243, 156, 18, 255};
 | 
			
		||||
    SDL_Surface *surface = nullptr;
 | 
			
		||||
    std::vector<SDL_Surface *> surfaces;
 | 
			
		||||
 | 
			
		||||
    // Shadow
 | 
			
		||||
    std::vector<SDL_Rect> shadowPositions;
 | 
			
		||||
    const SDL_Color shadowColor = {243, 156, 18, 100};
 | 
			
		||||
    SDL_Surface *shadowSurface = nullptr;
 | 
			
		||||
    std::vector<SDL_Surface *> shadowSurfaces;
 | 
			
		||||
    const int shadowOffset = 3;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@@ -31,7 +35,7 @@ public:
 | 
			
		||||
     * @param text This class takes care of freeing text
 | 
			
		||||
     * @param screenSize This won't be freed by this class
 | 
			
		||||
     */
 | 
			
		||||
    TextScreen(std::string *text, SDL_Point *screenSize) : text(text), hasUpdated(false) {
 | 
			
		||||
    TextScreen(const std::string& text, SDL_Point *screenSize) : hasUpdated(false) {
 | 
			
		||||
        if (defaultFontPath == nullptr) {
 | 
			
		||||
            std::cerr << "Font path is not set for this platform (null)" << std::endl;
 | 
			
		||||
            exit(-1);
 | 
			
		||||
@@ -41,74 +45,122 @@ public:
 | 
			
		||||
            std::cerr << "Failed to load font: " << TTF_GetError() << std::endl;
 | 
			
		||||
            exit(-1);
 | 
			
		||||
        }
 | 
			
		||||
        this->position = SDL_Rect{50, 50, screenSize->x - 50, screenSize->y - 50};
 | 
			
		||||
 | 
			
		||||
        lines = splitString(text, '\n');
 | 
			
		||||
        surfaces.reserve(lines.size());
 | 
			
		||||
        shadowSurfaces.reserve(lines.size());
 | 
			
		||||
        positions.reserve(lines.size());
 | 
			
		||||
        shadowPositions.reserve(lines.size());
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < lines.size(); ++i) {
 | 
			
		||||
            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,
 | 
			
		||||
                                       textHeight};
 | 
			
		||||
            positions.push_back(regularPosition);
 | 
			
		||||
            shadowPositions.push_back(shadowPosition);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~TextScreen() {
 | 
			
		||||
        if (font)
 | 
			
		||||
            TTF_CloseFont(font);
 | 
			
		||||
        SDL_FreeSurface(surface);
 | 
			
		||||
        delete text;
 | 
			
		||||
        for (auto *surface: surfaces)
 | 
			
		||||
            SDL_FreeSurface(surface);
 | 
			
		||||
        for (auto *surface: shadowSurfaces)
 | 
			
		||||
            SDL_FreeSurface(surface);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void draw(SDL_Renderer *renderer) {
 | 
			
		||||
        // Draw shadow
 | 
			
		||||
        if (shadowSurface != nullptr) {
 | 
			
		||||
            SDL_Texture *shadowTexture = SDL_CreateTextureFromSurface(renderer, shadowSurface);
 | 
			
		||||
        for (int i = 0; i < surfaces.size(); ++i) {
 | 
			
		||||
            // Draw shadow
 | 
			
		||||
            SDL_Texture *shadowTexture = SDL_CreateTextureFromSurface(renderer, shadowSurfaces[i]);
 | 
			
		||||
            if (shadowTexture != nullptr) {
 | 
			
		||||
                SDL_Rect shadowPosition = {position.x + shadowOffset, position.y + shadowOffset, position.w,
 | 
			
		||||
                                           position.h};
 | 
			
		||||
                SDL_RenderCopy(renderer, shadowTexture, nullptr, &shadowPosition);
 | 
			
		||||
                SDL_RenderCopy(renderer, shadowTexture, nullptr, &shadowPositions[i]);
 | 
			
		||||
                SDL_DestroyTexture(shadowTexture);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Draw text
 | 
			
		||||
        if (surface != nullptr) {
 | 
			
		||||
            SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
 | 
			
		||||
            // Draw text
 | 
			
		||||
            SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surfaces[i]);
 | 
			
		||||
            if (texture != nullptr) {
 | 
			
		||||
                SDL_RenderCopy(renderer, texture, nullptr, &position);
 | 
			
		||||
                SDL_RenderCopy(renderer, texture, nullptr, &positions[i]);
 | 
			
		||||
                SDL_DestroyTexture(texture);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setText(std::string *replaceText) {
 | 
			
		||||
        delete this->text;
 | 
			
		||||
        this->text = replaceText;
 | 
			
		||||
    void setText(std::string &replaceText) {
 | 
			
		||||
        lines = splitString(replaceText, '\n');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void replaceCharAtIndex(char c, int index) {
 | 
			
		||||
        if (text->length() <= index) {
 | 
			
		||||
            std::cerr << "text string is at length " << text->length() << ", but index is " << index << std::endl;
 | 
			
		||||
    void replaceCharAtIndex(char c, int line, int index) {
 | 
			
		||||
        if (lines.size() <= line) {
 | 
			
		||||
            if (lines[line].length() <= index) {
 | 
			
		||||
                std::cerr << "string lines is of length " << lines.size() << ", but line index is " << index
 | 
			
		||||
                          << std::endl;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (lines[line].length() <= index) {
 | 
			
		||||
            std::cerr << "text string is of length " << lines[line].length() << ", but index is " << index << std::endl;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        hasUpdated = false;
 | 
			
		||||
        text[index] = c;
 | 
			
		||||
        lines[line][index] = c;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void update() {
 | 
			
		||||
        if (!hasUpdated)
 | 
			
		||||
        if (hasUpdated)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        SDL_FreeSurface(surface);
 | 
			
		||||
        SDL_FreeSurface(shadowSurface);
 | 
			
		||||
        for (auto &surface: surfaces)
 | 
			
		||||
            SDL_FreeSurface(surface);
 | 
			
		||||
        for (auto &shadowSurface: shadowSurfaces)
 | 
			
		||||
            SDL_FreeSurface(shadowSurface);
 | 
			
		||||
        surfaces.clear();
 | 
			
		||||
        shadowSurfaces.clear();
 | 
			
		||||
 | 
			
		||||
        shadowSurface = TTF_RenderText_Solid(font, text->c_str(), shadowColor);
 | 
			
		||||
        if (shadowSurface == nullptr)
 | 
			
		||||
            std::cerr << "Failed to create shadow text surface (TextScreen): " << TTF_GetError() << std::endl;
 | 
			
		||||
        for (const auto &line: lines) {
 | 
			
		||||
            SDL_Surface *textSurface = TTF_RenderText_Solid(font, line.c_str(), color);
 | 
			
		||||
            SDL_Surface *shadowSurface = TTF_RenderText_Solid(font, line.c_str(), shadowColor);
 | 
			
		||||
 | 
			
		||||
        surface = TTF_RenderText_Solid(font, text->c_str(), color);
 | 
			
		||||
        if (surface == nullptr)
 | 
			
		||||
            std::cerr << "Failed to create text surface (TextScreen): " << TTF_GetError() << std::endl;
 | 
			
		||||
            if (textSurface == nullptr || shadowSurface == nullptr) {
 | 
			
		||||
                std::cerr << "Failed to create text surface (TextScreen): " << TTF_GetError() << std::endl;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            surfaces.push_back(textSurface);
 | 
			
		||||
            shadowSurfaces.push_back(shadowSurface);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        hasUpdated = true;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool gameContextFinished() {
 | 
			
		||||
private:
 | 
			
		||||
    static std::vector<std::string> splitString(const std::string &string, const char delim) {
 | 
			
		||||
        int size = 0;
 | 
			
		||||
        for (char c: string)
 | 
			
		||||
            if (c == delim) size++;
 | 
			
		||||
 | 
			
		||||
        std::vector<std::string> lines;
 | 
			
		||||
        lines.reserve(size);
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss(string);
 | 
			
		||||
        std::string line;
 | 
			
		||||
        while (std::getline(ss, line, delim))
 | 
			
		||||
            lines.push_back(line);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return lines;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user