Логическая неточность CGRectIntersectsRect

В приложении я задаю случайные кадры квадратам каждый раз, когда оно запускается. Я использую эту логику, чтобы убедиться,

а) каждый квадрат находится не слишком близко к игроку
и
б) каждый квадрат содержится в поле зрения на экране в) ни один квадрат не касается другого квадрата

for(UIButton* button in squareArray) {
    BOOL shouldContinue = YES;
    do {
        int randX = arc4random() % 321;
        int randY = arc4random() % 481;
        button.frame = CGRectMake(randX, randY, button.frame.size.width, button.frame.size.height);
        CGRect playerRect = CGRectMake(100, 180, 120, 120);
        for(UIButton* b in squareArray)
            if(!CGRectIntersectsRect(b.frame, button.frame) && 
                !CGRectIntersectsRect(button.frame, playerRect) && 
                CGRectContainsRect(self.view.frame, button.frame)) {
                shouldContinue = NO;
            }
    } while (shouldContinue);
}

С этим кодом я ожидал бы, что каждый квадрат в SquareArray будет (после завершения цикла) полностью внутри границ представления, не пересекаясь ни с какими другими кнопками в массиве, и полностью вне границ прямоугольника playerRect , представляющий собой квадрат 120 х 120 в центре экрана. Мой код неправильный? Потому что я не получаю ни одной из этих функций.

Редактировать: на самом деле я получаю одну из желаемых черт этого метода: ни один квадрат никогда не пересекает playerRect. Но я все еще получаю квадраты, перекрывающие друг друга, и квадраты, которые частично не видны.

Редактировать 2:

Я внес эти изменения во вложенный цикл for:

for(UIButton* b in squareArray)
            if(![b isEqual:button]) {
                if(CGRectIntersectsRect(b.frame, button.frame) || 
                   CGRectIntersectsRect(button.frame, playerRect) || 
                   !CGRectContainsRect(CGRectMake(10, 10, 300, 460), button.frame))
                    shouldContinue = YES;
                else
                    shouldContinue = NO;
            }

И теперь квадраты всегда находятся внутри немного измененного (меньшего) прямоугольника для представления и никогда не пересекают квадрат игрока. Ура. Но они все равно появляются друг над другом. Почему?


person eric.mitchell    schedule 20.11.2011    source источник
comment
Вы не задали вопрос и не объяснили, что, по вашему мнению, не так с вашим кодом.   -  person rob mayoff    schedule 20.11.2011
comment
Бахахаха, извините, я случайно разместил это, прежде чем задать вопрос, потому что я спешил. Обновление сейчас...   -  person eric.mitchell    schedule 20.11.2011


Ответы (3)


Это не проблема с CGRectIntersectsRect, это проблема с вашей логикой. Когда внутренний цикл находит один UIButton, который не пересекается с кнопкой, вы принимаете случайные координаты, сгенерированные для кнопки. Вам нужно убедиться, что все кнопки в SquareArray не пересекаются с кнопкой, а не только с одной.

Кроме того, для чего инициализируются рамки кнопок? Возможно, это было бы лучшим решением:

for (int i=0; i<[squareArray count]; i++) {
    UIButton* button = [squareArray objectAtIndex:i];
    BOOL shouldContinue = NO;
    do {
        int randX = arc4random() % 321;
        int randY = arc4random() % 481;
        button.frame = CGRectMake(randX, randY, button.frame.size.width, button.frame.size.height);
        CGRect playerRect = CGRectMake(100, 180, 120, 120);
        for(int j=0; j<i; j++)
            UIButton *b = [squareArray objectAtIndex:j];
            if(CGRectIntersectsRect(b.frame, button.frame) || 
                CGRectIntersectsRect(button.frame, playerRect) || 
                !CGRectContainsRect(self.view.frame, button.frame)) {
                shouldContinue = YES;
            }
    } while (shouldContinue);
}

Обратите внимание, что я вообще не проверял это. Кроме того, в зависимости от ситуации, это может зацикливаться бесконечно, если для кнопки нет допустимой позиции. Может быть лучшее решение, чем размещение всего совершенно случайным образом, в зависимости от вашей проблемы.

person Rob Lourens    schedule 20.11.2011
comment
Фреймы инициализируются через IB, потому что каждый квадрат в squareArray является IBOutlet для UIButton, поэтому все они имеют позиции по умолчанию, и я просто хочу рандомизировать их. - person eric.mitchell; 20.11.2011
comment
Вы уверены, что у вас есть все квадраты в SquareArray? Если они от пера, может быть, есть некоторые, которые вы не перемещаете? - person Jesse Rusak; 20.11.2011
comment
Нет; Я знаю, что в массив загружается правильное количество квадратов, потому что при запуске, в зависимости от количества квадратов, которые должны быть в массиве (в зависимости от выбора пользователя), эти квадраты жестко запрограммированы в массив. Кроме того, поскольку один и тот же массив используется оба раза, если он не включает все квадраты (их четыре), то все они не будут располагаться каждый раз в случайных позициях, а они есть. - person eric.mitchell; 20.11.2011

Предположим, что squareArray содержит три кнопки A, B и C со следующими рамками:

A.frame == CGRectMake(10,10,10,10)
B.frame == CGRectMake(10,10,10,10)
C.frame == CGRectMake(20,10,10,10)

Обратите внимание, что A и B перекрываются, но A и B не перекрывают C. Теперь рассмотрим, что происходит в вашем внутреннем цикле, когда button == B.

При первом проходе через внутренний цикл b == A вы обнаруживаете CGRectIntersectsRect(b.frame, button.frame) и устанавливаете shouldContinue = YES.

При втором проходе b == B означает [b isEqual:button], так что вы ничего не делаете.

На третьем (и последнем) проходе b == C. Вы обнаружите, что CGRectIntersectsRect(b.frame, button.frame) ложно (поскольку B.frame не пересекается C.frame), CGRectIntersectsRect(button.frame, playerRect) ложно, а !CGRectContainsRect(CGRectMake(10, 10, 300, 460), button.frame) ложно. Итак, вы установили shouldContinue = NO.

Затем внутренний цикл выходит. Вы проверяете shouldContinue, обнаруживаете, что оно ложно, и выходите из цикла do/while. У вас осталась кнопка B, перекрывающая кнопку A.

person rob mayoff    schedule 20.11.2011
comment
Другими словами, удалите строку, в которой вы установили shouldContinue = NO. - person Jesse Rusak; 20.11.2011
comment
У меня есть длинное объяснение, почему это не сработает, и я собирался опубликовать его и попросить о дополнительной помощи, но потом решил перетерпеть и разобраться сам. Удаление этой строки не сработает, потому что даже после того, как условие выполнено, shouldContinue по-прежнему имеет значение YES с момента, когда оно не было выполнено, и вы получаете бесконечный цикл. Я только что опубликовал рабочее решение. - person eric.mitchell; 20.11.2011

В конце концов, это сработало: (комбинация замечательных ответов Роба Лоуренса и Роба Мэйоффа - спасибо, ребята, и здесь у каждого из вас есть голос! :))

for(int iii = 0; iii < [squareArray count]; iii++) {

    int randX = arc4random() % 321;
    int randY = arc4random() % 481;

    [(UIButton*)[squareArray objectAtIndex:iii] setFrame:CGRectMake(randX, randY, [(UIButton*)[squareArray objectAtIndex:iii] frame].size.width, [(UIButton*)[squareArray objectAtIndex:iii] frame].size.height)];

    CGRect playerRect = CGRectMake(40, 120, 150, 150);

    for(UIButton* b in squareArray)

        if(![b isEqual:[squareArray objectAtIndex:iii]]) {

            if(CGRectIntersectsRect(b.frame, [(UIButton*)[squareArray objectAtIndex:iii] frame]))
            {
                iii--;
                break;
            } else if(CGRectIntersectsRect([(UIButton*)[squareArray objectAtIndex:iii] frame], playerRect)) {
                iii--;
                break;
            } else if(![self checkBounds:[(UIButton*)[squareArray objectAtIndex:iii] frame]]) {
                iii--;
                break;
            }
        }
}
person eric.mitchell    schedule 20.11.2011