## Collision detection problems

Moderator
Posts: 102
Joined: 2003.07
Post: #1
I'm trying to write a collision detection method that checks for collisions between the sprite and the tiles (a 64 x 64 array of ints) here is all the code that is necesary to understand what I am doing, it doesn't work and I don't know why.
the index of the mapTiles array is either a 1 (there is a tile there) or 0 (no tile)
4096 is the width of the map in pixels.
Code:
```- (BOOL) checkCollision {     int x, y, top, right, left, bottom, position;     left = [thePlayer getPlayerLocationX];     bottom = [thePlayer getPlayerLocationY];     right = [thePlayer getPlayerLocationX] + 128;     top = [thePlayer getPlayerLocationY] + 64;          for ( x = 0; x < 4096; x += 64)         for (y = 0; y < 4096; y += 64)             {             position = [map checkIndex: x And: y ];             if (position == 1)                 {                                  //if ( x < right)        return YES;                 //if ( x + 64 < left)    return YES;                 //if ( y < bottom)        return YES;                 //if ( y > top)            return YES;                                  //return NO;                                  }             else                 {                 return NO;                 }                          } } ... // this is the [map checkIndex: x And: y] function - (int) checkIndex: (int) X And: (int) Y {     return mapTiles[ (X/64) + ((Y / 64) * 64)]; } // and this is how I use the collisions ( check collision is called every frame) if ( !collided) { [thePlayer movePlayerLeft]; } // and so on for the other directions```

I think that's about it, thanks for the help

-CarbonX
Tasnu Arakun
Unregistered

Post: #2
your code is essentially (you need to take a closer look at those if-statements) checking if the map tile is within or to the left of the player tile (first two if-statements). if not it will still return YES since then you check if the game tile is either below, within or above the player tile.
to get things right you have to check if the two rectangles intersect and then what you need is a "monster" like this.
Code:
```if ( right > x && left < x + 64 && top > y && bottom < y ) return YES; // collision from above else if ( right > x + 64 && left < x + 64 && top > y && bottom < y + 64 ) return YES; // collision from the right else if ( right > x && left < x + 64 && top > y + 64 && bottom < y + 64 ) return YES; // collision from below else if ( right > x && left < x && top > y && bottom < y + 64 ) return YES; // collision from the left else if ( right > x && left < x + 64 && top > y && bottom < y + 64 ) return YES; // is within else return NO;```
but why reinvent the wheel? the code has already been written for you.
Code:
```NSRect playerRect, tileRect; playerRect.origin = [ thePlayer location ]; playerRect.size = NSMakeSize( 128, 64 ); for ( x = 0; x < 4096; x += 64 ) for (y = 0; y < 4096; y += 64 ) {     position = [map checkIndex: x And: y ];     if (position == 1) {         tileRect.origin = NSMakePoint( x, y );         tileSize.size = NSMakeSize( 64, 64 );         return NSIntersectsRect( playerRect, tileRect );     } }```
note: i haven't tested the code so i hope there are no errors.

edit: i took the liberty of replacing [ thePlayer getPlayerLocationX ] and [ thePlayer getPlayerLocationX ] with the method [ thePlayer location ]...
that is: - ( NSPoint ) location;
Moderator
Posts: 102
Joined: 2003.07
Post: #3
wow, I didn't know there was an NSIntersectRect method, that would've made my life a whole lot easier... thanks for the input, and by spriteRect I assume you mean tileRect, anyways thanks again I'll let you know if it for some reason doesn't work

-CarbonX
Moderator
Posts: 1,563
Joined: 2003.10
Post: #4
Tasnu Arakun seems to have you on the right path for getting the collisions to work. Here's a few things I spotted in your code that could be cleaned up a bit:

Code:
```    left = [thePlayer getPlayerLocationX];     bottom = [thePlayer getPlayerLocationY];     right = [thePlayer getPlayerLocationX] + 128;     top = [thePlayer getPlayerLocationY] + 64;```

You can easily avoid 2 function calls here by doing this instead:

Code:
```    left = [thePlayer getPlayerLocationX];     bottom = [thePlayer getPlayerLocationY];     right = left + 128;     top = bottom + 64;```

Code:
```- (int) checkIndex: (int) X And: (int) Y {     return mapTiles[ (X/64) + ((Y / 64) * 64)]; }```

((Y / 64) * 64) is redundant, but you wouldn't really want to change it to just Y, because those two 64s mean two different things. What would be really be best is if you would define constants for map size and tile size, like so:

Code:
```#define TILE_SIZE 64 #define MAP_SIZE_X 64 #define MAP_SIZE_Y 64 ... - (int) checkIndex: (int) X And: (int) Y {     return mapTiles[(X / TILE_SIZE) + ((Y / TILE_SIZE) * MAP_SIZE_X)]; }```

While this boils down to the same thing in the end, it makes your code much clearer. Instead of just having a couple of "magic numbers" (in this case, 64) scattered throughout your code, you use named constants, which unambiguously tells anyone reading the code exactly what the number means. Also, it makes it MUCH easier if you decide later to change your map or tile size.

Back on the subject of collisions, I have one thing to add: It's not enough to simply cease movement in a direction when you've detected a collision. What you'll want to do is to back up the player (or, if you're detecting collisions in advance, move the player forward) to the closest point to the object without actually being inside the object.

Let me know if that didn't make any sense.

Alex Diener
Tasnu Arakun
Unregistered

Post: #5
CarbonX Wrote:thanks for the input, and by spriteRect I assume you mean tileRect, anyways thanks again

it's been corrected.
Member
Posts: 142
Joined: 2002.11
Post: #6
Looking at your code I'm reminded of a bit of programmer wisdom that goes a little something like this

Quote:Good code only has two numbers in it, 0 and 1, all other numbers, especially ones that are used several times should be stored in variables or replaced by #definitions.

The reason for this is that it makes your code a lot easier to understand and if you wish to change one number you'll only have to change it in one spot in the code.

I also haden't heard of NSIntersectsRect, and instead was using this thing! I doubt Apple's method could be much faster than my own though.

Quote:- (BOOL) rect:(NSRect)aRect1 collidesWithRect:(NSRect)aRect2 {

int left1, left2;
int right1, right2;
int top1, top2;
int bottom1, bottom2;

left1 = aRect1.origin.x;
left2 = aRect2.origin.x;
right1 = aRect1.origin.x + aRect1.size.width;
right2 = aRect2.origin.x + aRect2.size.width;
top1 = aRect1.origin.y;
top2 = aRect2.origin.y;
bottom1 = aRect1.origin.y + aRect1.size.height;
bottom2 = aRect2.origin.y + aRect2.size.height;

if (bottom1 < top2) return FALSE;
if (top1 > bottom2) return FALSE;

if (right1 < left2) return FALSE;
if (left1 > right2) return FALSE;

return TRUE;

}
Tasnu Arakun
Unregistered

Post: #7
Holmes - i'm pretty sure apple wrote their code to be as fast as possible. in your case you're creating 8 temporary variables that you use only one time each. and don't forget that NSRect contains floats, so there'll be a typecast before the value can be assigned to the variable.
so while you're code is more suitable and a lot easier to read than my "monster"-collision-code (which was written as you can see for a somewhat other purpose) it ismost likely slower than existing code and still reinventing the wheel.

oh, and another tip before i go to bed - take a look at "Foundation Functions" in the Cocoa API. for example instead of writing:
Code:
`bottom1 = aRect1.origin.y + aRect1.size.height;`
you could write:
Code:
`bottom1 = NSMaxY( aRect1 );`

i must admit that i myself didn't know of these functions before i did the above post. but suddenly it occured to me that if apple supply the NSRect type then there must be functions to manipulate those. i guess i'll spend some time cleaning up my own code

CarbonX - it's possible that NSRect is completely wrong for your purpose however. i don't know about the player location, but the rest of the numbers are clearly integers. thus you'd be better of writing your own rectangle struct and your own collision function using integers.