How to Build a Simple Game using OpenGL in C

“OpenGL (Open Graphics Library) is a cross-language, multi-platform applicaion
programming interface for rendering2D and 3D vector graphics. The API is typically used to interact with a graphics processor unit (GPU), to achieve hardware accelarated rendering.”

So OpenGL is an open library that let us a draw graphics. In this post we will show how a rookie can build a OpenGLsimple game with OpenGL in a C environment. Specifically this game will feature a spacecraft and we will design and implement each part of it using the primitive types of  OpenGL . Moreover we will build the environment which includes  space, stars , a sun and some planets with the same way. Then we will introduce the way that we can add an animation movement. Finally we will load an asteroid from an object file that will have an orbit towards our spacecraft and the goal will be to avoid the asteroid. If the asteroid hits the spacecraft the game is over and a message will pop up, otherwise the game continues. The movement of the spacecraft (which in our example looks more like an airplane) is rotation of spacecraft/camera, moving left/right/up and down and will be made through keyboard.

Demonstration of our simple implementation

Demonstration of our simple implementation

Our first step is to build the environment:

Let’s start with the stars! This is the structure of  star:

struct star{
int x;
int y;
float red;
float green;
float blue;
int distance;
float scale;
float alpha;
};

typedef struct star Star;

The first two properties are the coordinations of each star, the following are obviously the color of it and lastly the size of it and the transparency. Below you can see the snippet of code that creates random stars with comprehensive comments:

/* Method that creates / renders our stars.
*
* */
void stars(){
int flag=0, j=0, flag2=0;

glPushMatrix();
glEnable( GL_POINT_SMOOTH );

if(starsPositions[0].x == 0 || changeStars == 10){//if array of stars is empty or we have reached 10 frames with the same stars
flag = 1;
changeStars = 0;
}

for( int i = 0; i < 500; i++ ){
glPointSize(1.5);//size of star

glColor3ub(255,255,255);

if(flag==1){//statement that checks whether to generate stars from beginning.
int x = (rand() % 480)-50;//build random values for star
int y = (rand() % 480)-50;
int distance = (rand() % 480)-50;

starsPositions[i].x = x;
starsPositions[i].y = y;
starsPositions[i].distance = distance;
//make stars 3D
gluLookAt( x, y, z, //this function enables us with keys z and x to rotate the camera around the space craft
x+lx, y+ly, z+lz, //keys c and v zoom in and out the space craft
0.0f, 1.0f, 0.0f);
glTranslatef(starsPositions[i].x+moveSun,starsPositions[i].y+moveSun2,starsPositions[i].distance+0);//moveSun moves the sun right and left
}
glBegin( GL_POINTS );

glVertex2i(starsPositions[i].x, starsPositions[i].y);//function that 'renders' stars into window
glEnd();
}
changeStars++;

glFlush();
glPopMatrix();
}

Critical Notice: As you can see in the above snippet the whole function is inside two commands, glPushMatrix() and glPopMatrix. This is crucial because it enables us to apply the transformations, such us transformation and  rotation, to the model (which in our case is stars) and only there and  inherits only transformations that are not inside those two commands. If you have not undestood that well, it’s ok. We will see that in the next parts of our game also.

Let’s move to sun:

Here we implement the sun using two concentric speres. The second sphere that lies behind the first one has an adjustable transparency that is being used to make the sun glowing. Enough talking , here is the snippet!

/*
* Method creates the sun.
*
* */
void sun(){
glPushMatrix();

glTranslatef(30+moveSun,30+moveSun2,20);//moveSun is used to move sun analogically to the motion of spacecraft.
glColor3f(1.0, 1.0, 0);
glutSolidSphere( 5, 20, 20);

glColor4f(1, 1, 0, transparency);
glutSolidSphere( 10, 20, 20);
glPopMatrix();
}

As you can see there are two unknown variables/parameters here, moveSun and moveSun2: The first one is being used to turn the sun left analogically with our movement and the other one right. For example when we are looking at the sun and turn our head left the sun ‘moves’ right and vice versa. Taking that into consideration this feature is mandatory in order to build a realistic approach of a space travel.

To avoid writing a lengthy post I will skip the creation of the planets but anyone that it is interested can download the entire code at the end.

We move on to the creation of  our spacecraft that will give us a better understanding of how we use opengl to build separate graphics and merge them together.

Firstly we implement the wings and the helices. here it is important to create the heliced independently because of their rotation around the center of the wings, otherwise the rotation will affect also the wings which is not visually pleasent 🙂 . Here’s the code:

/*
* Method that creates two helices, left and right.
*
* */
void helices(){
if(rotate2 == 360)//if helice did a full circle (360 degrees) reset degrees to zero.
rotate2 = 0;
else
rotate2+=5;//speed of helices.

glPushMatrix();

glPushMatrix();
glTranslatef(0, 0, -5.5);

glScalef(0.4f, 0.7f, 0.3f);//size of our helice
glColor3f(0.5, 0.5, 0.7);

glutSolidSphere( 3, 3, 3);
glPopMatrix();

glPushMatrix();
glTranslatef(0, 0, -7.5);

glRotatef(rotate2, 0.0f, 0.0f, 1.0f);

glScalef(1.4f, 0.2f, 0.3f);
glColor3f(1.0, 0.0, 0.0);

glutSolidSphere( 5, 20, 20);
glPopMatrix();

glPushMatrix();

glTranslatef(0, 0, -7.5);
glRotatef(rotate2+90, 0.0f, 0.0f, 1.0f);//the other helice must be always +90 degrees from the other component of helice.

glScalef(1.4f, 0.2f, 0.3f);
glColor3f(1, 0.0, 0.0);

glutSolidSphere( 5, 20, 20);//primitive object of OpenGL, other ones are rectangle, triangle, line and circle.
glPopMatrix();
glPopMatrix();
}

And with the same way we are creating our wings and the other parts of the spacecraft such us the tail and the cockpit:

/*
* Method that creates the wings of the spacecraft.
*
* */
void wings()
{
glPushMatrix();//left wing
glTranslatef(20, 0, 0);
glScalef(1.0f, 0.2f, 0.5f);
glColor3f(0.56, 0.63, 0.69);
glutSolidSphere( 10, 10, 20);
glPopMatrix();
glPushMatrix();//right wing
glTranslatef(-20, 0, 0);
glScalef(1.0f, 0.2f, 0.5f);
glColor3f(0.56, 0.63, 0.69);
glutSolidSphere( 10, 10, 20);
glPopMatrix();
}

Now how are we going to load the asteroid in OpenGL and display it in our screen?

Here’s the way:

Have in mind that each object file contains faces and vertices, so in order to display it we must gather all the coordinates of each vertice and face.

/*
* Method that read the object file of asteroid and creates it's model.
*
* */
void ReadFile(){
using namespace std;
int i=0;

ifstream obj_file("asteroid.obj");//open the file for reading OBJINFO.TXT

if (obj_file.fail())
exit(1);

string line;
struct point p;
struct face f;

while (!obj_file.eof()){//while file of object has not reached the last line
getline(obj_file,line);
string type = line.substr(0,2); //take the first two characters of line

int j = 0, z=0, pos;
if(type.compare("v ") == 0){//if it is a 'v' line
string arr[4];
stringstream ssin(line);
while (ssin.good() && j < 4){//split line into arguments by whitespace
ssin >> arr[j];
++j;
}

p.x = atof(arr[1].c_str());//vx
p.y = atof(arr[2].c_str());//vy
p.z = atof(arr[3].c_str());//vz
m.obj_points.push_back(p);
}else if(type.compare("f ") == 0){//if it is a 'f' line
string arr[4], arr2[3];
stringstream ssin(line);

while (ssin.good() && j < 4){//split by whitespace
ssin >> arr[j];
stringstream ss(arr[j]);

pos = arr[j].find('/');
if (pos!=std::string::npos){//split face number e.g 1234//567 -> 1234
arr2[j-1] = arr[j].substr(0, pos);
}
f.vtx1 = atoi(arr2[0].c_str());
f.vtx2 = atoi(arr2[1].c_str());
f.vtx3 = atoi(arr2[2].c_str());
m.faces.push_back(f);
++j;
}
}
i++;
}

vector<struct point>::iterator v = m.obj_points.begin();//print vertices
i=0;
while( v != m.obj_points.end()) {
v++;
i++;
}

m.numberOfVertices = i+1; //9121 + 1 = 9122 this is the number of vertices in our example object file

i=0;
vector<struct face>::iterator v2 = m.faces.begin();//print faces
while( v2 != m.faces.end()) {
v2++;
i++;
}
cout<<"End of reading"<<endl;

m.numberOfFaces = i+1;

obj_file.close();
}

After we gather all the necessary information in our model 'm' we diplay it with the following function:

/*
* Method displays the model 'm' with all vertices and faces in screen.
*
* */
void DisplayModel(model m){
glPushMatrix();
glBegin(GL_TRIANGLES);
for (int i = 0; i < m.numberOfFaces; i++){
glVertex3f(m.obj_points[m.faces[i].vtx1-1].x,m.obj_points[m.faces[i].vtx1-1].y,m.obj_points[m.faces[i].vtx1-1].z);
glVertex3f(m.obj_points[m.faces[i].vtx2-1].x,m.obj_points[m.faces[i].vtx2-1].y,m.obj_points[m.faces[i].vtx2-1].z);
glVertex3f(m.obj_points[m.faces[i].vtx3-1].x,m.obj_points[m.faces[i].vtx3-1].y,m.obj_points[m.faces[i].vtx3-1].z);
}
glEnd();
glPopMatrix();
}

Now that we have created the two above functions that read the object file and display it we must create a method that adjusts the correct size,  color,  rotation around itself (as all asteroids do in reality) and of course it’s course  in the z-axis.

The logic of the course of the asteroid that we must implement is simple. It will always follow the coordinates of our spacecraft (x & y) and it will have only a fixed starting z-axis value that is going to get decreased (and by that I mean to start coming towards us or better our spacecraft) until it overtake us or collide with us. If there is a collision there will be a pop up  ‘Game Over’ message:

/*
* Method creates the asteroid that collides with our spacecraft..
*
* */
void asteroid(){
glPushMatrix();
gluLookAt( x, y, z, //this function enables us with keys z and x to rotate the camera around the space craft
x+lx, y+ly, z+lz, //keys c and v zoom in and out the space craft
0.0f, 1.0f, 0.0f);

if(collision == 0){//If we have a collision do not generate another asteroid.
if(meteorDistance < 300){
meteorDistance += 5;
}else{
meteorDistance = -700;
heightOfMeteor = moveUpDown;
}
}

//check for collision and show 'Game Over' message.
if(meteorDistance == 20 && (heightOfMeteor > (moveUpDown - 10)) && (heightOfMeteor < (moveUpDown + 10)) || collision == 1){
text("Game Over! Press key 'q' to exit!!!", 0.03);
collision = 1;
}

glTranslatef(0, heightOfMeteor, meteorDistance);//generate asteroid with starting position this of the spacecraft
glScalef(0.005f, 0.005f, 0.005f);
rotateAsteroid+=5;//how fast asteroid is coming to us
glRotatef(rotateAsteroid, 0.0f, 0.0f, 1.0f);

if(rotateAsteroid==360)//rotate asteroid around it's self.
rotateAsteroid=0;

glColor3f(1, 0.0, 0.0);//set drawing colour
DisplayModel(m);
glPopMatrix();
}

 

Lastly this is the snippet of code for the keyboard keys that will be used for the movement of the spacecraft, for the rotation of the camera around the spacecraft and also for zooming in and out :

/*
* Method that assigns keyboard keys with functionalities of the spacecraft
*
* */

void Keyboard(unsigned char key,int x,int y){
float old_lx=0;

float fraction = 0.1f;
switch(key){//Press q to exit
case 'q' : exit(0);
break;
case 'a' : //Press a to rotate spacecraft anti clockwise until 90 degrees
if(rotate < 90 && collision == 0)
rotate++;
break;
case 'd' : //Press d to rotate spacecraft clockwise until -90 degrees
if(rotate > -90 && collision == 0)
rotate--;
break;
case 'w' : //Press w to move spacecraft up
if(collision == 0)
moveUpDown++;
break;
case 's' ://Press s to move spacecraft down
if(collision == 0)
moveUpDown--;
break;
case 'l' ://Press l to rotate camera around spacecraft clockwisely
if(collision ==0){
angle -= 0.01f;
lx = sin(angle);
lz = -cos(angle);

moveSun++;
if (moveSun>540){
moveSun = -87;
}
}
break;
case 'j' : //Press j to rotate camera around spacecraft anti-clockwisely
if(collision == 0){
angle += 0.01f;
lx = sin(angle);
lz = -cos(angle);
moveSun--;
if (moveSun<-602){
moveSun = 27;
}
}
break;
case 'k' ://Press k to move camera above the spacecraft
if(collision == 0){
if(angle2<1.0){
angle2 += 0.01f;
ly = sin(angle2);

moveSun2-=0.025;
}
}
break;
case 'i' : //Press k to move camera below the spacecraft
if(collision == 0){
if(angle2>-1.0){
angle2 -= 0.01f;
ly = sin(angle2);
moveSun2+=0.025;
}
}
break;
case 'c' ://Press k to zoom into spacecraft
if(collision == 0){
x += 5*lx * fraction;
z += 5*lz * fraction;
}
break;

case 'v' :////Press k to zoom out of spacecraft
if(collision == 0){
x -= 5*lx * fraction;
z -= 5*lz * fraction;
}
break;
default : break;
}
glutPostRedisplay();//this command re-displays our model
}

Click here to download the complete implementation of space travel.

Enoy and have fun!

Sources: www.wikipedia.com



Giannis Kanellopoulos

Giannis Kanellopoulos

Biography to be completed

More Posts