Collision detection
Last updated: 17-06-2006
Project: Game Programming with DirectX 9
Prerequisites: Completion of all the preceding tutorials in this project is required.
Downloads: DirectXCollisionDetection.zip
In this version of our application we are going to implement simple collision detection.
The source code that comes with this tutorial (see Downloads section above) extends the previous solution.
To see all the tutorials that belong to this project click on the project name above.
Collision detection can be done in various ways. One of the most simple and common techniques to implement collision detection is the "bounding box" collision detection algorithm. As the name suggests, in order to determine if two objects collide, we surround these objects with boxes and then check if coordinates of the boxes intersect with a certain degree of precision.
This method is very simple to implement but is not very precise. Consider our situation where asteroids and fireballs are both spheres. If you surround two spheres with boxes and let them pass very close to each other, then the "bounding box" algorithm will probably every once in a while detect collision between the objects where it shouldn't. That's because it refers to a sphere as a box. One way to address this problem is for example making the bounding box smaller. Special problems arise when one of the objects is considerably bigger than the other.
By examining the picture above, you will see that two green fireballs are a legitimate 'hit' but the red one is not. Although the red fireball clearly misses the blue asteroid sphere, it will be detected as a 'hit' by the "bounding box" collision detection algorithm.
In our program we are not going to be concerned with precision that much. At this level we only want to demonstrate how a very simple collision detection algorithm works.
The isCollisionDetected() method takes position vectors of two objects as parameters and determines if there is any collision between them by calculating the distance between the position vectors. The distance is calculated according to the well known and publicly available formula from Linear Algebra.
bool isCollisionDetected(const D3DXVECTOR3 & pos1, const D3DXVECTOR3 & pos2){
    float x = pos1.x - pos2.x;
    float y = pos1.y - pos2.y;
    float z = pos1.z - pos2.z;
    float distance = (float)sqrt(x*x + y*y + z*z);
    return distance <= 2.0f;
}
After playing our game for a while you might find evidence that this method is not as precise as we would like it to be, but it certainly works. While calculating the distance between two position vectors "collision anomalies" may still occur. Collision detection is a tricky subject and it is up to the programmer to determine what algorithm to use in each situation. There are many algorithms for collision detection available, inclusive determining collision on a pixel level, which is out of scope of this project.
Inside the drawScene() method we add collision processing after all the objects were drawn:
// collision detection
    
for(vector<Fireball>::iterator iterAsteroids = asteroids.begin(); 
        iterAsteroids != asteroids.end(); ++iterAsteroids){
    // for each fireball check if it collides with this asteroid
    for(vector<Fireball>::iterator iterFireballs = fireballs.begin(); 
        iterFireballs != fireballs.end(); ++iterFireballs){
            if(isCollisionDetected(iterAsteroids->position, 
                iterFireballs->position)){
                // make them invisible by relocating to their min/max positions 
                // so they will be removed by the eraseInvisibleObjects() later
                iterAsteroids->position.z = MIN_ASTEROID_Z;
                iterFireballs->position.z = MAX_FIREBALL_Z;

                // update score
                SendMessage(hwndControlPanel, WM_COMMAND, ID_SCORE, 0);

            }
    }

    // check if box collides with this asteroid
    if(isCollisionDetected(iterAsteroids->position, posBox)){
        isGameOver = true;
        break;
    }
}

What happens here is looping through all the elements inside asteroids vector and then looping through all the elements of fireballs vector (the balls fired by the box) and calling the isCollisionDetected() method while passing the asteroid's and the fireball's position vectors as parameters.
If collision is detected we transform both objects to the invisible range so it will be erased by the eraseInvisibleObjects() method after we have finished to render the scene and we also update the score by sending a message to the hwndControlPanel window.
We then check if our flying box collides with an asteroid, if yes - the game is over. Notice that we use the isCollisionDetected() method again, just this time with the asteroid's and the box's position vectors as parameters.
After the scene is rendered we check if game is over to send the appropriate message to a wndControlPanel window and we erase all the invisible objects.
if(isGameOver){
    SendMessage(hwndControlPanel, WM_COMMAND, ID_GAME_OVER, 0);
}
eraseInvisibleObjects();
Inside the DlgProcControlPanel() method we add two new cases: a first case to update the score (how many asteroids were hit) and a second case to process the "game over" event. The code bellow is self-explanatory.
case ID_SCORE:
    score += 1;
    SetDlgItemText(hwnd, ID_SCORE, (LPCSTR)CStr(score).c_str());
break;

case ID_GAME_OVER:
{
    SetDlgItemText(hwnd, ID_GAME_OVER, "GAME OVER!");
    eraseAllObjects();
}
break;
We did slightly modify some other sections of the program, like for example inside the WndProc() method we generate fireballs only if the game is not over yet. Similarly inside the WinMain() method we generate asteroids only if game is not over yet.
In the next tutorial we work with color, material and light.