From 5c87e2d918d9a1d26f7a2b2a45c531b7efb12929 Mon Sep 17 00:00:00 2001 From: Love Billenius Date: Mon, 29 Jan 2024 00:43:01 +0100 Subject: [PATCH] start screen text --- src/Game.h | 91 ++++++++++++++++++++------------- src/SdlWrapper.h | 5 +- src/TextScreen.h | 128 +++++++++++++++++++++++++++++++++-------------- 3 files changed, 149 insertions(+), 75 deletions(-) diff --git a/src/Game.h b/src/Game.h index ffb6173..8e8ca27 100644 --- a/src/Game.h +++ b/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; } }; \ No newline at end of file diff --git a/src/SdlWrapper.h b/src/SdlWrapper.h index 105cefa..a3d7447 100644 --- a/src/SdlWrapper.h +++ b/src/SdlWrapper.h @@ -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); } diff --git a/src/TextScreen.h b/src/TextScreen.h index f226d95..ae51864 100644 --- a/src/TextScreen.h +++ b/src/TextScreen.h @@ -6,23 +6,27 @@ #include #include +#include +#include #include "defaultfont.h" #include "iostream" + class TextScreen { private: - std::string *text; + std::vector lines; TTF_Font *font; bool hasUpdated; // Regular - SDL_Rect position; + std::vector positions; SDL_Color color = {243, 156, 18, 255}; - SDL_Surface *surface = nullptr; + std::vector surfaces; // Shadow + std::vector shadowPositions; const SDL_Color shadowColor = {243, 156, 18, 100}; - SDL_Surface *shadowSurface = nullptr; + std::vector 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 splitString(const std::string &string, const char delim) { + int size = 0; + for (char c: string) + if (c == delim) size++; + std::vector lines; + lines.reserve(size); + + std::stringstream ss(string); + std::string line; + while (std::getline(ss, line, delim)) + lines.push_back(line); + + + return lines; } - };