mirror of
https://github.com/lov3b/Pong.git
synced 2025-01-18 20:50:12 +01:00
physics input works
This commit is contained in:
commit
30235c5fc8
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.idea
|
||||||
|
cmake-build-debug
|
28
CMakeLists.txt
Normal file
28
CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.27)
|
||||||
|
project(Pong)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# Base SDL2
|
||||||
|
find_package(SDL2 REQUIRED)
|
||||||
|
include_directories(${SDL2_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
# SDL2 Gfx
|
||||||
|
find_library(SDL2_GFX_LIBRARY NAMES SDL2_gfx SDL2_gfxd)
|
||||||
|
if (NOT SDL2_GFX_LIBRARY)
|
||||||
|
message(FATAL_ERROR "SDL2_gfx not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Define the executable target before linking libraries
|
||||||
|
add_executable(Pong main.cpp
|
||||||
|
SdlWrapper.h
|
||||||
|
Game.h
|
||||||
|
VisibleObjects/Ball.h
|
||||||
|
utils.h
|
||||||
|
Vec2d/Vec2d.h
|
||||||
|
Vec2d/Bump.h
|
||||||
|
VisibleObjects/PlayerPaddle.h
|
||||||
|
VisibleObjects/Side.h)
|
||||||
|
|
||||||
|
# Now link the libraries to the target
|
||||||
|
target_link_libraries(Pong ${SDL2_LIBRARIES} ${SDL2_GFX_LIBRARY})
|
95
Game.h
Normal file
95
Game.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
//
|
||||||
|
// Created by love on 2024-01-18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma
|
||||||
|
|
||||||
|
#include "SdlWrapper.h"
|
||||||
|
#include "SDL.h"
|
||||||
|
#include "VisibleObjects/Ball.h"
|
||||||
|
#include "VisibleObjects/PlayerPaddle.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Game : public SdlWrapper {
|
||||||
|
private:
|
||||||
|
Ball *ball;
|
||||||
|
|
||||||
|
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);
|
||||||
|
ball = new Ball(&this->screenSize, leftPaddle, rightPaddle);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Game() override {
|
||||||
|
delete ball;
|
||||||
|
delete leftPaddle;
|
||||||
|
delete rightPaddle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(SDL_Renderer *renderer) override {
|
||||||
|
// Background
|
||||||
|
SDL_SetRenderDrawColor(renderer, 128, 0, 128, 0);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
|
ball->draw(renderer);
|
||||||
|
leftPaddle->draw(renderer);
|
||||||
|
rightPaddle->draw(renderer);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool update() override {
|
||||||
|
ball->update();
|
||||||
|
leftPaddle->update();
|
||||||
|
rightPaddle->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;
|
||||||
|
}
|
||||||
|
};
|
60
SdlWrapper.h
Normal file
60
SdlWrapper.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// Created by love on 2024-01-18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "SDL.h"
|
||||||
|
|
||||||
|
class SdlWrapper {
|
||||||
|
private:
|
||||||
|
const uint8_t fps = 60;
|
||||||
|
SDL_Window *window;
|
||||||
|
SDL_Renderer *renderer;
|
||||||
|
protected:
|
||||||
|
SDL_Point screenSize;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SdlWrapper(const char *title, const SDL_Point screenSize, const uint8_t fps) : fps(fps) {
|
||||||
|
this->screenSize = screenSize;
|
||||||
|
|
||||||
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
|
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 (true) {
|
||||||
|
if (!handleEvents() || !update())
|
||||||
|
break;
|
||||||
|
draw(renderer);
|
||||||
|
SDL_Delay(1000 / fps);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
13
Vec2d/Bump.h
Normal file
13
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
|
||||||
|
};
|
63
Vec2d/Vec2d.h
Normal file
63
Vec2d/Vec2d.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// 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;
|
||||||
|
const float_t hypotenuse;
|
||||||
|
double_t x, y;
|
||||||
|
|
||||||
|
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);
|
||||||
|
double_t angle = 15;
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
83
VisibleObjects/Ball.h
Normal file
83
VisibleObjects/Ball.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// Created by love on 2024-01-18.
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../utils.h"
|
||||||
|
#include "../Vec2d/Vec2d.h"
|
||||||
|
#include "PlayerPaddle.h"
|
||||||
|
#include <SDL2/SDL2_gfxPrimitives.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include "optional"
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Ball(const SDL_Point *screen, const PlayerPaddle *leftPaddle, const PlayerPaddle *rightPaddle) {
|
||||||
|
this->screen = screen;
|
||||||
|
this->x = screen->x / 2;
|
||||||
|
this->y = screen->y / 2;
|
||||||
|
this->vec2d = new Vec2d(2.5);
|
||||||
|
this->leftPaddle = leftPaddle;
|
||||||
|
this->rightPaddle = rightPaddle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(SDL_Renderer *renderer) const {
|
||||||
|
filledCircleColor(renderer, x, y, RADIUS, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
std::cout << "Ball x: " << x << ", y: " << y << std::endl;
|
||||||
|
std::optional<Side> paddleSide = collidedPaddle();
|
||||||
|
bool screenEdgeVertical = collidedScreenEdgeVertical();
|
||||||
|
if (screenEdgeVertical && paddleSide.has_value()) {
|
||||||
|
vec2d->bump(BumpType::BOTH, PaddleDirection::NONE);
|
||||||
|
} else if (screenEdgeVertical) {
|
||||||
|
vec2d->bump(BumpType::WALL, PaddleDirection::NONE);
|
||||||
|
} else if (collidedScreenEdgeHorizontal()) {
|
||||||
|
std::cout << "Player won" << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool collidedScreenEdgeHorizontal() {
|
||||||
|
return x + RADIUS >= screen->x || x - RADIUS <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Side> collidedPaddle() {
|
||||||
|
// Check collision with right paddle
|
||||||
|
if (x + RADIUS >= rightPaddle->x &&
|
||||||
|
y >= rightPaddle->y &&
|
||||||
|
y <= rightPaddle->y + rightPaddle->h) {
|
||||||
|
return Side::RIGHT;
|
||||||
|
}
|
||||||
|
// Check collision with left paddle
|
||||||
|
else if (x - RADIUS <= leftPaddle->x + leftPaddle->w &&
|
||||||
|
y >= leftPaddle->y &&
|
||||||
|
y <= leftPaddle->y + leftPaddle->h) {
|
||||||
|
return Side::LEFT;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
79
VisibleObjects/PlayerPaddle.h
Normal file
79
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
9
VisibleObjects/Side.h
Normal file
9
VisibleObjects/Side.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
//
|
||||||
|
// Created by love on 2024-01-19.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
enum class Side {
|
||||||
|
LEFT, RIGHT
|
||||||
|
};
|
8
main.cpp
Normal file
8
main.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include "Game.h"
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Game game(SDL_Point{1000, 600});
|
||||||
|
|
||||||
|
return game.loop();
|
||||||
|
}
|
57
utils.h
Normal file
57
utils.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// Created by love on 2024-01-18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SDL.h"
|
||||||
|
|
||||||
|
|
||||||
|
int roundUpToMultipleOfEight(int v) {
|
||||||
|
return (v + (8 - 1)) & -8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/38334081/how-to-draw-circles-arcs-and-vector-graphics-in-sdl
|
||||||
|
void DrawCircle(SDL_Renderer *renderer, SDL_Point ¢er, int radius) {
|
||||||
|
// 35 / 49 is a slightly biased approximation of 1/sqrt(2)
|
||||||
|
const int arrSize = roundUpToMultipleOfEight(radius * 8 * 35 / 49);
|
||||||
|
SDL_Point points[arrSize];
|
||||||
|
int drawCount = 0;
|
||||||
|
|
||||||
|
const int32_t diameter = (radius * 2);
|
||||||
|
|
||||||
|
int32_t x = (radius - 1);
|
||||||
|
int32_t y = 0;
|
||||||
|
int32_t tx = 1;
|
||||||
|
int32_t ty = 1;
|
||||||
|
int32_t error = (tx - diameter);
|
||||||
|
|
||||||
|
while (x >= y) {
|
||||||
|
// Each of the following renders an octant of the circle
|
||||||
|
points[drawCount + 0] = {center.x + x, center.y - y};
|
||||||
|
points[drawCount + 1] = {center.x + x, center.y + y};
|
||||||
|
points[drawCount + 2] = {center.x - x, center.y - y};
|
||||||
|
points[drawCount + 3] = {center.x - x, center.y + y};
|
||||||
|
points[drawCount + 4] = {center.x + y, center.y - x};
|
||||||
|
points[drawCount + 5] = {center.x + y, center.y + x};
|
||||||
|
points[drawCount + 6] = {center.x - y, center.y - x};
|
||||||
|
points[drawCount + 7] = {center.x - y, center.y + x};
|
||||||
|
|
||||||
|
drawCount += 8;
|
||||||
|
|
||||||
|
if (error <= 0) {
|
||||||
|
++y;
|
||||||
|
error += ty;
|
||||||
|
ty += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error > 0) {
|
||||||
|
--x;
|
||||||
|
tx += 2;
|
||||||
|
error += (tx - diameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderDrawPoints(renderer, points, drawCount);
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user