0
\$\begingroup\$

I'm trying to do collision detection between bullets and enemies in my game. But it's very unpredictable and sometimes it works, sometimes it doesn't (bullet goes straight through without doing anything) and sometimes it kills the script returning "Uncaught TypeError: Cannot read property 'x' of undefined".

The collision detection is just a trivial for loop like this:

if (enemies.length > 0 && bullets.length > 0) {
    for (a = 0; a < bullets.length; a++) {
        for (b = 0; b < bullets.length; b++) {
            if (bullets[a].x + 36 >= enemies[b].x && bullets[a].x <= enemies[b].x + 56 && bullets[a].y >= enemies[b].y && bullets[a].y + 6 <= enemies[b].y + 69) {
                bullets[a].Delete();
                enemies[b].Delete();
            }
        }
    }
}

The .Delete() function is just a reference to arr.splice(index, 1) in the objects. Of course I didn't expect this to be perfect, but I'm surprised by the random behaviour of it. What could cause this and how can I improve the collision detection?

\$\endgroup\$
  • \$\begingroup\$ Long life to "magic numbers" in code! \$\endgroup\$ – wondra Jul 31 '14 at 17:47
  • \$\begingroup\$ @wondra Could you elaborate? The "long life" sounds logical without even knowing what it is. What causes it and what's the solution? \$\endgroup\$ – justanotherhobbyist Jul 31 '14 at 17:51
  • \$\begingroup\$ I was just being sarcastic about the terrible practice of magic numbers in code. see my answer below, its more helpfull. \$\endgroup\$ – wondra Jul 31 '14 at 17:56
  • \$\begingroup\$ Oh, well if there's any other way to handle an array that can be anything between 0 and 100 let me know. :) \$\endgroup\$ – justanotherhobbyist Jul 31 '14 at 17:59
1
\$\begingroup\$

If I understand correctly, you have a bullet "object" of size 36x6 colliding with an enemy of size 56x69.

Let's take a look at each step of the collision:

bullets[a].x + 36 >= enemies[b].x

This will test if the bullet right side is to the right of the enemy's left side. Right after, you have this:

bullets[a].x <= enemy[b].x + 56

Here, you test that the left side of the bullet is to the left of the right side of the player. So far, the bullet must be fully contained horizontally.

Next, vertical collisions:

bullets[a].y >= enemies[b].y

This tests that the upper bound of the bullet is below or equal to the upper bound of the enemy. And then , you have:

bullets[a].y + 6 <= enemies[b].y + 69

Which does the same for the lower bound.

From what I can read, the bullet must be totally contained within the enemy for the collision to occur. So you will sometimes see the bullet "touch" the enemy, but not being totally inside, and the collision will not occur.

Worst than that, if on one update tick the bullet is off on the left, and on the other is off on the right, the bullet will have passed through the enemy without killing it.

Little ASCII arts to illustrate:

The bullet starts by being off on the left side:

  --------- // Tick 1 - Does not trigger collision
  |       |
-----     |
  |       |
  ---------

Then you increment the bullet's position:

  --------- // Tick 2 - Does not trigger collision
  |       |
  |     -----
  |       |
  ---------

The bullet will never have triggered the collision. But this, on the other hand, will:

  --------- // This triggers collision
  |       |
  | ----- |
  |       |
  ---------

If you want to have better collision management that does not require the bullet to be fully contained, but only to touch the enemy, have a look at AABB collisions:

For the crash side of things, Evan already found the problem, so refer to it's answer.

\$\endgroup\$
  • \$\begingroup\$ Seems logical now that you say it. \$\endgroup\$ – justanotherhobbyist Jul 31 '14 at 18:06
  • \$\begingroup\$ I've updated my hitboxes like in this video: youtube.com/watch?v=8b_reDI7iPM but I still get the same results of the bullet just going straight through the enemy (not always). Even when slowing down the speed to 2px per frame (width is 36 and 56, it should detect a collision between all those frames). Any clue what I could be doing wrong? \$\endgroup\$ – justanotherhobbyist Jul 31 '14 at 21:14
  • \$\begingroup\$ @hustlerinc In this case, please update your question or start another one. I cannot look at the link you sent me, so the actual code would be nice to have. \$\endgroup\$ – Alexandre Desbiens Aug 1 '14 at 11:42
  • \$\begingroup\$ Nevermind I solved it, the problem was I was checking bullets.length in both for loops. Consequences of being lazy and copy/paste'ing code. Noticed it crashed once the bullets became more than the ships and figured it out. \$\endgroup\$ – justanotherhobbyist Aug 1 '14 at 12:58
3
\$\begingroup\$

At first glance, I would say you're going to have a problem with splicing during the loop. Say you're at index 1 and you have a collision. You then remove that spot, and now everything moves up a space. But your loop counter still increments. So what used to be in the second index is in the first one, and you went from the first to the second, skipping it entirely. This explains why some collisions are never checked, and it would mean that if it happens in the last spot, you would go out of bounds.

\$\endgroup\$
  • \$\begingroup\$ Yeah I was thinking that too, but one of the enemies can't be killed first even though nothing has been spliced. If the other 2 enemies are dead though then I can kill it. It's really confusing me. \$\endgroup\$ – justanotherhobbyist Jul 31 '14 at 18:00
  • \$\begingroup\$ Nevermind, judging by AlphaSpirits answer it seems it's 2 problems causing this. And the bullet not hitting has nothing to do with the loop. \$\endgroup\$ – justanotherhobbyist Jul 31 '14 at 18:08
2
\$\begingroup\$

You problem is in speed of checking for collisions.
there are two possible solutions:

  1. Check several (n) times each frame (=move 1/n speed n times)
  2. Instead of point-rectangle intersection perform line segment - rectangle intersection

enter image description here the first solution is fast and easily implemented, on the other hand it doesnt really solve anything, if your bullets are too fast, they will still miss.
At cost of more difficult to implement collision detection(and some performance) you can get perfect results even for rectangles with lim size -> 0.
At the end the decision is up to you.

\$\endgroup\$
  • \$\begingroup\$ If you fail to implement it, I v got some old code for line segment intersection somewhere. \$\endgroup\$ – wondra Jul 31 '14 at 17:54
  • \$\begingroup\$ In most cases, if you limit the speed of the bullet to it's width + the enemy's width, you should be fine. Having small fast bullets like in your image isn't the best option. You could add that as a 3rd solution to your post. \$\endgroup\$ – Alexandre Desbiens Jul 31 '14 at 18:31
  • \$\begingroup\$ Your proposed solution would work only for axis-aligned bullets (and even for them only sometimes - you would have to limit speed of bullet to enemy width /2 and have constant fps). Even if you limit the speed of bullets, you can miss corners of enemy rectangle (you know what i mean? Emeny width isnt the same from all directions) \$\endgroup\$ – wondra Jul 31 '14 at 18:36
  • \$\begingroup\$ I totally agree with you. But in the case OP is making a side scrolling shooter, this should be more than enough. \$\endgroup\$ – Alexandre Desbiens Jul 31 '14 at 18:43
  • \$\begingroup\$ I disagree - I had this problem exactly in 2D side scrolling shooter. For example shotgun shooting one bullet(or several next each other) looks weird , it feels more like shotgun if it is spreading. And Space Imapact-like games need it too. \$\endgroup\$ – wondra Jul 31 '14 at 18:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.