mirror of
https://github.com/lov3b/Pong.git
synced 2025-06-28 15:50:27 +02:00
add src dir
This commit is contained in:
106
src/Game.h
Normal file
106
src/Game.h
Normal file
@ -0,0 +1,106 @@
|
||||
//
|
||||
// Created by love on 2024-01-18.
|
||||
//
|
||||
|
||||
#pragma
|
||||
|
||||
#include "SdlWrapper.h"
|
||||
#include "SDL.h"
|
||||
#include "VisibleObjects/Ball.h"
|
||||
#include "VisibleObjects/PlayerPaddle.h"
|
||||
#include "VisibleObjects/Score.h"
|
||||
|
||||
|
||||
class Game : public SdlWrapper {
|
||||
private:
|
||||
Ball *ball;
|
||||
Score *score;
|
||||
PlayerPaddle *leftPaddle, *rightPaddle;
|
||||
|
||||
public:
|
||||
explicit Game(SDL_Point screenSize) : SdlWrapper("Pong", screenSize, 60) {
|
||||
leftPaddle = new PlayerPaddle(&this->screenSize, Side::LEFT);
|
||||
rightPaddle = new PlayerPaddle(&this->screenSize, Side::RIGHT);
|
||||
|
||||
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 {
|
||||
delete ball;
|
||||
delete leftPaddle;
|
||||
delete rightPaddle;
|
||||
delete score;
|
||||
}
|
||||
|
||||
void draw(SDL_Renderer *renderer) override {
|
||||
// Background
|
||||
SDL_SetRenderDrawColor(renderer, 128, 0, 128, 0);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
ball->draw(renderer);
|
||||
score->draw(renderer);
|
||||
leftPaddle->draw(renderer);
|
||||
rightPaddle->draw(renderer);
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
|
||||
bool update() override {
|
||||
ball->update();
|
||||
leftPaddle->update();
|
||||
rightPaddle->update();
|
||||
score->update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handleEvents() override {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event) != 0) {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
70
src/SdlWrapper.h
Normal file
70
src/SdlWrapper.h
Normal file
@ -0,0 +1,70 @@
|
||||
//
|
||||
// Created by love on 2024-01-18.
|
||||
//
|
||||
|
||||
#pragma
|
||||
|
||||
#include <string>
|
||||
#include <SDL_ttf.h>
|
||||
#include <iostream>
|
||||
#include "SDL.h"
|
||||
|
||||
class SdlWrapper {
|
||||
private:
|
||||
const uint8_t fps = 60;
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
protected:
|
||||
SDL_Point screenSize;
|
||||
bool running = true;
|
||||
|
||||
public:
|
||||
explicit SdlWrapper(const char *title, const SDL_Point screenSize, const uint8_t fps) : fps(fps) {
|
||||
this->screenSize = screenSize;
|
||||
|
||||
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(
|
||||
title,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
screenSize.x,
|
||||
screenSize.y,
|
||||
0
|
||||
);
|
||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
|
||||
virtual ~SdlWrapper() {
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
virtual void draw(SDL_Renderer *renderer) = 0;
|
||||
|
||||
virtual bool update() = 0;
|
||||
|
||||
virtual bool handleEvents() = 0;
|
||||
|
||||
int loop() {
|
||||
while (running) {
|
||||
if (!handleEvents() || !update())
|
||||
break;
|
||||
draw(renderer);
|
||||
SDL_Delay(1000 / fps);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
};
|
13
src/Vec2d/Bump.h
Normal file
13
src/Vec2d/Bump.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by love on 2024-01-19.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class BumpType {
|
||||
WALL, PADDLE, BOTH
|
||||
};
|
||||
|
||||
enum class PaddleDirection {
|
||||
MOVING_UP, NOT_MOVING, MOVING_DOWN, NONE
|
||||
};
|
69
src/Vec2d/Vec2d.h
Normal file
69
src/Vec2d/Vec2d.h
Normal file
@ -0,0 +1,69 @@
|
||||
//
|
||||
// Created by love on 2024-01-19.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "random"
|
||||
#include "ctime"
|
||||
#include "cmath"
|
||||
|
||||
#include "SDL_rect.h"
|
||||
#include "Bump.h"
|
||||
|
||||
inline double_t toRadians(double_t degrees) {
|
||||
return degrees * M_PI / 100;
|
||||
}
|
||||
|
||||
class Vec2d {
|
||||
private:
|
||||
std::default_random_engine random;
|
||||
float_t hypotenuse;
|
||||
double_t x, y;
|
||||
const float_t bumpSpeedIncrease = 1.05;
|
||||
|
||||
public:
|
||||
Vec2d(float_t hypotenuse) : hypotenuse(hypotenuse) {
|
||||
std::random_device rd;
|
||||
random = std::default_random_engine(rd());
|
||||
int sign = random() % 2 == 0 ? -1 : 1;
|
||||
double_t angle = toRadians(random() % 6000 / 100 - 30);
|
||||
x = cos(angle) * sign * hypotenuse;
|
||||
y = sin(angle) * sign * hypotenuse;
|
||||
}
|
||||
|
||||
void applyVector(Sint16 *ox, Sint16 *oy) const {
|
||||
*ox += static_cast<int>(std::round(x));
|
||||
*oy += static_cast<int>(std::round(y));
|
||||
}
|
||||
|
||||
void bump(BumpType bumpType, PaddleDirection paddleDirection) {
|
||||
// Make everything a bit faster so it's not boring
|
||||
hypotenuse *= bumpSpeedIncrease;
|
||||
x *= bumpSpeedIncrease;
|
||||
y *= bumpSpeedIncrease;
|
||||
|
||||
switch (bumpType) {
|
||||
case BumpType::BOTH:
|
||||
x = -x;
|
||||
y = -y;
|
||||
break;
|
||||
case BumpType::WALL:
|
||||
y = -y;
|
||||
break;
|
||||
case BumpType::PADDLE:
|
||||
x = -x;
|
||||
double angle = 0;
|
||||
if (paddleDirection != PaddleDirection::NOT_MOVING) {
|
||||
double_t degrees = rand() % 500 / 100 + 15;
|
||||
degrees *= paddleDirection == PaddleDirection::MOVING_UP ? -1 : 1;
|
||||
angle = toRadians(degrees);
|
||||
|
||||
// Adjusting y direction based on the angle
|
||||
y = sin(angle) * hypotenuse;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
98
src/VisibleObjects/Ball.h
Normal file
98
src/VisibleObjects/Ball.h
Normal file
@ -0,0 +1,98 @@
|
||||
//
|
||||
// Created by love on 2024-01-18.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "../Vec2d/Vec2d.h"
|
||||
#include "PlayerPaddle.h"
|
||||
#include <SDL2/SDL2_gfxPrimitives.h>
|
||||
#include <iostream>
|
||||
#include "optional"
|
||||
#include "Score.h"
|
||||
|
||||
class Ball {
|
||||
private:
|
||||
static const uint8_t RADIUS = 15;
|
||||
const SDL_Point *screen;
|
||||
Sint16 x, y;
|
||||
Vec2d *vec2d;
|
||||
static const uint32_t color = 0xCD5C5CFF;
|
||||
const PlayerPaddle *leftPaddle, *rightPaddle;
|
||||
Score *score;
|
||||
|
||||
public:
|
||||
explicit Ball(const SDL_Point *screen, const PlayerPaddle *leftPaddle, const PlayerPaddle *rightPaddle,
|
||||
Score *score) {
|
||||
this->score = score;
|
||||
this->screen = screen;
|
||||
this->leftPaddle = leftPaddle;
|
||||
this->rightPaddle = rightPaddle;
|
||||
this->x = screen->x / 2;
|
||||
this->y = screen->y / 2;
|
||||
vec2d = new Vec2d(6);
|
||||
}
|
||||
|
||||
void resetPosition() {
|
||||
this->x = screen->x / 2;
|
||||
this->y = screen->y / 2;
|
||||
|
||||
delete vec2d;
|
||||
vec2d = new Vec2d(6);
|
||||
}
|
||||
|
||||
void draw(SDL_Renderer *renderer) const {
|
||||
filledCircleColor(renderer, x, y, RADIUS, color);
|
||||
}
|
||||
|
||||
void update() {
|
||||
std::optional<Side> paddleSide = collidedPaddle();
|
||||
bool screenEdgeVertical = collidedScreenEdgeVertical();
|
||||
std::optional<Side> scoreSide = collidedScreenEdgeHorizontal();
|
||||
|
||||
if (screenEdgeVertical && paddleSide.has_value()) {
|
||||
vec2d->bump(BumpType::BOTH, PaddleDirection::NONE);
|
||||
} else if (screenEdgeVertical) {
|
||||
vec2d->bump(BumpType::WALL, PaddleDirection::NONE);
|
||||
} else if (scoreSide.has_value()) {
|
||||
score->incrementScore(scoreSide.value());
|
||||
resetPosition();
|
||||
}
|
||||
if (paddleSide.has_value()) {
|
||||
const PlayerPaddle *paddle = paddleSide.value() == Side::LEFT ? leftPaddle : rightPaddle;
|
||||
vec2d->bump(BumpType::PADDLE, paddle->getPaddleDirection());
|
||||
}
|
||||
vec2d->applyVector(&x, &y);
|
||||
}
|
||||
|
||||
private:
|
||||
bool collidedScreenEdgeVertical() {
|
||||
return y - RADIUS <= 0 || y + RADIUS >= screen->y;
|
||||
}
|
||||
|
||||
std::optional<Side> collidedScreenEdgeHorizontal() {
|
||||
if (x + RADIUS >= screen->x)
|
||||
return Side::RIGHT;
|
||||
else if (x - RADIUS <= 0)
|
||||
return Side::LEFT;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Side> collidedPaddle() {
|
||||
// Right paddle
|
||||
if (x + RADIUS >= rightPaddle->x &&
|
||||
y >= rightPaddle->y &&
|
||||
y <= rightPaddle->y + rightPaddle->h) {
|
||||
return Side::RIGHT;
|
||||
}
|
||||
// Left paddle
|
||||
if (x - RADIUS <= leftPaddle->x + leftPaddle->w &&
|
||||
y >= leftPaddle->y &&
|
||||
y <= leftPaddle->y + leftPaddle->h) {
|
||||
return Side::LEFT;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
79
src/VisibleObjects/PlayerPaddle.h
Normal file
79
src/VisibleObjects/PlayerPaddle.h
Normal file
@ -0,0 +1,79 @@
|
||||
//
|
||||
// Created by love on 2024-01-19.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "Side.h"
|
||||
#include "../Vec2d/Bump.h"
|
||||
|
||||
class PlayerPaddle : public SDL_Rect {
|
||||
private:
|
||||
static const int MOVE_PER_TICK = 5;
|
||||
const SDL_Point *screen;
|
||||
bool movingUp, movingDown;
|
||||
uint8_t color[4]{};
|
||||
|
||||
public:
|
||||
PlayerPaddle(const SDL_Point *screen, const Side side) : SDL_Rect() {
|
||||
w = 20;
|
||||
h = 80;
|
||||
x = side == Side::LEFT ? 0 : screen->x - w;
|
||||
y = (screen->y - h) / 2;
|
||||
movingUp = false;
|
||||
movingDown = false;
|
||||
this->screen = screen;
|
||||
|
||||
color[0] = 255;
|
||||
color[1] = 234;
|
||||
color[2] = 0;
|
||||
color[3] = 255;
|
||||
}
|
||||
|
||||
PaddleDirection getPaddleDirection() const {
|
||||
if (movingUp != movingDown)
|
||||
return PaddleDirection::NOT_MOVING;
|
||||
else if (movingUp)
|
||||
return PaddleDirection::MOVING_UP;
|
||||
return PaddleDirection::MOVING_DOWN;
|
||||
}
|
||||
|
||||
void draw(SDL_Renderer *renderer) {
|
||||
SDL_SetRenderDrawColor(renderer, color[0], color[1], color[2], color[3]);
|
||||
SDL_RenderFillRect(renderer, this);
|
||||
}
|
||||
|
||||
void startMoving(bool up) {
|
||||
if (up)
|
||||
movingUp = true;
|
||||
else movingDown = true;
|
||||
}
|
||||
|
||||
void stopMoving(bool up) {
|
||||
if (up)
|
||||
movingUp = false;
|
||||
else movingDown = false;
|
||||
}
|
||||
|
||||
void update() {
|
||||
// We cannot move up and down
|
||||
if (movingUp == movingDown)
|
||||
return;
|
||||
|
||||
if (movingUp && canMoveUp())
|
||||
y -= MOVE_PER_TICK;
|
||||
else if (movingDown && canMoveDown())
|
||||
y += MOVE_PER_TICK;
|
||||
}
|
||||
|
||||
private:
|
||||
bool canMoveDown() {
|
||||
return y + h < screen->y;
|
||||
}
|
||||
|
||||
bool canMoveUp() {
|
||||
return y > 0;
|
||||
}
|
||||
|
||||
};
|
134
src/VisibleObjects/Score.h
Normal file
134
src/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);
|
||||
}
|
||||
|
||||
};
|
9
src/VisibleObjects/Side.h
Normal file
9
src/VisibleObjects/Side.h
Normal file
@ -0,0 +1,9 @@
|
||||
//
|
||||
// Created by love on 2024-01-19.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class Side {
|
||||
LEFT, RIGHT
|
||||
};
|
8
src/main.cpp
Normal file
8
src/main.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
#include "Game.h"
|
||||
|
||||
|
||||
int main() {
|
||||
Game game(SDL_Point{1000, 600});
|
||||
|
||||
return game.loop();
|
||||
}
|
Reference in New Issue
Block a user