2022-03-21 01:59:09 +02:00
|
|
|
#include <iostream>
|
2022-03-21 01:39:06 +02:00
|
|
|
#include <functional>
|
|
|
|
#include <string>
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
#include <GL/gl.h>
|
|
|
|
#include <GL/glu.h>
|
|
|
|
#include <GL/glut.h>
|
|
|
|
|
|
|
|
#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:
|
2022-03-21 01:59:09 +02:00
|
|
|
static constexpr GLfloat defaultVeloX = 0.5;
|
|
|
|
|
2022-03-21 01:39:06 +02:00
|
|
|
GLfloat radius;
|
|
|
|
GLfloat x, y;
|
|
|
|
GLfloat veloX, veloY;
|
|
|
|
|
2022-03-21 01:59:09 +02:00
|
|
|
Ball(GLfloat x, GLfloat y) : x(x), y(y), radius(0.025), veloX(defaultVeloX) {
|
2022-03-21 01:39:06 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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<std::string()> strObtainer;
|
|
|
|
public:
|
|
|
|
std::function<GLfloat()> x, y;
|
|
|
|
void *font;
|
|
|
|
Text(std::function<std::string()> strObtainer, GLfloat x, GLfloat y) : strObtainer(strObtainer), x([=](){return x;}), y([=](){return y;}), font(GLUT_BITMAP_HELVETICA_18) {}
|
|
|
|
Text(std::function<std::string()> strObtainer, std::function<GLfloat()> x, std::function<GLfloat()> y) : strObtainer(strObtainer), x(x), y(y), font(GLUT_BITMAP_HELVETICA_18) {}
|
|
|
|
Text(std::function<std::string()> strObtainer, std::array<GLfloat, 2> pos) : strObtainer(strObtainer), x([=](){return pos[0];}), y([=](){return pos[1];}), font(GLUT_BITMAP_HELVETICA_18) {}
|
|
|
|
Text(std::function<std::string()> strObtainer, std::function<std::array<GLfloat, 2>()> 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;
|
|
|
|
|
2022-03-21 01:59:09 +02:00
|
|
|
const GLfloat paddleMoveSpeed = 0.8;
|
2022-03-21 01:39:06 +02:00
|
|
|
|
|
|
|
void idle() {
|
|
|
|
int newTime = glutGet(GLUT_ELAPSED_TIME);
|
|
|
|
deltaTime = (newTime - gameTime) / 1000.0;
|
|
|
|
gameTime = newTime;
|
|
|
|
|
|
|
|
if (gameRunning) {
|
|
|
|
ball.update();
|
|
|
|
|
2022-03-21 01:59:09 +02:00
|
|
|
// 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;
|
|
|
|
gameRunning = false;
|
|
|
|
}
|
|
|
|
else if (ball.x - ball.radius > 1) {
|
|
|
|
// Ball exited board on the right side
|
|
|
|
leftScore += 1;
|
|
|
|
ball.x = ball.y = 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;
|
|
|
|
}
|
2022-03-21 01:39:06 +02:00
|
|
|
}
|
|
|
|
|
2022-03-21 01:59:09 +02:00
|
|
|
// 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;
|
|
|
|
|
2022-03-21 01:39:06 +02:00
|
|
|
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;
|
|
|
|
}
|