Need help with implementing collision detection using the Separating Axis Theorem

Posted by Eddie Ringle on Stack Overflow See other posts from Stack Overflow or by Eddie Ringle
Published on 2010-06-01T02:07:24Z Indexed on 2010/06/01 2:13 UTC
Read the original article Hit count: 345

So, after hours of Googling and reading, I've found that the basic process of detecting a collision using SAT is:

for each edge of poly A
    project A and B onto the normal for this edge
    if intervals do not overlap, return false
end for

for each edge of poly B
    project A and B onto the normal for this edge
    if intervals do not overlap, return false
end for

However, as many ways as I try to implement this in code, I just cannot get it to detect the collision. My current code is as follows:

for (unsigned int i = 0; i < asteroids.size(); i++) {
        if (asteroids.valid(i)) {
            asteroids[i]->Update();

            // Player-Asteroid collision detection
            bool collision = true;
            SDL_Rect asteroidBox = asteroids[i]->boundingBox;

            // Bullet-Asteroid collision detection
            for (unsigned int j = 0; j < player.bullets.size(); j++) {
                if (player.bullets.valid(j)) {
                    Bullet b = player.bullets[j];

                    collision = true;
                    if (b.x + (b.w / 2.0f) < asteroidBox.x - (asteroidBox.w / 2.0f)) collision = false;
                    if (b.x - (b.w / 2.0f) > asteroidBox.x + (asteroidBox.w / 2.0f)) collision = false;
                    if (b.y - (b.h / 2.0f) > asteroidBox.y + (asteroidBox.h / 2.0f)) collision = false;
                    if (b.y + (b.h / 2.0f) < asteroidBox.y - (asteroidBox.h / 2.0f)) collision = false;

                    if (collision) {
                        bool realCollision = false;

                        float min1, max1, min2, max2;

                        // Create a list of vertices for the bullet
                        CrissCross::Data::LList<Vector2D *> bullVerts;
                        bullVerts.insert(new Vector2D(b.x - b.w / 2.0f, b.y + b.h / 2.0f));
                        bullVerts.insert(new Vector2D(b.x - b.w / 2.0f, b.y - b.h / 2.0f));
                        bullVerts.insert(new Vector2D(b.x + b.w / 2.0f, b.y - b.h / 2.0f));
                        bullVerts.insert(new Vector2D(b.x + b.w / 2.0f, b.y + b.h / 2.0f));
                        // Create a list of vectors of the edges of the bullet and the asteroid
                        CrissCross::Data::LList<Vector2D *> bullEdges;
                        CrissCross::Data::LList<Vector2D *> asteroidEdges;
                        for (int k = 0; k < 4; k++) {
                            int n = (k == 3) ? 0 : k + 1;
                            bullEdges.insert(new Vector2D(bullVerts[k]->x - bullVerts[n]->x,
                                                    bullVerts[k]->y - bullVerts[n]->y));
                            asteroidEdges.insert(new Vector2D(asteroids[i]->vertices[k]->x - asteroids[i]->vertices[n]->x,
                                                        asteroids[i]->vertices[k]->y - asteroids[i]->vertices[n]->y));

                        }

                        for (unsigned int k = 0; k < asteroidEdges.size(); k++) {
                            Vector2D *axis = asteroidEdges[k]->getPerpendicular();
                            min1 = max1 = axis->dotProduct(asteroids[i]->vertices[0]);
                            for (unsigned int l = 1; l < asteroids[i]->vertices.size(); l++) {
                                float test = axis->dotProduct(asteroids[i]->vertices[l]);
                                min1 = (test < min1) ? test : min1;
                                max1 = (test > max1) ? test : max1;
                            }
                            min2 = max2 = axis->dotProduct(bullVerts[0]);
                            for (unsigned int l = 1; l < bullVerts.size(); l++) {
                                float test = axis->dotProduct(bullVerts[l]);
                                min2 = (test < min2) ? test : min2;
                                max2 = (test > max2) ? test : max2;
                            }
                            delete axis; axis = NULL;
                            if ( (min1 - max2) > 0 || (min2 - max1) > 0 ) {
                                realCollision = false;
                                break;
                            } else {
                                realCollision = true;
                            }
                        }

                        if (realCollision == false) {
                            for (unsigned int k = 0; k < bullEdges.size(); k++) {
                                Vector2D *axis = bullEdges[k]->getPerpendicular();
                                min1 = max1 = axis->dotProduct(asteroids[i]->vertices[0]);
                                for (unsigned int l = 1; l < asteroids[i]->vertices.size(); l++) {
                                    float test = axis->dotProduct(asteroids[i]->vertices[l]);
                                    min1 = (test < min1) ? test : min1;
                                    max1 = (test > max1) ? test : max1;
                                }
                                min2 = max2 = axis->dotProduct(bullVerts[0]);
                                for (unsigned int l = 1; l < bullVerts.size(); l++) {
                                    float test = axis->dotProduct(bullVerts[l]);
                                    min2 = (test < min2) ? test : min2;
                                    max2 = (test > max2) ? test : max2;
                                }
                                delete axis; axis = NULL;
                                if ( (min1 - max2) > 0 || (min2 - max1) > 0 ) {
                                    realCollision = false;
                                    break;
                                } else {
                                    realCollision = true;
                                }
                            }
                        }
                        if (realCollision) {
                            player.bullets.remove(j);

                            int numAsteroids;
                            float newDegree;
                            srand ( j + asteroidBox.x );
                            if ( asteroids[i]->degree == 90.0f ) {
                                if ( rand() % 2 == 1 ) {
                                    numAsteroids = 3;
                                    newDegree = 30.0f;
                                } else {
                                    numAsteroids = 2;
                                    newDegree = 45.0f;
                                }
                                for ( int k = 0; k < numAsteroids; k++)
                                    asteroids.insert(new Asteroid(asteroidBox.x + (10 * k), asteroidBox.y + (10 * k), newDegree));
                            }
                            delete asteroids[i];
                            asteroids.remove(i);
                        }
                        while (bullVerts.size()) {
                            delete bullVerts[0];
                            bullVerts.remove(0);
                        }
                        while (bullEdges.size()) {
                            delete bullEdges[0];
                            bullEdges.remove(0);
                        }
                        while (asteroidEdges.size()) {
                            delete asteroidEdges[0];
                            asteroidEdges.remove(0);
                        }
                    }
                }
            }
        }
    }

bullEdges is a list of vectors of the edges of a bullet, asteroidEdges is similar, and bullVerts and asteroids[i].vertices are, obviously, lists of vectors of each vertex for the respective bullet or asteroid.

Honestly, I'm not looking for code corrections, just a fresh set of eyes.

© Stack Overflow or respective owner

Related posts about c++

Related posts about game-development