This commit is contained in:
= 2024-01-19 18:41:26 +01:00
parent 30235c5fc8
commit 45ac2ffa68
5 changed files with 201 additions and 22 deletions

View File

@ -13,6 +13,13 @@ if (NOT SDL2_GFX_LIBRARY)
message(FATAL_ERROR "SDL2_gfx not found") message(FATAL_ERROR "SDL2_gfx not found")
endif () endif ()
find_library(SDL2_TTF_LIBRARY NAMES SDL2_ttf SDL2_TTF SDL2TTF)
if (NOT SDL2_TTF_LIBRARY)
message(FATAL_ERROR "SDL2_TTF not found")
endif ()
# Define the executable target before linking libraries # Define the executable target before linking libraries
add_executable(Pong main.cpp add_executable(Pong main.cpp
SdlWrapper.h SdlWrapper.h
@ -22,7 +29,8 @@ add_executable(Pong main.cpp
Vec2d/Vec2d.h Vec2d/Vec2d.h
Vec2d/Bump.h Vec2d/Bump.h
VisibleObjects/PlayerPaddle.h VisibleObjects/PlayerPaddle.h
VisibleObjects/Side.h) VisibleObjects/Side.h
VisibleObjects/Score.h)
# Now link the libraries to the target # Now link the libraries to the target
target_link_libraries(Pong ${SDL2_LIBRARIES} ${SDL2_GFX_LIBRARY}) target_link_libraries(Pong ${SDL2_LIBRARIES} ${SDL2_GFX_LIBRARY} ${SDL2_TTF_LIBRARY})

15
Game.h
View File

@ -8,25 +8,34 @@
#include "SDL.h" #include "SDL.h"
#include "VisibleObjects/Ball.h" #include "VisibleObjects/Ball.h"
#include "VisibleObjects/PlayerPaddle.h" #include "VisibleObjects/PlayerPaddle.h"
#include "VisibleObjects/Score.h"
class Game : public SdlWrapper { class Game : public SdlWrapper {
private: private:
Ball *ball; Ball *ball;
Score *score;
PlayerPaddle *leftPaddle, *rightPaddle; PlayerPaddle *leftPaddle, *rightPaddle;
public: public:
explicit Game(SDL_Point screenSize) : SdlWrapper("Pong", screenSize, 60) { explicit Game(SDL_Point screenSize) : SdlWrapper("Pong", screenSize, 60) {
leftPaddle = new PlayerPaddle(&this->screenSize, Side::LEFT); leftPaddle = new PlayerPaddle(&this->screenSize, Side::LEFT);
rightPaddle = new PlayerPaddle(&this->screenSize, Side::RIGHT); rightPaddle = new PlayerPaddle(&this->screenSize, Side::RIGHT);
ball = new Ball(&this->screenSize, leftPaddle, rightPaddle);
auto func = [this](Side side) {
const char *player = side == Side::LEFT ? "one" : "two";
std::cout << "Player " << player << " won" << std::endl;
this->running = false;
};
score = new Score(5, &this->screenSize, func);
ball = new Ball(&this->screenSize, leftPaddle, rightPaddle, score);
} }
~Game() override { ~Game() override {
delete ball; delete ball;
delete leftPaddle; delete leftPaddle;
delete rightPaddle; delete rightPaddle;
delete score;
} }
void draw(SDL_Renderer *renderer) override { void draw(SDL_Renderer *renderer) override {
@ -35,6 +44,7 @@ public:
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
ball->draw(renderer); ball->draw(renderer);
score->draw(renderer);
leftPaddle->draw(renderer); leftPaddle->draw(renderer);
rightPaddle->draw(renderer); rightPaddle->draw(renderer);
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
@ -44,6 +54,7 @@ public:
ball->update(); ball->update();
leftPaddle->update(); leftPaddle->update();
rightPaddle->update(); rightPaddle->update();
score->update();
return true; return true;
} }

View File

@ -5,6 +5,8 @@
#pragma #pragma
#include <string> #include <string>
#include <SDL_ttf.h>
#include <iostream>
#include "SDL.h" #include "SDL.h"
class SdlWrapper { class SdlWrapper {
@ -14,12 +16,20 @@ private:
SDL_Renderer *renderer; SDL_Renderer *renderer;
protected: protected:
SDL_Point screenSize; SDL_Point screenSize;
bool running = true;
public: public:
explicit SdlWrapper(const char *title, const SDL_Point screenSize, const uint8_t fps) : fps(fps) { explicit SdlWrapper(const char *title, const SDL_Point screenSize, const uint8_t fps) : fps(fps) {
this->screenSize = screenSize; this->screenSize = screenSize;
SDL_Init(SDL_INIT_VIDEO); if (SDL_Init(SDL_INIT_VIDEO) < 0) {
std::cerr << "Failed to initialize SDL: " << SDL_GetError() << std::endl;
exit(-1);
}
if (TTF_Init() < 0) {
std::cerr << "Failed to initialize TTF: " << TTF_GetError() << std::endl;
exit(-1);
}
window = SDL_CreateWindow( window = SDL_CreateWindow(
title, title,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
@ -48,7 +58,7 @@ public:
virtual bool handleEvents() = 0; virtual bool handleEvents() = 0;
int loop() { int loop() {
while (true) { while (running) {
if (!handleEvents() || !update()) if (!handleEvents() || !update())
break; break;
draw(renderer); draw(renderer);

View File

@ -9,6 +9,7 @@
#include <SDL2/SDL2_gfxPrimitives.h> #include <SDL2/SDL2_gfxPrimitives.h>
#include <iostream> #include <iostream>
#include "optional" #include "optional"
#include "Score.h"
class Ball { class Ball {
private: private:
@ -18,15 +19,26 @@ private:
Vec2d *vec2d; Vec2d *vec2d;
static const uint32_t color = 0xCD5C5CFF; static const uint32_t color = 0xCD5C5CFF;
const PlayerPaddle *leftPaddle, *rightPaddle; const PlayerPaddle *leftPaddle, *rightPaddle;
Score *score;
public: public:
explicit Ball(const SDL_Point *screen, const PlayerPaddle *leftPaddle, const PlayerPaddle *rightPaddle) { explicit Ball(const SDL_Point *screen, const PlayerPaddle *leftPaddle, const PlayerPaddle *rightPaddle,
Score *score) {
this->score = score;
this->screen = screen; this->screen = screen;
this->x = screen->x / 2;
this->y = screen->y / 2;
this->vec2d = new Vec2d(2.5);
this->leftPaddle = leftPaddle; this->leftPaddle = leftPaddle;
this->rightPaddle = rightPaddle; this->rightPaddle = rightPaddle;
this->x = screen->x / 2;
this->y = screen->y / 2;
vec2d = new Vec2d(4);
}
void resetPosition() {
this->x = screen->x / 2;
this->y = screen->y / 2;
delete vec2d;
vec2d = new Vec2d(4);
} }
void draw(SDL_Renderer *renderer) const { void draw(SDL_Renderer *renderer) const {
@ -34,16 +46,17 @@ public:
} }
void update() { void update() {
std::cout << "Ball x: " << x << ", y: " << y << std::endl;
std::optional<Side> paddleSide = collidedPaddle(); std::optional<Side> paddleSide = collidedPaddle();
bool screenEdgeVertical = collidedScreenEdgeVertical(); bool screenEdgeVertical = collidedScreenEdgeVertical();
std::optional<Side> scoreSide = collidedScreenEdgeHorizontal();
if (screenEdgeVertical && paddleSide.has_value()) { if (screenEdgeVertical && paddleSide.has_value()) {
vec2d->bump(BumpType::BOTH, PaddleDirection::NONE); vec2d->bump(BumpType::BOTH, PaddleDirection::NONE);
} else if (screenEdgeVertical) { } else if (screenEdgeVertical) {
vec2d->bump(BumpType::WALL, PaddleDirection::NONE); vec2d->bump(BumpType::WALL, PaddleDirection::NONE);
} else if (collidedScreenEdgeHorizontal()) { } else if (scoreSide.has_value()) {
std::cout << "Player won" << std::endl; score->incrementScore(scoreSide.value());
exit(1); resetPosition();
} }
if (paddleSide.has_value()) { if (paddleSide.has_value()) {
const PlayerPaddle *paddle = paddleSide.value() == Side::LEFT ? leftPaddle : rightPaddle; const PlayerPaddle *paddle = paddleSide.value() == Side::LEFT ? leftPaddle : rightPaddle;
@ -57,27 +70,30 @@ private:
return y - RADIUS <= 0 || y + RADIUS >= screen->y; return y - RADIUS <= 0 || y + RADIUS >= screen->y;
} }
bool collidedScreenEdgeHorizontal() { std::optional<Side> collidedScreenEdgeHorizontal() {
return x + RADIUS >= screen->x || x - RADIUS <= 0; if (x + RADIUS >= screen->x)
return Side::RIGHT;
else if (x - RADIUS <= 0)
return Side::LEFT;
return std::nullopt;
} }
std::optional<Side> collidedPaddle() { std::optional<Side> collidedPaddle() {
// Check collision with right paddle // Right paddle
if (x + RADIUS >= rightPaddle->x && if (x + RADIUS >= rightPaddle->x &&
y >= rightPaddle->y && y >= rightPaddle->y &&
y <= rightPaddle->y + rightPaddle->h) { y <= rightPaddle->y + rightPaddle->h) {
return Side::RIGHT; return Side::RIGHT;
} }
// Check collision with left paddle // Left paddle
else if (x - RADIUS <= leftPaddle->x + leftPaddle->w && if (x - RADIUS <= leftPaddle->x + leftPaddle->w &&
y >= leftPaddle->y && y >= leftPaddle->y &&
y <= leftPaddle->y + leftPaddle->h) { y <= leftPaddle->y + leftPaddle->h) {
return Side::LEFT; return Side::LEFT;
} }
return std::nullopt; return std::nullopt;
} }
}; };

134
VisibleObjects/Score.h Normal file
View File

@ -0,0 +1,134 @@
//
// 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 <SDL_ttf.h>
#include <string>
#include <iostream>
#if defined(_WIN32) || defined(_WIN64)
// Windows
const char* defaultFontPath = "C:\\Windows\\Fonts\\Arial.ttf";
#elif defined(__linux__)
const char *defaultFontPath = "/usr/share/fonts/truetype/DejaVuSans-Bold.ttf";
#else
// Other platforms
const char* defaultFontPath = "path/to/a/default/font.ttf";
#endif
class Score {
private:
uint8_t leftScore, rightScore;
const uint8_t MAX_SCORE;
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}; // Black color for the shadow
SDL_Surface *shadowSurface = nullptr;
const int shadowOffset = 3;
public:
explicit Score(uint8_t max_score, SDL_Point *screenSize, std::function<void(Side)> whenWon) : MAX_SCORE(max_score),
whenWon(whenWon) {
resetScore();
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, 40};
}
~Score() {
if (font)
TTF_CloseFont(font);
if (surface)
SDL_FreeSurface(surface);
}
void update() {
if (!hasIncremented && surface != nullptr && shadowSurface != nullptr) return;
if (surface != nullptr)
SDL_FreeSurface(surface);
if (shadowSurface != nullptr)
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, NULL, &shadowPosition);
SDL_DestroyTexture(shadowTexture);
}
}
// Draw text
if (surface != nullptr) {
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
if (texture != nullptr) {
SDL_RenderCopy(renderer, texture, NULL, &position);
SDL_DestroyTexture(texture);
}
}
}
void resetScore() {
leftScore = rightScore = 0;
}
void incrementScore(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);
}
};