Faculty of Electrical Engineering
X36TJC - Class Project
Project Name: Coolaaber
Author: Marek Handl
Date: February 2008
Goal:
The goal of the project was to create a simple computer game of pool (billiard). Main programming language shall be C++. The graphic part of the application is shall be displayed using OpenGL. GUI shall be created using Qt libraries. There was no intention to create any kind of artificial intelligence; the game shall be played only by humans. It was expected that collision detection and handling might be slightly complicated.
Achievements:
The application is in a state where it is possible to play a game. Simple game rules apply. The simulation engine is not perfect but shall be able to handle most of situation. It has been decided to concentrate on movements in 2D only as the third dimension proved to be very complex to add. Even in 2D a lot of equation needs to be solved to do the simulation. It took me unexpectedly tens of hours to even derive these 2D equations and to debug them and yet they are not perfect. To implement the other dimension would require other tens of hours, which I find inappropriate for a class project of this kind. The foundations of the program are ready for implementation of the third dimension, though. Collision detection and resolving should work in 3D, although it hasn’t been thoroughly tested. Main concern is the gravity, as it complicates the computation a lot. Therefore no gravity has been implemented and an assumption has been made, that all collisions would happen in one horizontal plane.
At the moment the application can resolve ball-ball collision as well as ball-cuboid collision. The cuboids collisions are working in general, it is easy to add any other cuboid in the game and the collision handling will work just fine.
It is possible to customize starting position of the game. By editing “game_setup.cfg” you can change positioning of balls and add additional cuboids to the game.
Due to complexity of the problem, graphic design of the application is therefore simple too. There are two possible views. The first one “overhead view” is stationary, looking at the table from above, whole table visible. The other one “white ball follow view” is dynamic – there is always the white ball in the center of screen and the view is looking in the direction of the cue. The second view is better for aiming.
Developing platform:
Intel Core2Duo
MS Windows
Visual Studio C++ 2005 Express
Qt 4.3.0 with OpenGL
The project hasn’t been tested on any other system, but the code shall be platform independent.
User manual:
Start the application using Coolaaber.exe.
Hold down left mouse button and move your mouse from left to right to aim.
Press right mouse button to do the stroke. If balls moving, you can press the right button again to stop displaying animation and let the application compute the result as fast as possible – it will be displayed when ready.
Use the slider on the right to control how much energy is put into the stroke.
Look in upper right corner for information whose turn it is and who won.
You cannot do a stroke unless cue is visible.
You can use the Restart button anytime to start a new game.
Use the Change view button to switch between “overhead view” and “follow white ball view”.
Clicking on Quit button closes the application immediately.
Edit game_setup.cfg file to modify number of balls in the game, their positions, colors and to add additional cuboids in the game. See the inside of the file for format description.
List of files with
description:
Ball.cpp |
Ball object |
Collision.cpp |
Represents one collision (either ball-wall or ball-ball) Collision resolving is implemented here |
Coolaaber.cpp |
Encapsulates the Qt gui |
Cuboid.cpp |
Box like object in a game – 6 walls, stationary Collision detection implemented here |
Cue.cpp |
Just cue class |
Direction.cpp |
Mathematical vector with operators |
Game.cpp |
Central class – here is most of the logic implemented Controls the flow of the game. Finds collisions and handles them. |
GameObject.cpp |
Abstract class for objects in the game |
GameSetup.cpp |
Sets up the game – creates table and balls Reads from configuration file |
Glwidget.cpp |
OpenGL widget – openGl setup, user input handling |
Main.cpp |
Main – just starts up the Qt application |
Plane.cpp |
Mathematical plane with operators |
Point.cpp |
Mathematical point with operators |
Wall.cpp |
One wall of a cuboid |
WallPoints.cpp |
Set of points – used to describe a wall |
Globals.h |
Constants, basic setup |
Coolaaber.pro |
Qt project file |
Coolaaber.ui |
Qt qui file – Qt generates a c++ .h file from it |
See attached class diagram for more information about the class model.
Simple description
how the program works:
Main.cpp calls Coolaaber class which creates gui and starts the application. Then all communication with the user goes through glwidget class, which has slots to receive user action signals and also controls the OpenGL widget (the whole animation).
Most of functionality is implemented in the Game class. It uses GameSetup class to create the world (the pool table and balls – they are read from the configuration file). Game class contains the game rules and controls almost everything (stroke, balls movement, ball-ball collision detection). Ball-wall collision detection is in Cuboid class. Collision handling is implemented in Collision class.
A lot of application settings can be changed in globals.h.
Some algorithms used:
Collision detection
Program uses iterative simulation. In each iteration, balls are moved according to their actual speed. Then collision detection takes place. When checking for collision with a cuboid a bounding sphere collision detection is done first. Only if this first test is positive a more sophisticated algorithm (listed below) is used. If there was collision, moment in time when the two objects touched is computed. All objects are moved back to this moment. It is checked for other collisions and if none is found the original collision is resolved. The balls finish their movements accordingly (based on the amount of time that was taken back).
Ball and Cuboid collision
- imagine walls of the cuboids are infinite planes
- compute distance of the ball from all the planes (positive if ball is in front of the wall with respect to the wall’s normal vector)
- if only one is positive and the distance is less than a diameter, there has been a collision with the wall (the wall plane will be used for computation)
- if two are positive … collision with the edge (compute a plane that is 45° to both of the walls and contains their intersection)
- if three are positive … collision with the corner (compute a plane that is 45° to all three walls and contains their intersection – the corner)
- do a rebound from the computed plane
- the resulting speed vector is initial vector mirrored with respect to plane’s normal, it needs to be change to negative as the last step
Ball and Ball
collision
- once centers of two balls are closer than two radii, there must have been a collision
// segment connecting the two centers
X_Axis = (center2 - center1)
X_Axis.normalize();
// projections of speed vectors into connecting axis and perpendicular direction
U1x = X_Axis * (X_Axis dot U1)
U1y = U1 - U1x
U2x = -X_Axis * (-X_Axis dot U2)
U2y = U2 - U2x
// computation based upon momentum, weights of balls are considered too
V1x = ((U1x * M1) + (U2x * M2) - (U1x - U2x) * M2) / (M1 + M2)
V2x = ((U1x * M1) + (U2x * M2) - (U2x - U1x) * M1) / (M1 + M2)
// new speed vectors
V1 = V1x + U1y
V2 = V2x + U2y
Speed Attenuation
- each iteration the balls lose some of their speed (simulates friction)
- both x and y component of the speed are multiplied by a deceleration factor
- when a ball hits a wall loss of speed is determined by the wall deceleration factor
- when a ball hits another ball bounce deceleration factor is used
- when the balls were standing next to each other (and one had been hit) the bounce deceleration factor is ignored to grant proper energy transport