#include #include #include #include #include "paddle.hpp" #include "ball.hpp" #include "math-helpers.hpp" /* Global variables used to instantiate structs */ const int WIDTH = 1500; const int HEIGHT = 600; const int RECT_H = HEIGHT / 3; const int RECT_W = 30; const int PADDLE_SPEED = 8; const int CIRC_RAD = 10; const float BASE_BOUNCE_DEG = 60; const float BASE_BOUNCE_RAD = (BASE_BOUNCE_DEG / 180.0) * M_PI; const float BASE_SPEED_COMPONENTS = 15; const float BASE_SPEED = sqrt(powf(BASE_SPEED_COMPONENTS, 2) * 2); raylib::Vector2 changeVelocityAfterCollision(Paddle paddle, Ball ball) { float paddle_mid_y = (paddle.getRect().y + paddle.getRect().GetHeight()) / 2.0; /* Middle y value of rectangle */ float ball_y = ball.pos.y; /* Y co-ordinate of ball */ float offset = paddle_mid_y - ball_y; /* Subtracting the ball coordinate will give us a value between -paddle_mid_y (represents bottom of paddle) and +paddle_mid_y (represents top of paddle) */ offset /= (paddle.getRect().GetHeight()); /* Normalize the value, by dividing it by its maximum magnitude. It is now a value between -1 and 1. */ offset *= 0.8 + (float)(std::rand()) / (float) (RAND_MAX / ( 1.2 - 0.8)); // Generate a random float from 0.8 to 1.2 float bounce_angle = offset * BASE_BOUNCE_RAD; /* Calculate the actual bounce angle from the base bounce angle. */ /* Calculate new velocities as multiples of the original velocity. I use sine and cosine, because when the ball hits the paddle perpendicular to it (bounce angle is 0), the y_velocity should be 0 (i.e. It should bounce straight back). The sin function does this for us. A similar reasoning was employed for the use of cosine */ float new_x_vel = abs(BASE_SPEED * cosf(bounce_angle)) * (-1 * signum(ball.vel.x)); /* Reverse the sign of the x-velocity */ float new_y_vel = abs(BASE_SPEED * sinf(bounce_angle)) * signum(ball.vel.y); /* Keep the sign of the y-velocity */ return raylib::Vector2(new_x_vel, new_y_vel); } int main() { /* Initialize window and other variables */ raylib::Window window = raylib::Window(WIDTH, HEIGHT, "Pong"); SetTraceLogLevel(LOG_INFO); window.ClearBackground(BLACK); SetTargetFPS(60); SetExitKey(KEY_Q); std::string points_str = std::string("0\t\t0"); bool game_started = false; srand(std::time(NULL)); /* Instantiate Paddle and Ball objects */ Paddle pad1 = Paddle(10, (HEIGHT / 2) - (RECT_H / 2), RECT_W, RECT_H); Paddle pad2 = Paddle(window.GetWidth() - RECT_W - 10, (HEIGHT / 2) - (RECT_H / 2), RECT_W, RECT_H); Ball ball = Ball(window.GetWidth()/2, window.GetHeight()/2, CIRC_RAD, BASE_SPEED, 0); window.BeginDrawing(); pad1.draw(); pad2.draw(); ball.draw(); window.EndDrawing(); /* Main loop */ while (!window.ShouldClose()) { if (!game_started) { if (IsKeyDown(KEY_SPACE)) { game_started = true; } } if (game_started) { /* Update paddle velocity */ if (IsKeyPressed(KEY_S)) { pad1.velocity.y = PADDLE_SPEED; /* Set positive (downward) velocity, since (0,0) is top-left */ } if (IsKeyPressed(KEY_W)) { pad1.velocity.y = (-1) * PADDLE_SPEED; /* Set negative (upward) velocity */ } if (IsKeyReleased(KEY_S) || IsKeyReleased(KEY_W)) { pad1.velocity.y = 0; } if (IsKeyPressed(KEY_UP)) { pad2.velocity.y = (-1) * PADDLE_SPEED; } if (IsKeyPressed(KEY_DOWN)) { pad2.velocity.y = PADDLE_SPEED; } if (IsKeyReleased(KEY_UP) || IsKeyReleased(KEY_DOWN)) { pad2.velocity.y = 0; } /* Update ball velocity based on collision detection */ if (pad1.getRect().CheckCollision(ball.pos, ball.radius)) { /* Collision with paddle 1 */ ball.pos.x = pad1.getRect().x + pad1.getRect().GetWidth() + ball.radius + 1; /* Ensuring that the ball doesn't get stuck inside the paddle */ ball.vel = changeVelocityAfterCollision(pad1, ball); } if (pad2.getRect().CheckCollision(ball.pos, ball.radius)) { /* Collision with paddle 2 */ ball.pos.x = pad2.getRect().x - ball.radius - 1; ball.vel = changeVelocityAfterCollision(pad2, ball); } if (ball.pos.x + ball.radius >= window.GetWidth()) { /* Collision with right wall */ pad1.incrementPoints(); game_started = false; ball.reset(); pad1.reset(); pad2.reset(); } if (ball.pos.x - ball.radius <= 0) { /* Collision with left wall */ pad2.incrementPoints(); game_started = false; ball.reset(); pad1.reset(); pad2.reset(); } if (ball.pos.y - ball.radius <= 0) { /* Collision with top wall */ ball.pos.y = ball.radius + 1; ball.vel.y = ball.vel.y * -1; } if (ball.pos.y + ball.radius >= window.GetHeight()) { /* Collision with bottom wall */ ball.pos.y = HEIGHT - ball.radius - 1; ball.vel.y = ball.vel.y * -1; } /* Update positions based on velocities */ pad1.updatePosition(); pad2.updatePosition(); ball.updatePosition(); } /* Draw objects */ window.BeginDrawing(); window.ClearBackground(BLACK); points_str = std::to_string(pad1.getPoints()) + "\t\t" + std::to_string(pad2.getPoints()); raylib::Text::Draw( points_str, (WIDTH / 2) - 30, HEIGHT / 10, 30, raylib::Color::White() ); pad1.draw(); pad2.draw(); ball.draw(); window.EndDrawing(); } window.Close(); return 0; }