#include #include #include #include #include #include #include #include "pixel_wise.h" #include "drawable.h" #include "utils.hpp" GLfloat deltaTime; bool gameRunning = false; bool mouseDown = false; int leftScore = 0; int rightScore = 0; int leftMove = 0; int rightMove = 0; void mouse(int button, int state, int x, int y) { if (state == GLUT_DOWN) { gameRunning = true; } } class BoardMiddle : public Drawable { public: virtual void draw() { glBegin(GL_POLYGON); glColor3ub(0xBD, 0xBD, 0xBD); glVertex2fv(PW::nm(-2, -1).data()); glVertex2fv(PW::nm(2, -1).data()); glVertex2fv(PW::nm(2, 1).data()); glVertex2fv(PW::nm(-2, 1).data()); glEnd(); } } bm; class Ball : public Drawable { public: static constexpr GLfloat defaultVeloX = 0.5; GLfloat radius; GLfloat x, y; GLfloat veloX, veloY; Ball(GLfloat x, GLfloat y) : x(x), y(y), radius(0.025), veloX(defaultVeloX) { } virtual void draw() { glBegin(GL_POLYGON); glColor3ub(0xFF, 0xEE, 0x58); for (float i = 0; i < 360; i += 0.5) { const auto rad = Utils::toRad(i); glVertex2f(x + radius * cos(rad), y + radius * sin(rad)); } glEnd(); } void update() { x += veloX * deltaTime; y += veloY * deltaTime; } } ball {0, 0}; class Text : public Drawable { std::function strObtainer; public: std::function x, y; void *font; Text(std::function strObtainer, GLfloat x, GLfloat y) : strObtainer(strObtainer), x([=](){return x;}), y([=](){return y;}), font(GLUT_BITMAP_HELVETICA_18) {} Text(std::function strObtainer, std::function x, std::function y) : strObtainer(strObtainer), x(x), y(y), font(GLUT_BITMAP_HELVETICA_18) {} Text(std::function strObtainer, std::array pos) : strObtainer(strObtainer), x([=](){return pos[0];}), y([=](){return pos[1];}), font(GLUT_BITMAP_HELVETICA_18) {} Text(std::function strObtainer, std::function()> pos) : strObtainer(strObtainer), x([=](){return pos()[0];}), y([=](){return pos()[1];}), font(GLUT_BITMAP_HELVETICA_18) {} virtual void draw() { glRasterPos2f(x(), y()); for (const auto& c: strObtainer()) { glutBitmapCharacter(font, c); } } } leftScoreText{[](){ return std::to_string(leftScore); }, [](){return PW::tm(-100, 20);}}, rightScoreText{[](){ return std::to_string(rightScore); }, [](){return PW::tm(100, 20);}}; class Paddle : public Drawable { public: GLfloat height, width; GLfloat x, y; Paddle(GLfloat x, GLfloat y) : x(x), y(y), height(0.4), width(0.02) {} virtual void draw() { glBegin(GL_POLYGON); glVertex2f(x, y); glVertex2f(x + width, y); glVertex2f(x + width, y + height); glVertex2f(x, y + height); glEnd(); } } leftPaddle{-0.9, -0.2}, rightPaddle{0.9 - 0.02, -0.2}; void display(void) { glClear(GL_COLOR_BUFFER_BIT); bm.draw(); ball.draw(); glColor3ub(0x66, 0xBB, 0x6A); leftScoreText.draw(); leftPaddle.draw(); glColor3ub(0x42, 0xA5, 0xF5); rightScoreText.draw(); rightPaddle.draw(); glFlush(); } int gameTime; const GLfloat paddleMoveSpeed = 0.8; void idle() { int newTime = glutGet(GLUT_ELAPSED_TIME); deltaTime = (newTime - gameTime) / 1000.0; gameTime = newTime; if (gameRunning) { ball.update(); // Detect ball collission if (leftPaddle.x <= (ball.x + ball.radius) && (ball.x - ball.radius) <= (leftPaddle.x + leftPaddle.width)) { // Ball inside left-right area of left paddle if (leftPaddle.y <= ball.y && ball.y <= leftPaddle.y + leftPaddle.height) { // Ball inside up-down area of left paddle ball.veloX = ball.defaultVeloX; ball.veloY = Utils::nummap(ball.y, leftPaddle.y, leftPaddle.y + leftPaddle.height, (GLfloat)-1, (GLfloat)1); } } else if (rightPaddle.x <= (ball.x + ball.radius) && (ball.x - ball.radius) <= (rightPaddle.x + rightPaddle.width)) { // Ball inside left-right area of right paddle if (rightPaddle.y <= ball.y && ball.y <= rightPaddle.y + rightPaddle.height) { // Ball inside up-down area of right paddle ball.veloX = -ball.defaultVeloX; ball.veloY = Utils::nummap(ball.y, rightPaddle.y, rightPaddle.y + rightPaddle.height, (GLfloat)-1, (GLfloat)1); } } else if (ball.x + ball.radius < -1) { // Ball exited board on the left side rightScore += 1; ball.x = ball.y = 0; ball.veloY = 0; gameRunning = false; } else if (ball.x - ball.radius > 1) { // Ball exited board on the right side leftScore += 1; ball.x = ball.y = 0; ball.veloY = 0; gameRunning = false; } else if (ball.y - ball.radius < -1) { // Ball touched bottom side ball.veloY *= -1; } else if (ball.y + ball.radius > 1) { // Ball touched top side ball.veloY *= -1; } } // Move paddle leftPaddle.y += leftMove * paddleMoveSpeed * deltaTime; rightPaddle.y += rightMove * paddleMoveSpeed * deltaTime; // Clamp paddle to stay on screen if (leftPaddle.y < -1) leftPaddle.y = -1; if (rightPaddle.y < -1) rightPaddle.y = -1; if (leftPaddle.y > 1 - leftPaddle.height) leftPaddle.y = 1 - leftPaddle.height; if (rightPaddle.y > 1 - rightPaddle.height) rightPaddle.y = 1 - rightPaddle.height; glutPostRedisplay(); } void keyDown(unsigned char key, int x, int y) { switch(key) { case 'w': case 'W': leftMove = 1; break; case 's': case 'S': leftMove = -1; break; case 'p': case 'P': rightMove = 1; break; case ';': case ':': rightMove = -1; break; } } void keyUp(unsigned char key, int x, int y) { switch(key) { case 'w': case 'W': case 's': case 'S': leftMove = 0; break; case 'p': case 'P': case ';': case ':': rightMove = 0; break; } } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(600, 600); glutInitWindowPosition(glutGet(GLUT_SCREEN_WIDTH) * 0.6, (glutGet(GLUT_SCREEN_HEIGHT) - 600) * 0.5); glutCreateWindow("Pong"); glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF); glutKeyboardFunc(keyDown); glutKeyboardUpFunc(keyUp); glutDisplayFunc(display); glutMouseFunc(mouse); glutIdleFunc(idle); glutMainLoop(); return 0; }