start screen text

This commit is contained in:
Love 2024-01-29 00:43:01 +01:00
parent f958b4b5ec
commit 5c87e2d918
3 changed files with 149 additions and 75 deletions

View File

@ -9,6 +9,7 @@
#include "VisibleObjects/Ball.h" #include "VisibleObjects/Ball.h"
#include "VisibleObjects/PlayerPaddle.h" #include "VisibleObjects/PlayerPaddle.h"
#include "VisibleObjects/Score.h" #include "VisibleObjects/Score.h"
#include "TextScreen.h"
enum class GameState { enum class GameState {
START_SCREEN, GAME, END_SCREEN START_SCREEN, GAME, END_SCREEN
@ -19,6 +20,7 @@ private:
Ball *ball; Ball *ball;
Score *score; Score *score;
PlayerPaddle *leftPaddle, *rightPaddle; PlayerPaddle *leftPaddle, *rightPaddle;
TextScreen *startScreen, *endScreen;
protected: protected:
GameState gameState; GameState gameState;
@ -35,6 +37,8 @@ public:
}; };
score = new Score(5, &this->screenSize, func); score = new Score(5, &this->screenSize, func);
ball = new Ball(&this->screenSize, leftPaddle, rightPaddle, score); 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; gameState = GameState::START_SCREEN;
} }
@ -53,6 +57,7 @@ public:
switch (gameState) { switch (gameState) {
case GameState::START_SCREEN: case GameState::START_SCREEN:
startScreen->draw(renderer);
break; break;
case GameState::GAME: case GameState::GAME:
ball->draw(renderer); ball->draw(renderer);
@ -67,9 +72,10 @@ public:
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
} }
bool update() override { void update() override {
switch (gameState) { switch (gameState) {
case GameState::START_SCREEN: case GameState::START_SCREEN:
startScreen->update();
break; break;
case GameState::GAME: case GameState::GAME:
ball->update(); ball->update();
@ -80,16 +86,33 @@ public:
case GameState::END_SCREEN: case GameState::END_SCREEN:
break; break;
} }
return true;
} }
bool handleEvents() override { bool handleEvents() override {
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event) != 0) {
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) if (event.type == SDL_QUIT)
return false; return false;
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;
}
}
return true;
}
void handleGameEvent(SDL_Event &event) {
if (event.type == SDL_KEYDOWN) { if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) { switch (event.key.keysym.sym) {
case SDLK_w: case SDLK_w:
@ -121,11 +144,9 @@ public:
case SDLK_DOWN: case SDLK_DOWN:
rightPaddle->stopMoving(false); rightPaddle->stopMoving(false);
break; break;
}
} }
} }
return true;
} }
}; };

View File

@ -53,14 +53,15 @@ public:
virtual void draw(SDL_Renderer *renderer) = 0; virtual void draw(SDL_Renderer *renderer) = 0;
virtual bool update() = 0; virtual void update() = 0;
virtual bool handleEvents() = 0; virtual bool handleEvents() = 0;
int loop() { int loop() {
while (running) { while (running) {
if (!handleEvents() || !update()) if (!handleEvents())
break; break;
update();
draw(renderer); draw(renderer);
SDL_Delay(1000 / fps); SDL_Delay(1000 / fps);
} }

View File

@ -6,23 +6,27 @@
#include <SDL_render.h> #include <SDL_render.h>
#include <SDL_ttf.h> #include <SDL_ttf.h>
#include <utility>
#include <vector>
#include "defaultfont.h" #include "defaultfont.h"
#include "iostream" #include "iostream"
class TextScreen { class TextScreen {
private: private:
std::string *text; std::vector<std::string> lines;
TTF_Font *font; TTF_Font *font;
bool hasUpdated; bool hasUpdated;
// Regular // Regular
SDL_Rect position; std::vector<SDL_Rect> positions;
SDL_Color color = {243, 156, 18, 255}; SDL_Color color = {243, 156, 18, 255};
SDL_Surface *surface = nullptr; std::vector<SDL_Surface *> surfaces;
// Shadow // Shadow
std::vector<SDL_Rect> shadowPositions;
const SDL_Color shadowColor = {243, 156, 18, 100}; const SDL_Color shadowColor = {243, 156, 18, 100};
SDL_Surface *shadowSurface = nullptr; std::vector<SDL_Surface *> shadowSurfaces;
const int shadowOffset = 3; const int shadowOffset = 3;
public: public:
@ -31,7 +35,7 @@ public:
* @param text This class takes care of freeing text * @param text This class takes care of freeing text
* @param screenSize This won't be freed by this class * @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) { if (defaultFontPath == nullptr) {
std::cerr << "Font path is not set for this platform (null)" << std::endl; std::cerr << "Font path is not set for this platform (null)" << std::endl;
exit(-1); exit(-1);
@ -41,74 +45,122 @@ public:
std::cerr << "Failed to load font: " << TTF_GetError() << std::endl; std::cerr << "Failed to load font: " << TTF_GetError() << std::endl;
exit(-1); 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() { ~TextScreen() {
if (font) if (font)
TTF_CloseFont(font); TTF_CloseFont(font);
for (auto *surface: surfaces)
SDL_FreeSurface(surface);
for (auto *surface: shadowSurfaces)
SDL_FreeSurface(surface); SDL_FreeSurface(surface);
delete text;
} }
void draw(SDL_Renderer *renderer) { void draw(SDL_Renderer *renderer) {
for (int i = 0; i < surfaces.size(); ++i) {
// Draw shadow // Draw shadow
if (shadowSurface != nullptr) { SDL_Texture *shadowTexture = SDL_CreateTextureFromSurface(renderer, shadowSurfaces[i]);
SDL_Texture *shadowTexture = SDL_CreateTextureFromSurface(renderer, shadowSurface);
if (shadowTexture != nullptr) { if (shadowTexture != nullptr) {
SDL_Rect shadowPosition = {position.x + shadowOffset, position.y + shadowOffset, position.w, SDL_RenderCopy(renderer, shadowTexture, nullptr, &shadowPositions[i]);
position.h};
SDL_RenderCopy(renderer, shadowTexture, nullptr, &shadowPosition);
SDL_DestroyTexture(shadowTexture); SDL_DestroyTexture(shadowTexture);
} }
}
// Draw text // Draw text
if (surface != nullptr) { SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surfaces[i]);
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
if (texture != nullptr) { if (texture != nullptr) {
SDL_RenderCopy(renderer, texture, nullptr, &position); SDL_RenderCopy(renderer, texture, nullptr, &positions[i]);
SDL_DestroyTexture(texture); SDL_DestroyTexture(texture);
} }
} }
} }
void setText(std::string *replaceText) { void setText(std::string &replaceText) {
delete this->text; lines = splitString(replaceText, '\n');
this->text = replaceText;
} }
void replaceCharAtIndex(char c, int index) { void replaceCharAtIndex(char c, int line, int index) {
if (text->length() <= index) { if (lines.size() <= line) {
std::cerr << "text string is at length " << text->length() << ", but index is " << index << std::endl; 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; return;
} }
hasUpdated = false; hasUpdated = false;
text[index] = c; lines[line][index] = c;
} }
void update() { void update() {
if (!hasUpdated) if (hasUpdated)
return; return;
for (auto &surface: surfaces)
SDL_FreeSurface(surface); SDL_FreeSurface(surface);
for (auto &shadowSurface: shadowSurfaces)
SDL_FreeSurface(shadowSurface); SDL_FreeSurface(shadowSurface);
surfaces.clear();
shadowSurfaces.clear();
shadowSurface = TTF_RenderText_Solid(font, text->c_str(), shadowColor); for (const auto &line: lines) {
if (shadowSurface == nullptr) SDL_Surface *textSurface = TTF_RenderText_Solid(font, line.c_str(), color);
std::cerr << "Failed to create shadow text surface (TextScreen): " << TTF_GetError() << std::endl; SDL_Surface *shadowSurface = TTF_RenderText_Solid(font, line.c_str(), shadowColor);
surface = TTF_RenderText_Solid(font, text->c_str(), color); if (textSurface == nullptr || shadowSurface == nullptr) {
if (surface == nullptr)
std::cerr << "Failed to create text surface (TextScreen): " << TTF_GetError() << std::endl; std::cerr << "Failed to create text surface (TextScreen): " << TTF_GetError() << std::endl;
continue;
}
surfaces.push_back(textSurface);
shadowSurfaces.push_back(shadowSurface);
}
hasUpdated = true; 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;
} }
}; };