diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b0fddda
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+SRC := components/
+BUILD := build/
+
+SOURCE := $(wildcard $(SRC)/*.cpp)
+OBJECT := $(patsubst $(SRC)/%.cpp, $(BUILD)/%.o, $(SOURCE))
+
+.PHONY: run
+
+cping_pong: $(OBJECT) $(BUILD)/main.o
+ gcc -Wall -lraylib -lm -o $@ $^
+
+$(BUILD)/main.o: main.cpp
+ gcc -Wall -c $< -o $@
+
+$(BUILD)/%.o: $(SRC)/%.cpp
+ gcc -Wall -c $< -o $@
+
+run: cping_pong
+ @./cping_pong
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b53ffe6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
+# Ping Pong CPP
+
+- A ping pong game written in Cpp using RayLib
+
+
+ ![In-Game screenshot](screens/screen.png)
+
+
+## How To Play
+
+- Use the keys `K/Up` and `J/Down` to move **Up** and **Down**
+
+## Build
+
+- Make sure that RayLib is installed on your system then compile the project.
+
+1. Create directory `build/`
+
+```sh
+$ mkdir -p build/
+```
+
+2. Compile the project by running `make` or `make run` to compile and run the project.
+
+```sh
+$ make
+$ ./cping_pong
+# OR
+$ make run
+```
diff --git a/build/ball.o b/build/ball.o
new file mode 100644
index 0000000..ef605bd
Binary files /dev/null and b/build/ball.o differ
diff --git a/build/game_manager.o b/build/game_manager.o
new file mode 100644
index 0000000..9d15ebc
Binary files /dev/null and b/build/game_manager.o differ
diff --git a/build/main.o b/build/main.o
new file mode 100644
index 0000000..aa20590
Binary files /dev/null and b/build/main.o differ
diff --git a/build/paddle.o b/build/paddle.o
new file mode 100644
index 0000000..789a958
Binary files /dev/null and b/build/paddle.o differ
diff --git a/components/ball.cpp b/components/ball.cpp
new file mode 100644
index 0000000..130e231
--- /dev/null
+++ b/components/ball.cpp
@@ -0,0 +1,46 @@
+#include "../include/ball.hpp"
+#include
+
+void Game::Ball::draw() { DrawCircle(position.x, position.y, radius, color); }
+
+void Game::Ball::gotoCenter() {
+ position.x = static_cast(GetScreenWidth()) / 2;
+ position.y = static_cast(GetScreenHeight()) / 2;
+}
+
+void Game::Ball::update() {
+ // limit ball speed
+ float speed = Vector2Length(velocity);
+ if (speed > 8)
+ velocity = Vector2Scale(velocity, 0.95f);
+ else if (speed < 6)
+ velocity = Vector2Scale(velocity, 1.1f);
+
+ // update ball position based on velocity
+ position.x += velocity.x;
+ position.y += velocity.y;
+
+ // collide with top wall
+ if (position.y - radius < 0) {
+ position.y = radius;
+ velocity.y *= -1;
+ }
+
+ // collide with bottom wall
+ if (position.y + radius > GetScreenHeight()) {
+ position.y = GetScreenHeight() - radius;
+ velocity.y *= -1;
+ }
+}
+
+void Game::Ball::shoot(Vector2 force) {
+ velocity.x += force.x;
+ velocity.y += force.y;
+}
+
+void Game::Ball::shoot(Vector2 direction, float force) {
+ direction = Vector2Normalize(direction);
+
+ velocity.x += direction.x * force;
+ velocity.y += direction.y * force;
+}
diff --git a/components/game_manager.cpp b/components/game_manager.cpp
new file mode 100644
index 0000000..bbaa9e0
--- /dev/null
+++ b/components/game_manager.cpp
@@ -0,0 +1,86 @@
+#include "../include/game_manager.hpp"
+#include
+#include
+#include
+
+void Game::GameManager::draw() {
+ const char *human_score_text = TextFormat("%u", human_score);
+ const char *cpu_score_text = TextFormat("%u", cpu_score);
+
+ int human_text_length = MeasureText(human_score_text, font_size);
+ int cpu_text_length = MeasureText(cpu_score_text, font_size);
+ int x_value = GetScreenWidth() / 4;
+ int y_value = GetScreenHeight() / 2 - font_size / 2;
+
+ DrawText(human_score_text, x_value - human_text_length / 2, y_value,
+ font_size, RAYWHITE);
+
+ DrawText(cpu_score_text, GetScreenWidth() - x_value - cpu_text_length / 2,
+ y_value, font_size, RAYWHITE);
+
+ DrawLine(x_value * 2, 0, x_value * 2, GetScreenHeight(), RAYWHITE);
+ DrawCircleLines(x_value * 2, GetScreenHeight() / 2, 80, RAYWHITE);
+}
+
+void Game::GameManager::update() {
+ // if ball hits left wall then score goal for CPU and reset ball
+ if (ball.position.x + ball.radius <= 0) {
+ ball.gotoCenter();
+ ball.velocity = {0, 0};
+ ball.shoot({1, 0}, 6);
+ cpu_score++;
+ }
+
+ // if ball hits right wall then score goal for human and reset ball
+ if (ball.position.x - ball.radius >= GetScreenWidth()) {
+ ball.gotoCenter();
+ ball.velocity = {0, 0};
+ ball.shoot({-1, 0}, 6);
+ human_score++;
+ }
+
+ if (IsKeyPressed(KEY_SPACE) && human_score > 0) {
+ ball.gotoCenter();
+ ball.velocity = {0, 0};
+ ball.shoot({-1, 0}, 6);
+ human_score--;
+ }
+
+ // change ball direction on collision with any paddle
+ if (CheckCollisionCircleRec(ball.position, ball.radius, human_paddle.rect))
+ onBallPaddleCollision(human_paddle);
+ if (CheckCollisionCircleRec(ball.position, ball.radius, cpu_paddle.rect))
+ onBallPaddleCollision(cpu_paddle);
+}
+
+void Game::GameManager::onBallPaddleCollision(const Paddle &paddle) {
+ static float timeA = -1, timer = 0;
+ float time;
+
+ float paddle_speed_ratio = paddle.velocity / paddle.max_velocity;
+ float ball_speed = Vector2Length(ball.velocity);
+ float y_touch =
+ (ball.position.y - paddle.rect.y) / paddle.rect.height * 2 - 1;
+ Vector2 normal = Vector2Normalize(ball.velocity);
+
+ normal.y = (y_touch + paddle_speed_ratio) / 2;
+ normal.x *= -1;
+ ball.velocity = Vector2Scale(normal, ball_speed);
+
+ // fix ball stuck glitch
+ time = GetTime();
+
+ if (time - timeA < 0.05)
+ timer += GetFrameTime();
+ else
+ timer = 0;
+
+ if (timer > 2) {
+ ball.gotoCenter();
+ ball.velocity = {0, 0};
+ ball.shoot({-1, 0}, 6);
+ timer = 0;
+ TraceLog(LOG_INFO, "Glitch Detected!!");
+ }
+ timeA = GetTime();
+}
diff --git a/components/paddle.cpp b/components/paddle.cpp
new file mode 100644
index 0000000..5e5f999
--- /dev/null
+++ b/components/paddle.cpp
@@ -0,0 +1,34 @@
+#include "../include/paddle.hpp"
+#include
+
+float Game::Paddle::window_padding = 10;
+
+void Game::Paddle::gotoCenter() {
+ rect.y = static_cast(GetScreenHeight()) / 2 - rect.height / 2;
+}
+
+void Game::Paddle::draw() { DrawRectangleRec(rect, color); }
+
+void Game::Paddle::update() {
+ // if velocity is too small set it to 0
+ if (std::abs(velocity) < 0.01)
+ velocity = 0;
+ // if velocity is too big set it to maximum velocity
+ if (std::abs(velocity) > max_velocity)
+ velocity = velocity > 0 ? max_velocity : -max_velocity;
+
+ // update the position of the paddle based on the velocity
+ rect.y += velocity;
+ // decrease the velocity over time, so the paddle stops after a while
+ velocity *= drag;
+
+ // limit the paddle to the screen edges
+ if (rect.y < 0)
+ rect.y = 0;
+ if (rect.y > GetScreenHeight() - rect.height)
+ rect.y = GetScreenHeight() - rect.height;
+}
+
+void Game::Paddle::moveUp() { velocity -= speed; }
+
+void Game::Paddle::moveDown() { velocity += speed; }
diff --git a/cping_pong b/cping_pong
new file mode 100755
index 0000000..136f3a9
Binary files /dev/null and b/cping_pong differ
diff --git a/include/ball.hpp b/include/ball.hpp
new file mode 100644
index 0000000..96c886e
--- /dev/null
+++ b/include/ball.hpp
@@ -0,0 +1,24 @@
+#ifndef BALL_HPP
+#define BALL_HPP
+
+#include
+
+namespace Game {
+class Ball {
+public:
+ Vector2 position = {0, 0};
+ Vector2 velocity = {0, 0};
+ Color color = RED;
+ float radius = 10;
+
+ void gotoCenter();
+
+ void draw();
+ void update();
+
+ void shoot(Vector2 force);
+ void shoot(Vector2 direction, float force);
+};
+} // namespace Game
+
+#endif
diff --git a/include/game_manager.hpp b/include/game_manager.hpp
new file mode 100644
index 0000000..3f3d15d
--- /dev/null
+++ b/include/game_manager.hpp
@@ -0,0 +1,31 @@
+#ifndef GAME_MANAGER_HPP
+#define GAME_MANAGER_HPP
+
+#include "ball.hpp"
+#include "paddle.hpp"
+
+namespace Game {
+
+class GameManager {
+ Ball &ball;
+ const Paddle &human_paddle;
+ const Paddle &cpu_paddle;
+
+ void onBallPaddleCollision(const Paddle &paddle);
+
+public:
+ unsigned int human_score = 0;
+ unsigned int cpu_score = 0;
+
+ int font_size = 164;
+
+ GameManager(Ball &ball, const Paddle &human_paddle, const Paddle &cpu_paddle)
+ : ball(ball), human_paddle(human_paddle), cpu_paddle(cpu_paddle) {}
+
+ void update();
+ void draw();
+};
+
+} // namespace Game
+
+#endif
diff --git a/include/paddle.hpp b/include/paddle.hpp
new file mode 100644
index 0000000..1d44920
--- /dev/null
+++ b/include/paddle.hpp
@@ -0,0 +1,28 @@
+#ifndef PADDLE_HPP
+#define PADDLE_HPP
+
+#include
+
+namespace Game {
+class Paddle {
+public:
+ Rectangle rect = {0, 0, 15, 100};
+ Color color = RAYWHITE;
+ float speed = 2;
+ float velocity = 0;
+ float max_velocity = 5;
+ float drag = 0.88;
+ static float window_padding;
+
+ void gotoCenter();
+
+ void draw();
+ void update();
+
+ void moveUp();
+ void moveDown();
+};
+
+} // namespace Game
+
+#endif
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..df5d830
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,64 @@
+#include "include/ball.hpp"
+#include "include/game_manager.hpp"
+#include "include/paddle.hpp"
+#include
+
+void cpu_paddle(Game::Paddle &paddle, const Game::Ball &ball) {
+ float diff = ball.position.y - paddle.rect.y - paddle.rect.height / 2;
+
+ if (diff < 0)
+ paddle.moveUp();
+ else
+ paddle.moveDown();
+}
+
+void human_paddle(Game::Paddle &paddle) {
+ if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_K))
+ paddle.moveUp();
+ else if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_J))
+ paddle.moveDown();
+}
+
+int main() {
+ Game::Ball ball;
+ Game::Paddle p1, p2;
+ Game::GameManager gm(ball, p2, p1);
+
+ InitWindow(800, 500, "PingPongCpp");
+ SetTargetFPS(60);
+
+ ball.gotoCenter();
+ ball.shoot({1, -1}, 6);
+
+ p1.rect.x = GetScreenWidth() - p1.rect.width - Game::Paddle::window_padding;
+ p1.gotoCenter();
+
+ p2.rect.x = Game::Paddle::window_padding;
+ p2.gotoCenter();
+
+ while (!WindowShouldClose()) {
+ ball.update();
+
+ p1.update();
+ cpu_paddle(p1, ball);
+
+ p2.update();
+ human_paddle(p2);
+
+ gm.update();
+
+ BeginDrawing();
+ ClearBackground(BLACK);
+
+ gm.draw();
+ ball.draw();
+ p1.draw();
+ p2.draw();
+
+ EndDrawing();
+ }
+
+ CloseWindow();
+
+ return 0;
+}
diff --git a/screens/screen.png b/screens/screen.png
new file mode 100644
index 0000000..504e84e
Binary files /dev/null and b/screens/screen.png differ