1
\$\begingroup\$

I'm making space invaders (in OpenGL/SDL) and I'm currently having issues with getting the collisions (box collisions) between the bullets and the enemies to be properly detected. In my update() function, I have a nested for loop which iterates through all of entities (for each bullet) detecting if each bullet has collided with it. To do this, I have a function checkCollision() in which I'm calculating the dimensions of each enemy and bullet that gets passed in and then checking if the "boxes" for each entity are intersecting:

Function for checking collisions

bool checkCollision(Enemy* anEnemy, Bullet* aBullet) {
    float enemyTop = anEnemy->y + (anEnemy->height / 2.0f);
    float enemyBottom = anEnemy->y - (anEnemy->height / 2.0f);
    float enemyRight = anEnemy->x + (anEnemy->width / 2.0f);
    float enemyLeft = anEnemy->x - (anEnemy->width / 2.0f);

    float bulletTop = aBullet->y + (aBullet->height / 2.0f);
    float bulletBottom = aBullet->y - (aBullet->height / 2.0f);
    float bulletLeft = aBullet->x - (aBullet->width / 2.0f);
    float bulletRight = aBullet->x + (aBullet->width / 2.0f);
    
    //This is supposed to check that if the objects are not intersecting, return false
    if (
        (bulletBottom > enemyTop) ||
        (bulletTop < enemyBottom) ||
        (bulletLeft > enemyRight) ||
        (bulletRight < enemyLeft)
        ) {
        return false;
    }
    else {
        return true;
    }
}

Code to check collisions for each enemy in update function

//Check if each of the bullets has hit any of the enemies
    for (int i = 0; i < bullets.size(); i++) {
        for (int j = 0; j < entities.size(); j++) {
            if (entities[j] != nullptr && bullets[i] != nullptr) {
                if (checkCollision(entities[j], bullets[i])) {
                    std::cout << "bullets y position: " << bullets[i]->y << std::endl;
                    std::cout << " hit enemy y position: " << entities[j]->y << std::endl;
                    std::cout << "bullets x position: " << bullets[i]->x << std::endl;
                    std::cout << " hit enemy x position: " << entities[j]->x << std::endl;
                    delete entities[j];
                    entities[j] = nullptr;
                    delete bullets[i];
                    bullets[i] = nullptr;
                }
            }
        }
    }

For some reason, when the bullet it shot from the spaceship (player), the collisions are detected before the bullet even reaches the enemy on the screen and I get the following console output:

bullet y position: 0.610021
enemy y position: 1.85
bullet x position: 0
enemy x position: -1.0335

How should I be structuring the collision detection differently so that the objects are actually intersecting before the collision is detected?

Edit:

I was able to modify the code to make the collisions happen closer to the enemies but they still aren't very precise. I had been scaling the entities by the widths and heights so I decided to reduce the scaling factor. The problem now is that the bullets are taking out two enemies before they are destroyed.

Full Code for reference

The image below shows the instance in which the collision is being detected between the bullet and the enemy

enter image description here

\$\endgroup\$
7
  • \$\begingroup\$ You are checking collision through boundaries, so i think it is not possible to have same value of bullet and enemy at any point. Second is might be possible it is the issue of your image canvas, may be your images have extra white space... \$\endgroup\$ Commented Mar 16, 2016 at 19:59
  • \$\begingroup\$ @HamzaHasan I think there is some extra white space but I don't think that it would be enough to make it so that the collisions are detected when the entities are so far apart. I updated the post with an image of when the collision is being detected. \$\endgroup\$ Commented Mar 16, 2016 at 21:54
  • 1
    \$\begingroup\$ put a breakpoint and inspect the values of the height / width. Use "set next statement" to re-execute the collision check, step in the function, you'll see immediately what is wrong in your condition. At first sight I'd say it looks OK so you have a value problem in your structures. Also the float could play a role. 2d games i've written use integer pixels, float is only used for intermediary calculations. \$\endgroup\$
    – v.oddou
    Commented Mar 17, 2016 at 1:15
  • \$\begingroup\$ @v.oddou I was able to modify the code to make the collisions happen closer to the enemies. I had been scaling the entities by the widths and heights so I decided to reduce the scaling factor. The problem now is that the bullets are taking out two enemies before they are destroyed. I added a link to the full code for reference. \$\endgroup\$ Commented Mar 17, 2016 at 1:45
  • \$\begingroup\$ Just so that we are clear, is your origin (your screen coordinates) at bottom left to be (0, 0)? I'm assuming bottom of the screen is Y=0, because your calculation shows bulletBottom = BulletCenter.Y - h/2 and bulletTop = center.Y + h/2 (so Y-up), but your X for the enemy is negative... So now, if I assume origin is dead center of the screen, then the part that would get you is on the negative quadrant, if you're doing 'if (bulletLeft > enemyRight)' it needs to switch to '<'. So, where is the origin (0,0) at? \$\endgroup\$
    – HidekiAI
    Commented Mar 17, 2016 at 2:00

1 Answer 1

1
\$\begingroup\$

When colliding two boxes, you must do more than check if one has edges inside the other.

_________
|A ___  |
|  |B|  |
|  |_|  |
|_______|

If checking B has edge inside A, it will be correct, but if checking A inside B, it will be wrong.

I approach this by checking if A has any corners inside B, AND if B has any corners inside A. Corners instead of edges as that may then be applied to rotated boxes as well.


You'd really benefit from making an AxisAlignedBoundingBox class. This would allow general flexibility with the use of an object's bounds, allowing it to be used for more than collision.


if (
    (bulletBottom > enemyTop) ||
    (bulletTop < enemyBottom) ||
    (bulletLeft > enemyRight) ||
    (bulletRight < enemyLeft)
    ) {
    return false;
}
else {
    return true;
}

Bad code!
instead

 return !(
    (bulletBottom > enemyTop) ||
    (bulletTop < enemyBottom) ||
    (bulletLeft > enemyRight) ||
    (bulletRight < enemyLeft) );
\$\endgroup\$
6
  • 2
    \$\begingroup\$ But...but that's...the same code. \$\endgroup\$
    – Kyralessa
    Commented Mar 22, 2016 at 21:16
  • \$\begingroup\$ Not an implementation of my solution, just a deuglifying refactor \$\endgroup\$
    – Khlorghaal
    Commented Mar 22, 2016 at 21:18
  • \$\begingroup\$ So why even include the bad code? What does it add to the answer? \$\endgroup\$
    – Kyralessa
    Commented Mar 22, 2016 at 21:21
  • \$\begingroup\$ It shows the proper way to return a bool? And bad shown for comparison \$\endgroup\$
    – Khlorghaal
    Commented Mar 22, 2016 at 21:26
  • 1
    \$\begingroup\$ How does this trivial rewrite answer the problem that the asker has a problem where his tests do not reflect his theory? "Use AABBs" is not an answer if the analogous coordinate ordering does not result in the right thing. \$\endgroup\$ Commented Mar 23, 2016 at 15:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .