Reputation: 11187
I have 4 elements: greenball, yellowball, orangeball, and redball that fall from the top of the screen
I also have an element, blueball, that follows my touch.
All of these things are working great! :D
However, I want to detect when the greenball intersects with the blueball
my blue ball is placed in the view at viewDidLoad, the 4 balls are placed on the view based on an nstimer that calls onTimer.
All 5 balls are created on the same layer:
[self.view insertSubview:greenImage belowSubview:bottomBar]
I can detect when the redball and greenball collide:
if (CGRectIntersectsRect(greenImage.frame, redImage.frame)) {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:@"title" message:@"points" delegate:nil cancelButtonTitle:@"cool" otherButtonTitles:nil];
[message show];
[message release];
}
If I replace "redImage" for "blueball" I don't get the alert. I assume this may be because they aren't declared in the same method.
Any ideas on how to detect this collision?
thanks!
--- EDIT ---
Using the suggestion below I'm doing the following:
greenImage = [[UIImageView alloc] initWithImage:green];
greenImage.frame = CGRectMake(startX,0,43,43);
[self.view insertSubview:greenImage belowSubview:bottomBar];
[UIView beginAnimations:nil context:greenImage];
[UIView setAnimationDuration:5 * speed];
greenImage.frame = CGRectMake(startX, 500.0, 43,43);
CGFloat r1 = greenImage.frame.size.width * .5;
CGFloat r2 = blueBall.frame.size.width * .5;
if(CGPointDistance(greenImage.center, blueBall.center) < (r1 + r2)){
UIAlertView *message = [[UIAlertView alloc] initWithTitle:@"notice" message:@"hit!" delegate:nil cancelButtonTitle:@"cool" otherButtonTitles:nil];
[message show];
[message release];
}
below this method I have:
CGFloat CGPointDistance(CGPoint p1, CGPoint p2)
{
return sqrtf((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}
get an error:
conflicting types for 'CGPointDistance'
and a warning:
implicit declaration of function 'CGPointDistance'
Upvotes: 0
Views: 1646
Reputation: 93
maybe this will help some, just a little modification to the code above.
-(CGFloat)CGPointDistance:(CGPoint)p1 p2:(CGPoint)p2
{
return sqrtf(powf(p1.x - p2.x) + powf(p1.y-p2.y));
}
-(BOOL)DidCollide:(View*)v1 v2:(View*)v2 v1Radius:(CGFloat)v1Radius v2Radius:(CGFloat)v2Radius
{
CGFloat r1 = v1.frame.size.width * v1Radius;
CGFloat r2 = v2.frame.size.width * v2Radius;
return [self PointDistance:v1.center p2:v2.center] < (r1 + r2)
}
and to check for collision your just do
if([self DidCollide:view1 v2:view2 v1Radius:radius v2Radius:radius])
{
//do something
}
Upvotes: 0
Reputation: 28419
To determine if two circles intersect, you simply see if the distance between the centers is less than the sum if their radii. For example...
CGFloat CGPointDistance(CGPoint p1, CGPoint p2)
{
return sqrtf((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}
It looks like your "balls" are views... Assuming the "ball" fills the frame, the radius will be 1/2 the width...
BOOL BallsCollide(UIView *v1, UIView *v2)
{
CGFloat r1 = v1.frame.size.width * .5;
CGFloat r2 = v2.frame.size.width * .5;
return CGPointDistance(v1.center, v2.center) < (r1 + r2);
}
OK -- I have hacked up a quick sample. You can almost drop it into your view controller. Don't use it for production stuff... just as an example of how you can do stuff... You can put this at the top and the rest of your controller below it...
// I do not recommend using this approach for naything non-trivial,
// but it demonstrates the very simple collision detection for circle shapes.
@interface Ball : UIView {
}
@property (nonatomic, strong) UIColor *origColor;
@property (nonatomic, strong) UIColor *color;
+ (id)ballWithRadius:(CGFloat)radius color:(UIColor*)color;
- (BOOL)doesIntersectWith:(Ball*)ball;
@end
@implementation Ball
@synthesize color = _color;
@synthesize origColor = _origColor;
+ (id)ballWithRadius:(CGFloat)radius color:(UIColor*)color
{
CGFloat diameter = radius * 2;
Ball *ball = [[Ball alloc] initWithFrame:CGRectMake(0, 0, diameter, diameter)];
ball.color = ball.origColor = color;
ball.backgroundColor = [UIColor clearColor];
return ball;
}
- (BOOL)doesIntersectWith:(Ball *)ball
{
// Two circles overlap if the sum of their radii
// is less than the distance between the two centers.
CGFloat r1 = self.frame.size.width * .5;
CGFloat r2 = ball.frame.size.width * .5;
CGFloat diffX = self.center.x - ball.center.x;
CGFloat diffY = self.center.y - ball.center.y;
return (diffX*diffX) + (diffY*diffY) < (r1+r2)*(r1+r2);
}
- (BOOL)containsPoint:(CGPoint)point
{
// Contains a point if distance from center is less than radius
CGFloat r = self.frame.size.width *.5;
CGFloat diffX = self.center.x - point.x;
CGFloat diffY = self.center.y - point.y;
return (diffX*diffX) + (diffY*diffY) < r*r;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
[self.color setFill];
[self.color setStroke];
CGContextSetLineWidth(context, 1);
CGContextFillEllipseInRect(context, self.bounds);
}
@end
@interface CollideVC() {
NSMutableDictionary *activeTouches;
NSMutableSet *currentCollisions;
}
@end
@implementation CollideVC
- (void)ball:(Ball*)ball touched:(UITouch*)touch
{
[activeTouches setObject:ball forKey:[NSValue valueWithPointer:(__bridge void*)touch]];
}
- (Ball*)getBallTouchedBy:(UITouch*)touch
{
return [activeTouches objectForKey:[NSValue valueWithPointer:(__bridge void*)touch]];
}
- (void)stopTouch:(UITouch*)touch
{
[activeTouches removeObjectForKey:[NSValue valueWithPointer:(__bridge void*)touch]];
}
- (void)ball:(Ball*)ball1 hasCollidedWith:(Ball*)ball2
{
[currentCollisions addObject:ball1];
[currentCollisions addObject:ball2];
ball1.color = ball2.color = [UIColor yellowColor];
[ball1 setNeedsDisplay];
[ball2 setNeedsDisplay];
}
// NOTE: You should delegate handling of collisions...
- (void)checkForCollisions
{
// Should filter those that don't actually need redisplay...
// For simplicity, all that were or are colliding get it...
[currentCollisions enumerateObjectsUsingBlock:^(Ball *ball, BOOL *stop) {
[ball setNeedsDisplay];
ball.color = ball.origColor;
}];
[currentCollisions removeAllObjects];
NSArray *views = self.view.subviews;
for (size_t i = 0; i < views.count; ++i) {
id v = [views objectAtIndex:i];
if (![v isKindOfClass:[Ball class]]) continue;
Ball *ball1 = v;
for (size_t j = i+1; j < views.count; ++j) {
v = [views objectAtIndex:j];
if (![v isKindOfClass:[Ball class]]) continue;
Ball *ball2 = v;
if ([ball1 doesIntersectWith:ball2]) {
[self ball:ball1 hasCollidedWith:ball2];
}
}
}
}
- (void)addBallWithRadius:(CGFloat)radius point:(CGPoint)point color:(UIColor*)color
{
Ball *ball = [Ball ballWithRadius:radius color:color];
ball.center = point;
[self.view addSubview:ball];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInView:self.view];
for (Ball *ball in self.view.subviews) {
if ([ball containsPoint:touchLocation]) {
[self ball:ball touched:touch];
}
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
// Use relative distance so center does nt jump to tap location.
CGPoint prev = [touch previousLocationInView:self.view];
CGPoint curr = [touch locationInView:self.view];
CGPoint c = [self getBallTouchedBy:touch].center;
[self getBallTouchedBy:touch].center = CGPointMake(c.x+curr.x-prev.x, c.y+curr.y-prev.y);
}
// NOTE: This does not check the path between the moves, only the new point.
// So a fast movement "past" the other object will not get detected.
// If you want to do comple detection, use an existing engine
[self checkForCollisions];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
[self stopTouch:touch];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
activeTouches = [NSMutableDictionary dictionary];
currentCollisions = [NSMutableSet set];
[self addBallWithRadius:20 point:CGPointMake(110, 100) color:[UIColor blueColor]];
[self addBallWithRadius:20 point:CGPointMake(200, 200) color:[UIColor redColor]];
[self addBallWithRadius:20 point:CGPointMake(235, 250) color:[UIColor greenColor]];
}
Upvotes: 1