1
votes

I am creating an app where there will be ~10 circles on screen, that can all be pushed and dragged around the screen via touch movements. These circles (which are simply UIView's with a circle drawn in them) also have collision detection on them, and the idea is that they push other circles out of the way when they collide. There is also no acceration or gravity in this app, it's more like pushing coins around, where they are simply pushed out of the way, and when you stop moving, so do they.

I have touch-to-circle interaction working, and the fundamentals of circle-to-circle working. The code however gives Circle01 all the power. IE, it can be pushed into other circles and they will move out of the way as hoped. But when the other circles are pushed into it, they move around Circle01, instead of pushing it out of the way.

I've read dozens of pages about collision detection, but I'm just stumped as to how I'd modify this code to give all the circles equal power.

Relevant code:

MyCircle.m

- (void)drawRect:(CGRect)rect
{
 CGPoint viewCenter = CGPointMake(50,50);
 bpath = [UIBezierPath bezierPathWithArcCenter:viewCenter radius:50 startAngle:0 endAngle:DEGREES_TO_RADIANS(360) clockwise:YES];

 [[UIColor blueColor] setFill];
 [bpath fill];
}

CircleVC.m

- (void)viewDidLoad
{
    Circle01 = [[MyCircle alloc] initWithFrame:CGRectMake(200, 200, 100, 100)];
      Circle01.backgroundColor = [UIColor redColor];
      Circle01.alpha = 0.5;
      [self.view addSubview:Circle01];
}

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
  // Testing circle collision detection
  for (UIView *aView in self.view.subviews) 
  { 
    if (Circle01 == anotherView) 
    continue; 
    if (CGRectIntersectsRect(Circle01.frame, aView.frame)) 
    { 
      float xDistance = aView.center.x - Circle01.center.x;
      float yDistance = aView.center.y - Circle01.center.y;
      float distance = xDistance * xDistance + yDistance * yDistance;
      float angle = atan2(yDistance, xDistance);
      CGPoint newCenter;

      if (distance < 100 * 100) 
      {
        newCenter.x = Circle01.center.x + (Circle01.frame.size.width * cos(angle));
        newCenter.y = Circle01.center.y + (Circle01.frame.size.width * sin(angle));
        aView.center = newCenter;
      }
    }
  }
}

Thanks for any help.

2

2 Answers

1
votes

It may be easier to offload this to a physics engine such as Box2d. Cocos2d is packaged with Box2d, but you will want the stand alone version since you are only using UIView based animations

_

Option 1

Here is a good, though slightly dated tutorial on using Box2d with UIKit. http://www.cocoanetics.com/2010/05/physics-101-uikit-app-with-box2d-for-gravity/

Some caveats: if you are using ARC you will need to use the god-awful bridging compiler hints that tell the ARC pre-compiler when your C objects are being put into and out of its control.

For example:

bodyDef.userData = (__bridge void *)physicalView;
UIView *oneView = (__bridge UIView*)b -> GetUserData();

_

Option 2

You can use Chipmunk (which would also require the above "god-awful" bridging to ARC statements). Here is a good tutorial: http://www.alexandre-gomes.com/articles/chipmunk/

_

Option 3 - Easiest (Not Cheapest)

Alternatively, you could use the Objective-Chipmunk library that comes with Chipmunk Pro which does work with ARC. Here is a recent and "to-the-point" tutorial on using Obj-Chipmunk with UIImageViews. This would be the same for any UIView based code. http://www.ayarsanimation.com/frankayars/chipmunk

You can buy Obj-Chipmunk costs $89 when you by an Indie version license for Chipmunk Pro. http://chipmunk-physics.net/chipmunkPro.php

I am not advocating a paid solution, but it may save you time.

0
votes

It seems pretty simple, unless I'm missing something. Instead of hard-coding Circle01 in your touchesMoved, do your hit detection based on the view that the user touched. That way, whatever view they are dragging will get priority.