I started developing plane for iOS recently. It’s a hacked up port of the original game I made when I was young. It was an excellent way to learn the iOS SDK and Objective-C.
Anyway, down to business… The initial collision detection I implemented was a very crude one that simply checks if the bounding boxes of the sprites intersect.
[objc]
-(void)checkCollision {
CGRect frame = sprite.frame;
if (frame.origin.y < -32 || frame.origin.y > 320) {
[parent collision];
}
NSMutableArray* baddies = parent.baddies;
UIImageView* object;
CGRect frame2;
for (object in baddies) {
frame2 = object.frame;
if (CGRectIntersectsRect(frame,frame2)) {
[parent collision];
}
}
}
[/objc]
Obviously it wasn’t very accurate and players would die before even hitting the objects – however, this was fast and simple. I tried to compensate for this by making the bounding box smaller – of course that didn’t work too well either.
There was no way around it, I had to use some more advanced collision checking solutions.
One solution I had was to draw a line around the edges of the sprite and have it check whether the lines intersect. But that would mean for every sprite, I had to somehow generate those lines. This was too complicated and too messy so I kept looking.
The solution I came to, albeit perhaps a bit slow, was to first get a bitmap and draw the two objects onto it with 0.5 alpha. Then, it’s simply just iterate through the bitmap and search for alpha values larger than 128. If there is one, it means a collision has occurred.
I dumped one such bitmap through the debugger and converted it to a PGM and it looks something like this:
Without further ado, here’s the code I used. You’ll have to make adjustments to make it work in your program but it’s here to demonstrate how it works. It’s also very bad code at the moment and constantly being optimized so watch out.
[objc]
-(void)checkCollision {
if (!doCollisionCheck) return;
CGRect frame = sprite.frame;
if (frame.origin.y < -32 || frame.origin.y > 320) {
[parent collision];
}
NSMutableArray* baddies = parent.baddies;
UIImageView* object;
CGRect frame2;
for (object in baddies) {
frame2 = object.frame;
if (CGRectIntersectsRect(frame,frame2)) {
[self checkCollisionExpensive:object withSpritePoints:frame.origin withOtherPoints:frame2.origin];
}
}
}
#define device_w 480
#define device_h 320
-(void)checkCollisionExpensive:(UIImageView*) image withSpritePoints:(CGPoint)spritePos withOtherPoints:(CGPoint)otherPos {
CGContextRef bitmap = CGBitmapContextCreate(NULL,
device_w,
device_h,
8,
device_w,
NULL,
kCGImageAlphaOnly);
if (!bitmap) { NSLog(@"Cannot alloc bitmap"); return; }
CGImageRef otherImageRef = image.image.CGImage;
CGImageRef selfImageRef = sprite.image.CGImage;
CGRect rect;
CGContextSetAlpha(bitmap, 0);
rect = CGRectMake(0, 0, device_w, device_h);
CGContextFillRect(bitmap, rect);
CGContextSetAlpha(bitmap, 0.5);
rect = CGRectMake(otherPos.x, device_h-otherPos.y-image.frame.size.height, image.frame.size.width, image.frame.size.height);
CGContextDrawImage(bitmap, rect, otherImageRef);
rect = CGRectMake(spritePos.x, device_h-spritePos.y-sprite.frame.size.height, sprite.frame.size.width, sprite.frame.size.height);
CGContextDrawImage(bitmap, rect, selfImageRef);
unsigned char* data = CGBitmapContextGetData(bitmap);
int collided = 0;
NSLog(@"%p",data);
for (size_t i = 0; i<device_w*device_h; i++) {
//NSLog(@"%x", (int)data[i]);
if (data[i] > 128) {
NSLog(@"collidepoint %d",(int)data[i]);
collided = 1;
break;
}
}
if (collided) [parent collision];
CGContextRelease(bitmap);
}
[/objc]
As the method name suggests, it’s a expensive process and calls to it should be reduced to a minimum. I used a combination of bounding box collision detection and the bitmapping to reduce calls.
Happy programming.