Browse Source

Created maze program

master v1.0.0
Kenneth Bruen 2 years ago
parent
commit
9e0eb19907
Signed by: kbruen
GPG Key ID: C1980A470C3EE5B1
  1. 2
      .vscode/launch.json
  2. 5
      examples/maze1.txt
  3. 9
      examples/maze2.txt
  4. 7
      examples/maze3.txt
  5. 6
      src/drawable.h
  6. 16
      src/glut_events.h
  7. 321
      src/main.cpp
  8. 153
      src/maze.cpp
  9. 27
      src/maze.h
  10. 22
      src/no_maze.cpp
  11. 7
      src/no_maze.h
  12. 13
      src/utils.hpp

2
.vscode/launch.json vendored

@ -9,7 +9,7 @@
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/program",
"args": [],
"args": ["examples/maze3.txt"],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],

5
examples/maze1.txt

@ -0,0 +1,5 @@
XXXXXXX
X X
X X X
X X
XXXXXXX

9
examples/maze2.txt

@ -0,0 +1,9 @@
XXXXXXXXXXXXXX
X X
XXXXXXXXXXXX X
X X
XXXXX XXXXXXXX
X X
XXXXXXXXXXX X
X X
XXXXXXXXXXXXXX

7
examples/maze3.txt

@ -0,0 +1,7 @@
XXXXXXX
X X
X X X
X XXX X
X X X
X X
XXXXXXX

6
src/drawable.h

@ -1,6 +0,0 @@
#pragma once
class Drawable {
public:
virtual void draw() = 0;
};

16
src/glut_events.h

@ -0,0 +1,16 @@
#pragma once
struct GlutEvents {
virtual void display() {}
virtual void overlayDisplay() {}
virtual void reshape(int width, int height) {}
virtual void keyboard(unsigned char key, int x, int y) {}
virtual void keyboardUp(unsigned char key, int x, int y) {}
virtual void mouse(int button, int state, int x, int y) {}
virtual void motion(int x, int y) {}
virtual void passiveMotion(int x, int y) {}
virtual void visibility(int state) {}
virtual void entry(int state) {}
virtual void special(int key, int x, int y) {}
virtual void idle() {}
};

321
src/main.cpp

@ -1,243 +1,158 @@
#include <iostream>
#include <functional>
#include <fstream>
#include <vector>
#include <memory>
#include <string>
#include <cmath>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include "pixel_wise.h"
#include "drawable.h"
#include "glut_events.h"
#include "utils.hpp"
#include "no_maze.h"
#include "maze.h"
GLfloat deltaTime;
std::vector<std::vector<int>> maze;
std::unique_ptr<GlutEvents> events;
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;
void display(void) {
if (events) {
events->display();
}
}
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) {
void overlayDisplay(void) {
if (events) {
events->overlayDisplay();
}
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));
}
void reshape(int width, int height) {
if (events) {
events->reshape(width, height);
}
glEnd();
}
void keyDown(unsigned char key, int x, int y) {
if (events) {
events->keyboard(key, x, y);
}
void update() {
x += veloX * deltaTime;
y += veloY * deltaTime;
}
void keyUp(unsigned char key, int x, int y) {
if (events) {
events->keyboardUp(key, x, y);
}
} 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);
}
void mouse(int button, int state, int x, int y) {
if (events) {
events->mouse(button, state, x, y);
}
}
void motion(int x, int y) {
if (events) {
events->motion(x, y);
}
} 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;
void passiveMotion(int x, int y) {
if (events) {
events->passiveMotion(x, y);
}
}
void visibility(int state) {
if (events) {
events->visibility(state);
}
}
void entry(int state) {
if (events) {
events->entry(state);
}
}
void special(int key, int x, int y) {
if (events) {
events->special(key, x, y);
}
}
void idle() {
if (events) {
events->idle();
}
// 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;
void load_maze(const std::string& path) {
std::ifstream f{path};
std::string line;
while(true) {
std::getline(f, line);
if (!f) {
break;
}
}
void keyUp(unsigned char key, int x, int y) {
switch(key) {
case 'w':
case 'W':
case 's':
case 'S':
leftMove = 0;
std::vector<int> row;
for (const auto& c: line) {
switch(c) {
case ' ':
row.push_back(0);
break;
case 'p':
case 'P':
case ';':
case ':':
rightMove = 0;
case 'X':
row.push_back(1);
break;
default:
row.push_back(-1);
}
}
if (row.size() > 0) {
maze.push_back(row);
}
}
}
int main(int argc, char** argv)
{
std::string maze_file;
if (argc < 2) {
events = std::make_unique<NoMazeScreen>();
}
else {
maze_file = std::string{argv[1]};
load_maze(maze_file);
events = std::make_unique<MazeScreen>(maze);
}
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");
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glClearDepth(1.0f);
glDepthFunc(GL_LEQUAL);
glutInitWindowSize(640, 480);
glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH) - 640) * 0.5, (glutGet(GLUT_SCREEN_HEIGHT) - 480) * 0.5);
if (maze_file.size() > 0) {
glutCreateWindow((std::string{"Maze - "} + maze_file).c_str());
}
else {
glutCreateWindow("Maze");
}
if (maze_file.size() > 0) {
glEnable(GL_DEPTH_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, 1, 0.1, 100);
}
glutDisplayFunc(display);
glutOverlayDisplayFunc(overlayDisplay);
glutReshapeFunc(reshape);
glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF);
glutKeyboardFunc(keyDown);
glutKeyboardUpFunc(keyUp);
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutPassiveMotionFunc(passiveMotion);
glutVisibilityFunc(visibility);
glutEntryFunc(entry);
glutSpecialFunc(special);
glutIdleFunc(idle);
glutMainLoop();
return 0;

153
src/maze.cpp

@ -0,0 +1,153 @@
#include "maze.h"
#include <cmath>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include "utils.hpp"
constexpr double PI = 3.14159265358979;
MazeScreen::MazeScreen(const std::vector<std::vector<int>>& maze):
maze(maze),
angleX(0),
mouseCapture(false),
posX(1),
posZ(1) {}
void MazeScreen::display() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//configures the camera with the position_x and position_z variables
gluLookAt(
posX,
0,
posZ,
posX + cos((angleX * PI) / 180) * 3,
0,
posZ - sin((angleX * PI) / 180) * 3,
0,
1,
0
);
for (int i = 0; i < maze.size(); i++) {
for (int j = 0; j < maze[i].size(); j++) {
switch (maze[i][j]) {
case -1:
glPushMatrix();
glColor3f(1, 0, 0);
glTranslatef(i, 0, j);
glutWireCube(1);
glPopMatrix();
break;
case 1:
glPushMatrix();
// Scale green by X
auto green = Utils::clamp<float>(Utils::nummap<float>(abs(i - posX), 0, 3, 0, 1), 0, 1);
// Scale blue by Z
auto blue = Utils::clamp<float>(Utils::nummap<float>(abs(j - posZ), 0, 3, 0, 1), 0, 1);
glColor3f(1, green, blue);
glTranslatef(i, 0, j);
if (solid) {
glutSolidCube(1);
}
else {
glutWireCube(1);
}
glPopMatrix();
break;
}
}
}
glFlush();
glutSwapBuffers();
}
void MazeScreen::keyboard(unsigned char key, int x, int y) {
if (key == '\e') {
mouseCapture = false;
glutSetCursor(GLUT_CURSOR_INHERIT);
}
else if (key == ' ') {
solid = !solid;
}
else if (key == 'w') {
forceX += 1;
}
else if (key == 's') {
forceX -= 1;
}
else if (key == 'a') {
forceZ -= 1;
}
else if (key == 'd') {
forceZ += 1;
}
glutPostRedisplay();
}
void MazeScreen::keyboardUp(unsigned char key, int x, int y) {
if (key == 'w') {
forceX -= 1;
}
else if (key == 's') {
forceX += 1;
}
else if (key == 'a') {
forceZ += 1;
}
else if (key == 'd') {
forceZ -= 1;
}
}
int gameTime;
void MazeScreen::idle() {
int newTime = glutGet(GLUT_ELAPSED_TIME);
auto deltaTime = (newTime - gameTime) / 1000.0;
gameTime = newTime;
posX += forceX * deltaTime * cos(angleX * PI / 180) + forceZ * deltaTime * sin(angleX * PI / 180);
posZ += forceZ * deltaTime * cos(angleX * PI / 180) - forceX * deltaTime * sin(angleX * PI / 180);
glutPostRedisplay();
}
void MazeScreen::passiveMotion(int x, int y) {
if (mouseCapture) {
auto centerX = glutGet(GLUT_WINDOW_WIDTH) / 2;
auto centerY = glutGet(GLUT_WINDOW_HEIGHT) / 2;
auto diffX = x - centerX;
auto diffY = x - centerY;
// only left-right movement
angleX -= diffX;
if (x != centerX || y != centerY) {
glutWarpPointer(centerX, centerY);
}
glutPostRedisplay();
}
}
void MazeScreen::mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON) {
auto centerX = glutGet(GLUT_WINDOW_WIDTH) / 2;
auto centerY = glutGet(GLUT_WINDOW_HEIGHT) / 2;
glutWarpPointer(centerX, centerY);
mouseCapture = true;
glutSetCursor(GLUT_CURSOR_NONE);
}
else if (button == GLUT_RIGHT_BUTTON) {
mouseCapture = false;
glutSetCursor(GLUT_CURSOR_INHERIT);
}
}

27
src/maze.h

@ -0,0 +1,27 @@
#pragma once
#include "glut_events.h"
#include <vector>
class MazeScreen : public GlutEvents {
private:
std::vector<std::vector<int>> maze;
int angleX;
bool mouseCapture;
float posX;
float posZ;
bool solid;
float forceX;
float forceZ;
public:
MazeScreen(const std::vector<std::vector<int>> &maze);
virtual void display();
virtual void keyboard(unsigned char key, int x, int y);
virtual void keyboardUp(unsigned char key, int x, int y);
virtual void idle();
virtual void passiveMotion(int x, int y);
virtual void mouse(int button, int state, int x, int y);
};

22
src/no_maze.cpp

@ -0,0 +1,22 @@
#include "no_maze.h"
#include <string>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include "pixel_wise.h"
void NoMazeScreen::display() {
glClear(GL_COLOR_BUFFER_BIT);
glColor3ub(0xFF, 0x00, 0x00);
glRasterPos2fv(PW::tm(-100, 20).data());
for (const auto& c : std::string{"No Maze Loaded"}) {
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, c);
}
glFlush();
glutSwapBuffers();
}

7
src/no_maze.h

@ -0,0 +1,7 @@
#pragma once
#include "glut_events.h"
struct NoMazeScreen : public GlutEvents {
virtual void display();
};

13
src/utils.hpp

@ -9,6 +9,19 @@ namespace Utils {
return (input - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
}
template <typename T>
T clamp(T input, T min, T max) {
if (input < min) {
return min;
}
else if (input > max) {
return max;
}
else {
return input;
}
}
template <typename T>
constexpr T toRad(T deg) {
return deg * PI / 180;

Loading…
Cancel
Save