Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm creating a basic "game" in iOS 4.1. The premise is simple, there is a green rectangle ("disk") that moves/bounces around the screen, and red rectangle ("bump") that is stationary. The user can move the red "bump" by touching another coordinate on the screen, but that's irrelevant to this question.

Each rectangle is a UIImageView (I will replace them with some kind of image/icon once I get the mechanics down). I've gotten as far as detecting when the rectangles collide, and I'm able to reverse the direction of the green "disk" on the Y axis if they do. This works well when the green "disk" approaches the red "bump" from top or bottom, it bounces off in the other direction. But when it approaches from the side, the bounce is incorrect; I need to reverse the X direction instead.

Here's the timer I setup:

- (void)viewDidLoad {
    xSpeed =  3;
    ySpeed = -3;
    gameTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(mainGameLoop:) userInfo:nil repeats:YES];    
    [super viewDidLoad];
}

Here's the main game loop:

- (void) mainGameLoop:(NSTimer *)theTimer {
    disk.center = CGPointMake(disk.center.x + xSpeed, disk.center.y + ySpeed);

    // make sure the disk does not travel off the edges of the screen
    // magic number values based on size of disk's frame
    // startAnimating causes the image to "pulse"
    if (disk.center.x < 55 || disk.center.x > 265) {
        xSpeed = xSpeed * -1;
        [disk startAnimating];
    } 
    if (disk.center.y < 55 || disk.center.y > 360) {
        ySpeed = ySpeed * -1;
        [disk startAnimating];
    }


    // check to see if the disk collides with the bump
    if (CGRectIntersectsRect(disk.frame, bump.frame)) {
        NSLog(@"Collision detected...");
        if (! [disk isAnimating]) {
            ySpeed = ySpeed * -1;
            [disk startAnimating];
        }    
    }
}

So my question is: how can I detect whether I need to flip the X speed or the Y speed? ie: how can I calculate which edge of the bump was collided with?

share|improve this question
add comment

3 Answers

up vote 4 down vote accepted

Since you're using rectangles and it sounds like you're also confining yourself to eight directional movement, you can probably get away with computing a vector from the ball's center to the bumper's, and examining that; it will tell you about their relative positions. If the magnitude of the Y component is greater the ball is hitting the top or bottom, if the X component is greater the ball is hitting the left or right, and if they are equal it's hitting exactly on the corner (maybe reverse both directions in that case?).

A more robust solution would probably involve the separating axis theorem and use of penetration vectors to gauge response and real reflection vectors to compute the change in velocity. I don't know how robust a solution you need though.

share|improve this answer
    
Computing the vector will only work if the rectangle has sides of equal length. Otherwise you'll have to factor in the ratio of the rectangle somehow. –  bummzack Jan 12 '11 at 9:09
    
Errr yup, that's totally correct, I was definitely thinking of squares and not rectangles. >_< –  Josh Petrie Jan 12 '11 at 16:40
    
After reviewing all of the proposed solutions, I believe that separating axis theorem is the way to go here. Thanks. –  Mike King Jan 18 '11 at 0:24
add comment

You can't simply do that with CGRectIntersectsRect. That's only a method that say if a collision is detected or not.

So you have to "split" your rect into more "pieces" and call CGRectIntersectsRect for each piece (or do that by hand without CGRectIntersectsRect). I.e.

CGRect Up = CGRectMake(bump.frame.origin.x, bump.frame.origin.y, bump.frame.size.width, bump.frame.size.height/4);
CGRect Down = CGRectMake(bump.frame.origin.x, bump.frame.origin.y + (bump.frame.size.height/4)*3, bump.frame.size.width, bump.frame.size.height/4);

CGRect Left = CGRectMake(bump.frame.origin.x, bump.frame.origin.y + bump.frame.size.height/4, bump.frame.size.width/4, bump.frame.size.height/2);
CGRect Right = CGRectMake(bump.frame.origin.x+(bump.frame.size.width/4)*3, bump.frame.origin.y + bump.frame.size.height/4, bump.frame.size.width/4, bump.frame.size.height/2);

Then you can check where your frame first collide (but be carefull: that example is only edge detection - the center isn't detected at all).

if (CGRectIntersectsRect(disk.frame, Up) || CGRectIntersectsRect(disk.frame, Down)) {
  NSLog(@"Collision detected...");
  if (! [disk isAnimating]) {
    ySpeed = ySpeed * -1;
    [disk startAnimating];
  }
}
if (CGRectIntersectsRect(disk.frame, Left) || CGRectIntersectsRect(disk.frame, Right)) {
  NSLog(@"Collision detected...");
  if (! [disk isAnimating]) {
    xSpeed = xSpeed * -1;
    [disk startAnimating];
  }    
}

I couldn't test this code, so please be carefull - it's only to give you an general idea about this...

share|improve this answer
    
Thanks, I think this might be the answer. I'll give it a shot and see how well it works. –  Mike King Jan 12 '11 at 15:47
    
The downside to this approach is that it requires far more checks and doesn't scale particularly well in general. –  Josh Petrie Jan 12 '11 at 16:42
add comment

As long as you're just checking circle vs. rectangle collisions it should be sufficient to do the following:

Whenever you detect a collision, check the center of your circle against the 6 outer sides of the rectangle, as in the following image:

6 outer areas of 2d rectangle

If the circle is in bottom or top, flip the Y-speed. If in left or right flip the X-speed. If the circle is located in one of the edges, you'll probably want to flip both. Please note that this will only work reliably when the traveled distance of the circle doesn't exceed the circle diameter (this is per update). If your circle is moving faster, you'll be better off with continuous collision detection.

This approach should be really fast and easy to implement. For a more robust collision detection that can also handle arbitrary convex polygons, check out the separation axis theorem (as also stated by Josh Petrie).

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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