Collision Checking

Sage
Posts: 1,066
Joined: 2004.07
Post: #1
I have been trying to get a good method of collision checking between sprites in SDL for about a week now. I realized that I should start simple and make some very rough code. I tried the following code in my main.cpp function. I call this code right before updating, clearing, and drawing the sprites so that it will counter any movement (otherwise sprites could be a few pixels into each other). Problem is that it sticks the two sprites together and makes them move as one. If anyone can figure it out, let me know.

Just so you know, the xset and yset functions are as they sound: they set the x and y coordinates of the upper right corner of the sprite. The get_ functions return a value: either the x or y coordinate, the height (H) or width(W).

Code:
void checkCollision(CSprite &sprite1, CSprite &sprite2)
{
    int leftC,rightC,topC,bottomC; //values to tell which side the collision is on
    leftC=rightC=topC=bottomC=1; //assume collision unless otherwise
    if ( sprite1.getX() > ( sprite2.getX() + sprite2.getW() )) { leftC = 0; }
    if ( sprite1.getY() > ( sprite2.getY() + sprite2.getH() )) { topC = 0; }
    if ( ( sprite1.getX() + sprite1.getW() ) < sprite2.getX() ) { rightC = 0; }
    if ( ( sprite1.getY() + sprite1.getH() ) < sprite2.getY() ) { bottomC = 0; }
    if (leftC = 1) { sprite1.xset(sprite2.getX() + sprite2.getW() + 1); }
    if (topC = 1) { sprite1.yset(sprite2.getY() + sprite2.getH() + 1); }
    if (rightC = 1) { sprite1.xset(sprite2.getX() - sprite1.getW() - 1); }
    if (bottomC = 1) { sprite1.yset(sprite2.getY() - sprite1.getH() - 1); }
}
Quote this message in a reply
Member
Posts: 72
Joined: 2006.10
Post: #2
Code:
if (leftC = 1)
if (topC = 1)
if (rightC = 1)
if (bottomC = 1)

try == instead... it helps Rolleyes

- Sohta
Quote this message in a reply
Member
Posts: 201
Joined: 2002.06
Post: #3
To further clarify, here is the difference between the two.... (assume a is 0 and b is 1)

Code:
if (a == b)

Parentheses first: a==b returns false because a and b do not have the same value.
Simplifies to "if (false)" which will fail the test and not execute the statements inside the block.

Code:
if (a = b)

Parentheses first: a=b sets a to 1. The assigned value is passed, so now the value inside the parentheses is 1.
Simplifies to "if (1)" which will pass the test and execute the statements inside the block.
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #4
geez now I feel stupid. Thanks for that advice.
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #5
Alright. I've refined the code to the point of working if I hard code the collision checking. Here's a look at my collision code:

Code:
int checkCollision(CSprite &sprite1, CSprite &sprite2)
{
    int collision;
    collision = 0;
    
    int left1, right1, top1, bottom1;
    int left2, right2, top2, bottom2;

    left1 = sprite1.getX();
    right1 = sprite1.getX() + sprite1.getW();
    top1 = sprite1.getY();
    bottom1 = sprite1.getY() + sprite1.getH();
    
    left2 = sprite2.getX();
    right2 = sprite2.getX() + sprite2.getW();
    top2 = sprite2.getY();
    bottom2 = sprite2.getY() + sprite2.getH();
    
    if ( ( right1 >= left2 ) && ( bottom1 >= top2 ) && ( left1 <= left2 ) && ( top1 <= top2 ) )
    {
        if ( bottom1 == top2 )
        {
            collision = 2;
        }
        if ( right1 == left2 )
        {
            collision = 1;
        }
    }

    if ( ( right2 >= left1 ) && ( bottom2 >= top1 ) && ( left2 <= left1 ) && ( top2 <= top1 ) )
    {
        if ( bottom2 == top1 )
        {
            collision = 2;
        }
        if ( right2 == left1 )
        {
            collision = 1;
        }
    }
    
    if ( ( left1 <= right2 ) && ( bottom1 >= top2 ) && ( right1 >= right2 ) && ( top1 <= top2 ) )
    {
        if ( bottom1 == top2 )
        {
            collision = 2;
        }
        if ( right1 == left2 )
        {
            collision = 1;
        }
    }

    if ( ( left2 <= right1 ) && ( bottom2 >= top1 ) && ( right2 >= right1 ) && ( top2 <= top1 ) )
    {
        if ( bottom2 == top1 )
        {
            collision = 2;
        }
        if ( right2 == left1 )
        {
            collision = 1;
        }
    }
    return collision;
}


It's pretty long, drawn out, and stupidly formatted but it works.

Now when I call this function the code looks like this:

Code:
switch (checkCollision(sprite[1],sprite[2]))
        {
            case 0:
            {
                break;
            }
                
            case 1:
            {
                sprite[1].toggleBPX();
                sprite[2].toggleBPX();
                break;
            }
                
            case 2:
            {
                sprite[1].toggleBPY();
                sprite[2].toggleBPY();
                break;
            }
        }


That works just perfectly. Unfortunately it would take forever for me to write that out for ALL the combinations of 10 different sprites so I chose to try a couple of for loops nested in each other. This is where I have a problem. The above code works, the code below does not. When I comment out the above code and use the bottom code, it simply ignores all collisions. **For reference, the variable "i" was earlier defined as an int for another for loop. I'm just recycling it.



Code:
int j;
        
        for ( i = 1; i <= Enemies; i++ )
        {
            for ( j = 1; j <= Enemies; j++)
            {
                if ( j != i )
                {
                    switch (checkCollision(sprite[i],sprite[j]))
                    {
                        case 0:
                        {
                            break;
                        }
                            
                        case 1:
                        {
                            sprite[i].toggleBPX();
                            sprite[j].toggleBPX();
                            break;
                        }
                            
                        case 2:
                        {
                            sprite[i].toggleBPY();
                            sprite[j].toggleBPY();
                            break;
                        }
                    }
                }
            }
}



I know that's a lot to look at but I've been trying at this for almost 10 hours today just hoping to catch a break and move on from simple rectangular collisions but I just can't get it. If you can figure out why the middle code works and the bottom code does not, I would very very much appreciate it.

And I am sorry for making so many of you look through tons of lines of code.

Thanks for help.
Quote this message in a reply
Moderator
Posts: 916
Joined: 2002.10
Post: #6
one thing (it's annoying me) take out the "case 0: break;"
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #7
Yeah I know. That was bugging me to but my partner kept telling me that I delete things to hastely so I should leave it unless it's harming it. I'll get rid of them. But the problem will still exist.
I think the problem may be that the sprites are skipping frames. They are only able to move 1 pixel in any direction per frame so the code should work but if one of them skipped a frame it could pass through the side and the collision would never occur. I figured this out because as I watched a couple of sprites bounce around seemingly not interacting, they would pass through and the sides that should hit (if the boxes were going opposite directions) hit and it was a huge mess. If anyone knows how I can make sure the boxes move 1 pixel per frame without skipping frames I think that should solve the problem.
Quote this message in a reply
Nibbie
Posts: 2
Joined: 2006.10
Post: #8
I don't know that any of this fixes your problem, but it's just some thoughts I had:

(1)
Here's slightly more efficient collision checking code, because it bails out early when there isn't a collision, which is probably the most common case. Everything after the first 4 if-tests is just stuff I wrote to get return codes similar to what you had. Normally I would just return 1 if there were any kind of collision, and let some other function figure out what to do about it.

Code:
int checkCollision(...)
{
    ...

    // First do quick checks for NO collision.
    if (right1 <= left2) return 0;
    if (right2 <= left1) return 0;
    if (top1 <= bottom2) return 0;
    if (top2 <= bottom1) return 0;
    
    // If we got here, then there MUST be a collision.
    // We can figure out whether the collision is
    // more horizontal or more vertical.
    if (top1 - bottom2 > 0) dy = top1 - bottom2;
    else if (top2 - bottom1 > 0) dy = top2 - bottom1;
    else dy = 0;
    
    if (right1 - left2 > 0) dx = right1 - left2;
    else if (right2 - left1 > 0) dx = right2 - left1;
    else dx = 0;
    
    if (dx > dy) return 1;
    else return 2;
}

(2)
Your code to check each pair of sprites for colllision actually checks every pair twice. For example, sprites 3 and 7 get checked the first time when i = 3 and j = 7 and then they get checked again when i = 7 and j = 3. So you probably want to code your loop like this instead (for 10 sprites, it's a difference of 45 collision tests versus 90):

Code:
for (i = 1; i <= Enemies; i++) {
    for (j = i+1; j <= Enemies; j++) {
    switch (checkCollision(sprite[i], sprite[j])) {
        ...
    }
    }
}

Also, is your sprite array really indexed from 1 to Enemies? And what does toggleBPX() / toggleBPY() do? I assume it moves the sprite to avoid the collision, but how does it know which way to move?

--phillip
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #9
The array is indexed from 1 to Enemies which is defined earlier in the code. toggleBPX() and toggleBPY() do exactly as you assume. Each enemy sprite has its "bounce path (BP)" and when they collide I simply reverse (toggle) them so that they move in the opposite direction. Fairly quick and harmless way to check collisions (or make a screen full of bouncing boxes).
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Checking renderer capabilities akb825 5 3,078 Apr 11, 2006 09:04 AM
Last Post: akb825
  checking CPU and video card load NYGhost 2 4,445 Jul 1, 2004 10:01 PM
Last Post: SOUR-Monkey
  Checking for the existance of OGL? Josh 5 5,139 May 11, 2002 04:57 AM
Last Post: OneSadCookie