This project demonstrates a collision and physics engine implemented in C++ using OpenGL. The engine manages game objects, detects collisions, and applies basic physics principles to create an interactive environment. The project includes representations for cubes and spheres, handling their interactions and movements.
This project showcases a collision and physics engine implemented in C++ using OpenGL. The engine provides a robust framework for managing game objects, detecting collisions, and applying basic physics principles, making it a foundational component for interactive game development.
Core Features
- Game Object Representation: Abstract base class for all game objects, with derived classes for specific shapes like cubes and spheres.
- Collision Detection: Implemented for both cubes and spheres, allowing for interaction between different types of objects.
- Physics Calculations: Basic physics principles applied to manage object movements and interactions.
- OpenGL Rendering: Uses OpenGL to render objects and update the scene in real-time.

Key Components
1. Game Object Base Class (GameObject.h / GameObject.cpp)
This class defines the base properties and behaviors for all game objects, including position, color, and collision detection.
#pragma once
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <map>
//A basic abstract class that all game objects in the scene are based off
// abstract = We never will make a direct instance of GameObject but will make classes inherit from it
class GameObject
{
public:
// -- variables --
float x, y, z; //position
float r, g, b; //colour
float x_coll_minus;
float x_coll_plus;
float y_coll_minus;
float y_coll_plus;
float z_coll_minus;
float z_coll_plus;
bool isProjectile = 0;
static std::map<char, bool> keys; //save key presses and let game objects access them
static std::map<int, bool> specialKeys; //static = one instance of these variables for the whole class (top of .cpp)
// -- constructors/destructors --
GameObject(); //default constructor - aka no values passed in (position at 0,0,0 - colour magenta)
GameObject(float _x, float _y, float _z); //pass in a position (no colour passed in so defaults to white)
GameObject(float _x, float _y, float _z,
float _r, float _g, float _b); //pass in a position, colour
~GameObject(); //default destructor
// -- functions --
//pure virtual so all classes that derive from this one need to include a definion for these
virtual void Draw() = 0;
virtual void Update(float deltaTime) = 0;
bool CheckCollision(GameObject* obj);
};
#include "GameObject.h"
std::map<char, bool> GameObject::keys;
std::map<int, bool> GameObject::specialKeys;
GameObject::GameObject()
{
x = 0.0f;
y = 0.0f;
z = 0.0f;
r = 1.0f;
g = 0.0f;
b = 1.0f;
}
GameObject::GameObject(float _x, float _y, float _z)
{
x = _x;
y = _y;
z = _z;
r = 1.0f;
g = 1.0f;
b = 1.0f;
}
GameObject::GameObject(float _x, float _y, float _z, float _r, float _g, float _b)
{
x = _x;
y = _y;
z = _z;
r = _r;
g = _g;
b = _b;
}
GameObject::~GameObject()
{
}
bool GameObject::CheckCollision(GameObject* obj)
{
if (x > obj->x_coll_minus && x < obj->x_coll_plus &&
y > obj->y_coll_minus && y < obj->y_coll_plus &&
z > obj->z_coll_minus && z < obj->z_coll_plus)
return 1;
return 0;
}
2. Cube Class (Cube.h / Cube.cpp)
This class defines a cube object, including its drawing and update logic.
#pragma once
#include "GameObject.h"
//A basic cube class derived from a game object
class Cube: public GameObject
{
public:
// -- variables --
float size;
bool isTarget;
bool movable;
// -- constructors/destructors --
Cube(); //default constructor - aka no values passed in (position at 0,0,0 - colour magenta - size 1)
Cube(float _x, float _y, float _z, float _size); //pass in a position and size
Cube(float _x, float _y, float _z, float _size, bool _isTarget, bool _movable);
Cube(float _x, float _y, float _z,
float _r, float _g, float _b,
float _size); //pass in a position, colour and size
~Cube() {}; //default destructor
// -- functions --
//Need to give definions to GameObject's pure virtual functions
void Draw();
void Update(float deltaTime);
};
#include "Cube.h"
//We can either hard code the values in GameObject (like in Cube())
//or
//We can use the GameEngine constructors to fill the values ( like in Cube(x, y, z, size))
Cube::Cube()
{
x = 0.0f;
y = 0.0f;
z = 0.0f;
r = 1.0f;
g = 0.0f;
b = 1.0f;
size = 1.0f;
isTarget = 0;
movable = 0;
x_coll_minus = x - (size / 2);
x_coll_plus = x + (size / 2);
y_coll_minus = y - (size / 2);
y_coll_plus = y + (size / 2);
z_coll_minus = z - (size / 2);
z_coll_plus = z + (size / 2);
}
Cube::Cube(float _x, float _y, float _z, float _size)
:GameObject(_x, _y, _z)
{
size = _size;
}
Cube::Cube(float _x, float _y, float _z, float _size, bool _isTarget, bool _movable)
: GameObject(_x, _y, _z)
{
size = _size;
isTarget = _isTarget;
movable = _movable;
}
Cube::Cube(float _x, float _y, float _z,
float _r, float _g, float _b,
float _size):
GameObject(_x, _y, _z, _r, _g, _b)
{
size = _size;
}
//Position and colour a cube in the scene
void Cube::Draw()
{
glPushMatrix();
glTranslatef(x, y, z);
glColor3f(r, g, b);
glutSolidCube(size);
//glutWireCube(size) - if you'd rather see through it
glPopMatrix();
}
void Cube::Update(float deltaTime)
{
x_coll_minus = x - (size / 2);
x_coll_plus = x + (size / 2);
y_coll_minus = y - (size / 2);
y_coll_plus = y + (size / 2);
z_coll_minus = z - (size / 2);
z_coll_plus = z + (size / 2);
}
3. Sphere Class (Sphere.h / Sphere.cpp)
This class defines a sphere object, including its movement and interaction logic.
#pragma once
#include "GameObject.h"
//A basic sphere class derived from a game object
//It can move (basic position changing only at the moment!) via the arrow keys
class Sphere : public GameObject
{
public:
// -- variables --
float radius;
float moveSpeed;
// -- constructors/destructors --
Sphere(); //default constructor - aka no values passed in (position at 0,0,0 - colour magenta - radius 1 - move speed - 1)
Sphere(float _x, float _y, float _z, float _radius, float _moveSpeed); //pass in a position, radius and movespeed
Sphere(float _x, float _y, float _z,
float _r, float _g, float _b,
float _radius, float _moveSpeed); //pass in a position, colour, radius and move speed
Sphere(float _x, float _y, float _z, bool _isProjectile);
~Sphere() {}; //default destructor
// -- functions --
//Need to give definions to GameObject's pure virtual functions
virtual void Draw();
virtual void Update(float deltaTime);
};
#include "Sphere.h"
//We can either hard code the values in GameObject (like in Sphere())
//or
//We can use the GameEngine constructors to fill the values ( like in Sphere(x, y, z, radius, speed))
Sphere::Sphere()
{
x = 0.0f;
y = 0.0f;
z = 0.0f;
r = 1.0f;
g = 0.0f;
b = 1.0f;
radius = 1.0f;
moveSpeed = 5.0f;
x_coll_minus = x - radius;
x_coll_plus = x + radius;
y_coll_minus = y - radius;
y_coll_plus = y + radius;
z_coll_minus = z - radius;
z_coll_plus = z + radius;
}
Sphere::Sphere(float _x, float _y, float _z, float _radius, float _moveSpeed)
:GameObject(_x, _y, _z) //note the use of GameObject constructor
{
radius = _radius;
moveSpeed = _moveSpeed;
}
Sphere::Sphere(float _x, float _y, float _z,
float _r, float _g, float _b,
float _radius, float _moveSpeed)
:GameObject(_x, _y, _z, _r, _g, _b)
{
radius = _radius;
moveSpeed = _moveSpeed;
}
Sphere::Sphere(float _x, float _y, float _z, bool _isProjectile)
:GameObject(_x, _y, _z)
{
radius = 1.f;
moveSpeed = 15.f;
isProjectile = _isProjectile;
}
void Sphere::Draw()
{
glPushMatrix();
glEnable(GL_DEPTH_TEST);
glTranslatef(x, y, z);
glColor3f(r, g, b);
glutSolidSphere(radius, 10, 10); //if you'd rather it shows solid
glutWireSphere(radius, 10, 10);
glPopMatrix();
}
void Sphere::Update(float deltaTime)
{
if (GameObject::keys['w'] == true)
z -= moveSpeed * deltaTime;
if (GameObject::keys['s'] == true)
z += moveSpeed * deltaTime;
if (GameObject::keys['d'] == true)
x += moveSpeed * deltaTime;
if (GameObject::keys['a'] == true)
x -= moveSpeed * deltaTime;
//Scale
if (GameObject::keys['q'] == true)
radius += 2 * deltaTime;
if (GameObject::keys['e'] == true)
radius -= 2 * deltaTime;
//Jump
if (GameObject::keys['f'] == true)
{
y += moveSpeed * 2 * deltaTime;
}
//Gravity
if (y >= 0.1)
y -= 9.5 * deltaTime;
}
4. Main Game Loop (main.cpp)
This file sets up the OpenGL environment, initializes game objects, and contains the main game loop with rendering and collision updates.
#include <GL/glew.h>
#include <GL/freeglut.h>
#pragma comment(lib, "glew32.lib")
#include <iostream>
#include <vector>
#include "Cube.h"
#include "Sphere.h"
using namespace std;
// -- global variables --
vector<GameObject*> objects;
int oldTimeSinceStart;
int newTimeSinceStart;
float deltaTime;
GameObject* movable = new Cube(5, 0, 0, 2);
GameObject* target = new Cube;
GameObject* player = new Sphere(-1, 0, -1, 0, 0, 1, 1.5f, 10.0f);
GameObject* collectable = new Cube(2, 0, -2, 0, 1, 0, 1);
GameObject* projectile = new Sphere(-1, 0, -1, 1);
// Initialization
void setup(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);
objects.push_back(movable);
objects.push_back(target);
objects.push_back(collectable);
objects.push_back(player);
}
//Drawing
void drawScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Position the objects for viewing
gluLookAt(0.0, 5.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
//Draw all our game objects
for (int i = 0; i < objects.size(); ++i)
{
objects[i]->Draw();
}
glutSwapBuffers();
}
void UpdateCollisions(float deltaTime)
{
std::cout << player->CheckCollision(collectable) << std::endl;
//Player - Movable Collision
if (player->CheckCollision(movable))
{
if (GameObject::keys['a'] == true)
movable->x -= 15.f * deltaTime;
if (GameObject::keys['d'] == true)
movable->x += 15.f * deltaTime;
if (GameObject::keys['w'] == true)
movable->z -= 15.f * deltaTime;
if (GameObject::keys['s'] == true)
movable->z += 15.f * deltaTime;
if (player->y_coll_minus < movable->y_coll_plus)
if (player->y >= 0.1)
player->y = 2.f;
}
if (GameObject::keys['g'] == true)
{
projectile->z -= 12 * deltaTime;
}
//Target - Projectile Collision
if (target->CheckCollision(projectile))
{
if (projectile->isProjectile)
target->x += 200.f;
}
//Player - Collectable Collision
if (player->CheckCollision(collectable))
{
collectable->x = 0;
collectable->y = 0;
collectable->z = 0;
}
}
//Called when nothing else is happening (such as rendering)
void idle()
{
//Work out the delta time - the time between the last frame and this frame
oldTimeSinceStart = newTimeSinceStart;
newTimeSinceStart = glutGet(GLUT_ELAPSED_TIME);
deltaTime = (newTimeSinceStart - oldTimeSinceStart) / 1000.0f;
//cout << "Delta Time (seconds): " << deltaTime << endl; - if you want to check the delta time
//Update all our game objects
for (int i = 0; i < objects.size(); ++i)
{
objects[i]->Update(deltaTime);
}
UpdateCollisions(deltaTime);
glutPostRedisplay();
}
//Delete any memory gotten from the new keyword
//Is only called when the ESC key is used to leave the game
void CleanUp()
{
for (int i = objects.size() - 1; i >= 0; --i)
{
delete objects[i];
}
objects.clear();
}
// Keyboard input processing functions
void keyInputDown(unsigned char key, int x, int y)
{
GameObject::keys[key] = true;
//If you want to see keys pressed printed to the console
//std::cout << "Key pressed: " << key << std::endl;
switch (key)
{
case 27: // Esc key
CleanUp();
exit(0);
break;
default:
break;
}
}
void keyInputUp(unsigned char key, int x, int y)
{
GameObject::keys[key] = false;
//If you want to see keys pressed printed to the console
//std::cout << "Key lifted: " << key << std::endl;
}
void keySpecialDown(int key, int x, int y)
{
GameObject::specialKeys[key] = true;
//If you want to see keys pressed printed to the console
//std::cout << "Special Key pressed: " << key << std::endl;
}
void keySpecialUp(int key, int x, int y)
{
GameObject::specialKeys[key] = false;
//If you want to see keys pressed printed to the console
//std::cout << "Special Key lifted: " << key << std::endl;
}
//OpenGL window reshape
void resize(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (float)w / (float)h, 1.0, 500.0);
glMatrixMode(GL_MODELVIEW);
}
//Entry point into the program
int main(int argc, char** argv)
{
//Setup for legacy OpenGL usage
glutInit(&argc, argv);
glutInitContextVersion(2, 0);
glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE);
//Window settings
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("Basic Resit Project");
//Callbacks
glutDisplayFunc(drawScene);
glutReshapeFunc(resize);
glutKeyboardFunc(keyInputDown);
glutKeyboardUpFunc(keyInputUp);
glutSpecialFunc(keySpecialDown);
glutSpecialUpFunc(keySpecialUp);
glutIdleFunc(idle);
glewExperimental = GL_TRUE;
glewInit();
setup();
glutMainLoop();
}

Leave a Reply