Почему я не получаю обнаружение столкновений с CGRectIntersectsRect в IOS?

У меня есть 4 элемента: зеленый, желтый, оранжевый и красный, которые падают сверху экрана.

У меня также есть элемент blueball, который следует за моим прикосновением.

Все это отлично работает! : D

Однако я хочу определить, когда зеленый шар пересекается с синим шаром.

мой синий шар помещается в представление в viewDidLoad, 4 шара размещаются в представлении на основе nstimer, который вызывает onTimer.

Все 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'


person dcp3450    schedule 11.04.2012    source источник
comment
Прямые пересечения - довольно плохой способ обнаружения столкновений с шарами. Вместо этого вы можете сравнить расстояния между центрами шаров с суммой радиусов. Это позволит правильно определить, когда шары сталкиваются.   -  person David Rönnqvist    schedule 12.04.2012
comment
Хорошо, не могли бы вы указать мне на объяснение того, как распознавать изображения как круг, получая центр (возможно, redImage.center?) И устанавливая радиусы? Я понимаю, что это может быть лучший вариант, просто нужно лучше разбираться в настройке / обнаружении кругов / шаров. Спасибо еще раз.   -  person dcp3450    schedule 12.04.2012
comment
Предполагая, что мяч заполняет изображение, радиус будет (ширина изображения) / 2. Если есть какая-то вставка, вам придется учесть ее: (ширина-вставка) / 2   -  person David Rönnqvist    schedule 12.04.2012
comment
что-то вроде: if ((greenImage.center + (greenImage.width / 2)) ‹((blueBall.center + (blueBall.width / 2))) {...}?   -  person dcp3450    schedule 12.04.2012
comment
Больше похоже на ((x1-x2) * (x1-x2) + (y1-y2) * (y1-y2) ‹((r1 + r2) * (r1 + r2)). Центр на самом деле точка, а ширина - длина   -  person David Rönnqvist    schedule 12.04.2012


Ответы (2)


Чтобы определить, пересекаются ли два круга, вы просто смотрите, меньше ли расстояние между центрами, чем сумма их радиусов. Например...

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]];
}
person Jody Hagins    schedule 11.04.2012
comment
(Преждевременная оптимизация, но) намного дешевле вычислить квадратное расстояние и сравнить его с квадратом (r1 + r2). Один квадратный корень дороже одного умножения. - person David Rönnqvist; 12.04.2012
comment
CGFloat r1 = v1.frame.size.width * .5; где v1 - greenImage дает нулевое значение. - person dcp3450; 13.04.2012
comment
Я отредактировал свою проблему, чтобы выразить проблему, с которой я столкнулся с вашим примером. Я понимаю, куда вы идете с этим примером. Думаю, у меня кое-что не так ... - person dcp3450; 13.04.2012
comment
Я переместил функцию наверх (на основе некоторых исследований), и ошибок не было, однако столкновений не было обнаружено, когда я переместил blueBall, чтобы он столкнулся с зеленым. - person dcp3450; 13.04.2012
comment
Кроме того, если я изменю blueBall на redImage, столкновение будет работать идеально. - person dcp3450; 13.04.2012
comment
Проблема в том, что я перемещаю blueBall касанием? - person dcp3450; 13.04.2012
comment
для уточнения: если (CGPointDistance (greenImage.center, redImage.center) ‹(r1 + r2)) работает, но если (CGPointDistance (greenImage.center, blueBall.center)‹ (r1 + r2)) столкновения не обнаружено. - person dcp3450; 13.04.2012
comment
Думаю, проблема возникает, когда у меня несколько мячей. Например, у меня есть 1 зеленый шар на экране, он отслеживает точки столкновения, но как только появляется второй зеленый шар, он начинает отслеживать эту точку и игнорирует исходный шар. Я переосмыслил свой подход. Возможно, имеет смысл отслеживать все столкновения, а затем определять цвет. Мысли? - person dcp3450; 23.05.2012

возможно, это поможет некоторым, просто небольшая модификация приведенного выше кода.

-(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
}
person RickyTheCoder    schedule 08.01.2013