1
\$\begingroup\$

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?

\$\endgroup\$

3 Answers 3

4
\$\begingroup\$

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.

\$\endgroup\$
3
  • \$\begingroup\$ 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. \$\endgroup\$
    – bummzack
    Commented Jan 12, 2011 at 9:09
  • \$\begingroup\$ Errr yup, that's totally correct, I was definitely thinking of squares and not rectangles. >_< \$\endgroup\$
    – user1430
    Commented Jan 12, 2011 at 16:40
  • \$\begingroup\$ After reviewing all of the proposed solutions, I believe that separating axis theorem is the way to go here. Thanks. \$\endgroup\$
    – Mike King
    Commented Jan 18, 2011 at 0:24
1
\$\begingroup\$

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...

\$\endgroup\$
2
  • \$\begingroup\$ Thanks, I think this might be the answer. I'll give it a shot and see how well it works. \$\endgroup\$
    – Mike King
    Commented Jan 12, 2011 at 15:47
  • \$\begingroup\$ The downside to this approach is that it requires far more checks and doesn't scale particularly well in general. \$\endgroup\$
    – user1430
    Commented Jan 12, 2011 at 16:42
1
\$\begingroup\$

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).

\$\endgroup\$

You must log in to answer this question.

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