Adam Smedley's profile

Game Engine & Space Game Project

Game Engine & Space Game Project
Week 1
This week was the start of the game engine tutorials in which I created the project, downloaded and included all of the necessary files. I began with creating a brand new empty project in Visual Studio and added a .cpp file for starting the game. I then downloaded all the needed files from GitHub and added the include folder and the two .dlls into the project folder. After this, I went into Visual Studio and added a new include directory and library directory by writing include, which refers to the include folder within the project folder. I then imported the StartGame.cpp code into my project and familiarised myself with some of this code.
This week I feel that the initial set up went badly as we received different instructions on what to include in our project in 217CR and 212CR so I initially tried to set this project and the various projects in 212CR in the same way, which went very wrong as they need different includes. What I feel went well this week was, after I had realised there were two different ways of including OpenGL, how easy it was to include all the necessary files.
Week 2
In this week, I  created the first class in my project using the class wizard, moved the code relating to creating asteroids to this new class and created a struct for the colour inside this classes header file. To begin with, I opened the class wizard and created a new class called Asteroid. This then created the header and .cpp files, in which I added multiple functions and variables, such as a vector 3 for the position, draw scene function, setup drawing function, constructor, start and update functions. I moved the various openGL includes into the Asteroid header file and included the Asteroid header file into the original .cpp I created last week. I also moved the code creating the asteroid into the asteroid class rather than the start game class. This resulted in the asteroid not being drawn, so I declared two instances of the asteroid class in the start game .cpp, added a setup drawing call in the setup function of the StartGame.cpp and a call to the draw scene function of the same file. After this, I created a display list called base and I created a new struct in the header file of the asteroid class. Within this struct I had three floats named r,g and b. 
struct Colour
{
 float r, g, b;
};

class Asteroid : public GameObject
{
private:
 Colour colour;
public:
 Asteroid(glm::vec3 position, Colour col);
 ~Asteroid();
 void drawScene();
 void start();
 void update(int deltaTime);
 unsigned int setupDrawing(unsigned int listBase);
};
Week 3
This week I created two new classes, one game object class and one spaceship class, each with a header file and a .cpp file. To begin with I moved a lot of the code from the asteroid header and .cpp files into the new game object header and .cpp files to create a generalised class that both the asteroid class and the spaceship class can inherit multiple variables and functions from. I then deleted the variables and functions that are included in the game object class from the asteroid class. Finally, I added specific functions and variables to the spaceship class in addition to redefining virtual functions and variables within the game object class.
What went well this week was the creation of the game object class and the asteroid and spaceship classes inheriting functions and variables from this game object class. What did not go so well was the creation of the spaceship class as there was no instruction in the tutorial on how to create it, so I had to compare what I was writing with the code for the asteroid class and what is inherited from the game object class to create the spaceship class and ensure it worked as intended.
class GameObject
 {
 protected:
    unsigned int base;
 public:
    glm::vec3 position;
    GameObject(glm::vec3 position);
    ~GameObject();
}
Week 4
This week I added movement to the spaceship class and created a game engine class for the majority of the functions and variables in the start game .cpp. I also later added acceleration into my movement code. To begin with I created two maps, one for normal keys and one for special keys, and two lambda functions for each, one for when a key is pressed and one for when the key is released. After this I created delta time, which gives the time between the current frame and the previous frame, which I will later times each movement by to make the movement smooth and consistent no matter the frame rate. I then created all the variables I need for movement including heading, rotation angle, pitch angle, turning speed and movement speed.  After this, I called the lambda functions and defined the keys I want players to press and what I wanted to happen when those keys were pressed. I also overwrite the draw scene function within the spaceship.cpp to ensure the movement code actually affects where the objects are drawn. I then created a new class called GameEngine, into which I moved the majority of the code from the StartGame.cpp so that the StartGame.cpp was very simplified and only contained the code to initially setup the game and to run the game engine which contained the code to setting up and running the rest of the game. 
I later modified this movement code to add acceleration and deceleration to my game. I did this by firstly moving the movement code out of the lambda function call and into the update function, then created a float named acceleration which I set to 0 and multiplied the movement code by the acceleration float. I then added into the lambda function calls for the up and down keys an increase and decrease to the acceleration float by 0.002 which gradually increases the speed of the spaceship when one of these buttons is pressed. I also added a lambda function call for the space key which would work as a brake for the spaceship by providing acceleration in the opposite direction to the direction the spaceship is currently traveling. Also in this week, I added a few lines of code which set the camera to follow to an object of my choosing, in this case the spaceship. To the lambda function calls I added a new call to k which would change the camera from the chase camera created above to a overhead camera and a side view camera. I then created a new bool for this function call as whenever the player pressed k it would change between cameras too quickly to follow whereas using this bool only allowed the camera to change once until the button is released and pressed again.
What went well this week was the creation of my acceleration and deceleration as it looks fairly realistic due to its slow acceleration but no reduction of acceleration unless the player presses a button to apply acceleration in the opposite direction or to provide deceleration. I decided not to add a maximum speed for my acceleration as the maximum speed we have travelled in space is 36,000mph and if the spaceship in my game got anywhere close to that then they not have enough time to react before they either collided with an asteroid or left the track so far behind it would take them a very long time to return too. What I feel went badly this week was the pitch angle not working correctly and the cameras moving too quickly to accurately switch between. The pitch angle did not work correctly as soon as the chase camera, which followed a stated object, was implemented suggesting an issue with this camera which I have not been able to fix. As mentioned earlier the camera changing issue was fixed by using a Boolean and ensuring the camera only changed when the k key was pressed and this bool stated the k key was not pressed in an earlier update function call.
void SpaceShip::update(int deltaTime)
 {
    float moveStep = MOVE_SPEED * (deltaTime / 1000.0f); //movement speed in units per second * deltaTime in sec = moveStep 
    float turningSpeed = TURNING_SPEED * (deltaTime / 1000.0f); //turning speed (degrees/sec) * deltaTime in sec = turning speed over delta time 
    if (keys['k'] && cameraActivated == false)
    {
       std::cout << "PRESSED" << std::endl;
       cameraActivated = true;
       if (GameEngine::chosenCamera<2)
       {
          GameEngine::chosenCamera += 1;
       }
       else
       { 
          GameEngine::chosenCamera = 0;
       }
    }
    if (!keys['k'] && cameraActivated == true)
    {
       std::cout << "RELEASED" << std::endl;
       cameraActivated = false;
    }
    if (specialKeys[GLUT_KEY_DOWN])
    {
       acceleration -= 0.002f; //this->position += this->heading * moveStep * acceleration;
       std::cout << "Acceleration: " << acceleration << std::endl;
    }
    if (specialKeys[GLUT_KEY_UP])
    {
       acceleration += 0.002f; //this->position += this->heading * moveStep * acceleration;
       std::cout << "Acceleration: " << acceleration << std::endl;
    }
    if (keys[' '])
    {
       if (acceleration > -0.004 && acceleration < 0.004)
       {
          acceleration = 0.0f;
       }
       else if (acceleration > 0.004)
       {
       acceleration -= 0.004f;
       }
       else if (acceleration < -0.004)
       {
          acceleration += 0.004f;
       }
       std::cout << acceleration << std::endl;
    }
}
void GameEngine::displayFunc()
{
   if (chosenCamera == 0) // CHASE CAMERA
   {
      //If we have no object to follow just put the cam in a static position. 
      if (cameraFollow != NULL)
      {
         //Hardcoded camera variables for the distance bewteen camera and object and y axis distance between camera and object.
         float distance = 8;
         float yAm = 2;
         gluLookAt(cameraFollow->position.x - (cameraFollow->heading.x * distance), cameraFollow->position.y - (cameraFollow->heading.y * distance) + yAm, cameraFollow->position.z - (cameraFollow->heading.z * distance), cameraFollow->position.x, cameraFollow->position.y, cameraFollow->position.z, 0.0, 1.0, 0.0);
      }
   }
   else if (chosenCamera == 1) // SIDE CAMERA
   {
      gluLookAt(0.0, 10.0, 30.0, 0.0, 0.0, 0.0, 0.0, -1.0, -5.0);
   }
   else if (chosenCamera == 2) // OVERHEAD CAMERA
   {
      gluLookAt(0.0, 300.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0, -45.0);
   }
}

 void GameEngine::addGameObject(GameObject * gameobject, bool camFollow)
 {
    if (camFollow)
    { 
      cameraFollow = gameobject;
    }
Week 5
In this week I created basic collision detection for my game and fixed multiple issues from earlier weeks as well as finishing anything I needed to get done as it is the last week to work on this project. To begin with I created a new header file for a general collider class with multiple variables and a single function, all of which except one glm::vec3 were set to virtual, meaning every class inheriting from this header must redefine these variables and the function. I then created a header and .cpp for the box collider class. I then added to this box collider class multiple functions to get the min and max of the collider on each axis and one to check if this box collider collides with another collider. I then created a new box collider within the asteroid and spaceship .cpp's so that each new object of one of these two would have a box collider. I finally added a check to the update function to check if any collision occur each frame.
I then later modified this code so that collisions involving a spaceship would send the spaceship in the opposite direction to which they were currently heading, bouncing off, and removed the code which would destroy the asteroid if it was involved in a collision. This week I also built the track to race around in the game. I created the straight lines with simple for loops and created the corners using a formula which uses Pi, radius, sine and cosine. 
What I feel went well this week was the creation of the track as, while it took a while to find out how, the algorithm to create the corners was fairly easy to implement once I knew what I was doing and had the correct coordinates, and radius for each corner. What did not go so well was the collision between the spaceship and an asteroid as in a some occasions the spaceship has gotten stuck inside the collision box for one of the asteroids.
bool CubeCollider::collidesWith(Collider * other)
{
   if (other == NULL)
   {
      return false;
   } 
   bool overlapX = other->minX() <= this->maxX() && other->maxX() >= this->minX();
   bool overlapY = other->minY() <= this->maxY() && other->maxY() >= this->minY();
   bool overlapZ = other->minZ() <= this->maxZ() && other->maxZ() >= this->minZ();
    return overlapX && overlapY && overlapZ;
}

void SpaceShip::collides(Collider* other)
{
   if (acceleration < -0.2 || acceleration > 0.2)
   {
      collideDelay = 1;
   }
   acceleration = 0 - acceleration;
}

void SpaceShip::update(int deltaTime)
{
   if (collideDelay > -1)
   {
      if (collideDelay == 0)
      {
         collideDelay = -1;
         acceleration = acceleration / 3;
         std::cout << "Acceleration reduced" << std::endl;
      } 
   collideDelay -= 0.1;
   std::cout << "Delay until speed affected by collision: " << collideDelay << std::endl;
   }
}
Final Game
In my final, finished space racing game many physics features are included. These include acceleration, deceleration, collision detection, rotation and creation of the corners of the track using a formula to make a semicircle of game objects. 
The movement within my game is fairly good although it could be slightly more realistic. This is because the speed will slowly but steadily increase as long as either up or down is pressed, due to the increase in acceleration whenever one of these keys is pressed and the movement being moved into the update function and out of any function calls for any key presses which allows the spaceship to continue moving at the same speed whenever any or all keys are released. Below is the code for accelerating and decelerating, which is simply applying acceleration in the opposite direction, as well as for moving.
float moveStep = MOVE_SPEED * (deltaTime / 1000.0f); 
this->position += this->heading * moveStep * acceleration;
if (specialKeys[GLUT_KEY_DOWN])
 {
  acceleration -= 0.002f;
 }
 if (specialKeys[GLUT_KEY_UP])
 {
  acceleration += 0.002f;
 }
The code above is all contained within the update function within the spaceship.cpp which is called every frame. Without the inclusion of movestep, which is multiplied by the delta time, the spaceship would move faster with a higher fps than with a lower fps, which is not what I wanted. Due to this, I had to times the move step by the delta time, divided by 1 thousand, and then times the heading by the movestep to give how far the spaceship should move each frame.
In addition to this, I also have movement code for rotating left and right as well as for pitching up and down. This was done in a similar way to the original way I created the movement code, with the left and right keys controlling rotating left and right and the page up and page down keys controlling pitching up and down. How much the spaceship rotated or pitched was controlled by the turning speed, which was also multiplied by the delta time for the same reason the move step was, and by the rotation angle and the pitch angle, which are increased or decreased using left, right, page up and page down. These variables at then passed into the following four lines of code, with the first two being contained in the draw scene function and the second two being contained at the end of the update function only followed by the glutpostredisplay().
glRotatef(pitchAngle, 0.0f, 0.0f, 1.0f);
glRotatef(rotationAngle, 0.0f, 1.0f, 0.0f);
this->heading = glm::rotate(this->startingHeading, glm::radians(rotationAngle), glm::vec3(0.0f, 1.0f, 0.0f));
his->heading = glm::rotate(this->heading, glm::radians(pitchAngle), glm::vec3(0.0f, 0.0f, 1.0f));
The collision detection in my game works fairly well although it could use some improvements and I could have done with creating some other types of collision boxes other than just cube colliders. My collision detection works by checking each and every possible collision to see whether any two collision boxes in the game collide, and if any objects do collide then the collide function for each object is called. This method of collision checking could be improved as checking whether any collision occurs between every object in the game every frame is quite costly and will likely slow down the game a fair bit. To improve this, I could have checked whether any objects overlap on all three axes x, y and z as any collision that occurs would need two objects which overlap on all three axes. This method could be better than my current method of collision detection but it could also be very similar as we would still need to compare each objects positions on the x, y and z planes to see whether they overlap with another object or not. 
If a collision is detected in my game then two functions will be called, these will be the collides with functions of the two objects colliding and contain the code to be run whenever a collision occurs involving that object. Initially, the asteroids collide with function contained code to destroy the asteroid whenever it is involved in a collision, but I decided against using this as it was not only unrealistic but it also was not suitable to be used for my track and for the obstacles within my track as it would result in the asteroids which make up the borders of my track and the asteroids that are obstacles within my track being destroyed whenever the spaceship collides with them. For the borders of my track this would be bad as if too many collisions occurred then the track might become unrecognisable and the player might not know where to race around, similarly if the obstacles are destroyed upon colliding with another object then there would be little reason not to crash into the obstacles as, although you lose some speed, nothing bad enough happens to stop players from destroying these obstacles, and in future laps it would be easier to race around the track with a high speed without worrying about hitting obstacles.
Within my game, all that happens when the spaceship collides with another object is that the speed of the asteroid is checked, if it is then a delay is activated through a float and then the acceleration of the spaceship is reversed. Once the delay runs out then more code will activate causing the spaceship to lose a third of its speed. If the spaceship is going fast enough for this if statement to be false then the acceleration will be divided by 3 and the acceleration will be set to 0 minus this new acceleration. 
Within my StartGame.cpp I created my track through the use of five for loops. One of these for loops I used to create the two straights for my track through calling the add game object function in the game engine class four times. Within these calls I defined the z position as differently for each of the four function calls but they would be the same each time the for loops activated so that all the objects each created would be in a straight line. 
for (int i = -5; i < 30; i++)
 {
  //first lane of asteroids
  GameEngine::addGameObject(new Asteroid(glm::vec3(i*5, 0.0, -10.0), { 0.0f, 0.0f, 1.0f }), false);
  GameEngine::addGameObject(new Asteroid(glm::vec3(i*5, 0.0, 10.0), { 1.0f, 0.0f,0.0f }), false);
  //second lane of asteroids
  GameEngine::addGameObject(new Asteroid(glm::vec3(i * 5, 0.0, -30.0), { 0.0f, 0.0f, 1.0f }), false);
  GameEngine::addGameObject(new Asteroid(glm::vec3(i * 5, 0.0, -50.0), { 1.0f, 0.0f,0.0f }), false);
 }
As shown above, the x position of my asteroids was defined by the for loop multiplied by 5, to give some spacing between the asteroids. My corners required their own for loop for each outer and inner loop, resulting in four different four loops.
for (float i = 0.0; i <= twoPI / 2; i += 0.1)//left outer corner
  {GameEngine::addGameObject(new Asteroid(glm::vec3(-30-(sin(i)*outerRadius),0, -20-(cos(i)*outerRadius)), { 0.0f,1.0f,0.0f }), false);}
 for (float i = 0.0; i <= twoPI / 2; i += 0.4)//left inner corner
  {GameEngine::addGameObject(new Asteroid(glm::vec3(-30 - (sin(i)*innerRadius), 0, -20 - (cos(i)*innerRadius)), { 0.0f,1.0f,0.0f }), false);}
 for (float i = 0.0; i <= twoPI / 2; i += 0.1)//right outer corner
  {GameEngine::addGameObject(new Asteroid(glm::vec3(150 + (sin(i)*outerRadius), 0, -20 + (cos(i)*outerRadius)), { 0.0f,1.0f,0.0f }), false);}
 for (float i = 0.0; i <= twoPI / 2; i += 0.4)//right inner corner
  {GameEngine::addGameObject(new Asteroid(glm::vec3(150 + (sin(i)*innerRadius), 0, -20 + (cos(i)*innerRadius)), { 0.0f,1.0f,0.0f }), false);}
As shown, the formula to create the corners of my track was much more complicated than creating the straight line parts of my track. This is because to create a semicircle of game objects I needed to use sine and cosine to calculate where each of the game objects should be placed. Also, the two for loops for the left corner minus the sine and the cosine whereas the two for loops for the right corner plus the sine and the cosine because this flips how the game objects are arranged as minus sine and cosine cause the game objects to be created on the minus side of the x axis while adding sine and cosine cause the game objects to be created on the positive side of the x axis. In addition to this, the radius of the semi-circle was required, which required two different variables with one for the outer side of the corners and one for the inner side of the corners. Finally, twoPI was required, which was literally 2 times Pi. 

Overall, I am fairly happy with this project and the various physics features included, although there are a few areas which I think could be greatly improved. What I am most happy with is the acceleration and the generation of the corners of the track. This is because the acceleration is fairly realistic and follows the laws of physics as there is no force stopping or slowing the spaceship in space so it keeps the same accelerations unless acceleration is provided in the opposite direction. I am happy about creating the corners of my track as when I tried to do it manually it did not look good and it took a while to figure out how to apply this formula to my code but once I had then it was fairly easy to actually implement. What did not go so well was the fact that speed was not kept in the same direction when turning, collision is slightly inaccurate sometimes due to cube colliders rather than sphere colliders on the asteroids and the issues relating to the pitching up and down of the spaceship. I learnt quite a lot during this module, especially about how different files interact with each other, how to include other files and about class structure including how to use static variables and functions. 
Video



Game Engine & Space Game Project
Published:

Game Engine & Space Game Project

2nd Year University Project to create a game engine using OpenGL and make a space racing game within this newly created game engine.

Published: