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\$
4
  • \$\begingroup\$ Long life to "magic numbers" in code! \$\endgroup\$
    – wondra
    Jul 31, 2014 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\$ Jul 31, 2014 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, 2014 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\$ Jul 31, 2014 at 17:59

3 Answers 3

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\$
4
  • \$\begingroup\$ Seems logical now that you say it. \$\endgroup\$ Jul 31, 2014 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\$ Jul 31, 2014 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\$ Aug 1, 2014 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\$ Aug 1, 2014 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\$
2
  • \$\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\$ Jul 31, 2014 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\$ Jul 31, 2014 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\$
5
  • \$\begingroup\$ If you fail to implement it, I v got some old code for line segment intersection somewhere. \$\endgroup\$
    – wondra
    Jul 31, 2014 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\$ Jul 31, 2014 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, 2014 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\$ Jul 31, 2014 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, 2014 at 18:46

You must log in to answer this question.

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