mirror of
https://github.com/lov3b/Pong.git
synced 2024-11-10 07:20:12 +01:00
Score
This commit is contained in:
parent
30235c5fc8
commit
45ac2ffa68
@ -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
15
Game.h
@ -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;
|
||||||
}
|
}
|
||||||
|
14
SdlWrapper.h
14
SdlWrapper.h
@ -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);
|
||||||
|
@ -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,19 +70,23 @@ 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;
|
||||||
@ -77,7 +94,6 @@ private:
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
134
VisibleObjects/Score.h
Normal file
134
VisibleObjects/Score.h
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user