为什么没有在IOS中使用CGRectIntersectsRect进行碰撞检测?

时间:2012-04-11 20:20:22

标签: ios collision-detection cgrectmake

我有4个元素:从屏幕顶部掉下来的绿球,黄球,橙球和红球

我也有一个元素,蓝球,跟着我的触摸。

所有这些都很有效! :d

但是,我想检测绿球与蓝球相交的时间

我的蓝色球被放置在viewDidLoad的视图中,4个球被放置在视图上,基于调用onTimer的nstimer。

所有5个球都在同一层上创建:

[self.view insertSubview:greenImage belowSubview:bottomBar]

我可以检测到红球和绿球碰撞的时间:

if (CGRectIntersectsRect(greenImage.frame, redImage.frame)) {
                UIAlertView *message = [[UIAlertView alloc] initWithTitle:@"title" message:@"points" delegate:nil cancelButtonTitle:@"cool" otherButtonTitles:nil];
                [message show];
                [message release];
            }

如果我将“redImage”替换为“blueball”,我就不会收到警报。我认为这可能是因为它们没有在同一方法中声明。

有关如何检测此碰撞的任何想法?

谢谢!

---编辑---

使用下面的建议我正在做以下事情:

        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];
        }

在这个方法之下我有:

CGFloat CGPointDistance(CGPoint p1, CGPoint p2)
{
    return sqrtf((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}

收到错误:

  

'CGPointDistance'的冲突类型

并发出警告:

  

函数'CGPointDistance'的隐式声明

2 个答案:

答案 0 :(得分:1)

要确定两个圆是否相交,您只需查看中心之间的距离是否小于它们的半径之和。例如......

CGFloat CGPointDistance(CGPoint p1, CGPoint p2)
{
    return sqrtf((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}

看起来你的“球”是视图......假设“球”填满了框架,半径将是宽度的1/2 ......

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);    
}

好的 - 我已经砍了一个快速的样本。您几乎可以将其放入视图控制器中。不要将它用于制作东西......就像你可以做什么的一个例子......你可以把它放在顶部,控制器的其余部分放在它下面......

// 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]];
}

答案 1 :(得分:0)

也许这会对某些人有所帮助,只需对上面的代码进行一些修改。

-(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)
}

并检查你刚刚做的碰撞

if([self DidCollide:view1 v2:view2 v1Radius:radius v2Radius:radius])
{
    //do something
}
相关问题