Reputation: 8845
In an app I give random frames to squares every time it is run. I use this logic to make sure
a) each square is not too close to the player
and
b) each square is contained inside the view of the screen
c) no square touches any other square
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);
}
With this code, I would expect that each square in squareArray would be (once the loop was complete) completely inside the boundaries of the view, not intersecting with any other of the buttons in the array, and completely outside the boundaries of the playerRect rect, which is a square 120 x 120 in the center of the screen. Is my code wrong? Because I get none of these functionalities.
Edit: I do in fact get one of the desired traits of this method: no square ever intersects playerRect. But I still get squares overlapping each other and squares that are partially out of the view.
Edit 2:
I have made these changes to the nested for loop:
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;
}
And now the squares always are within the slightly modified (smaller) rect for the view, and they never intersect the player square. Yay. But they still appear on top of each other. Why?
Upvotes: 1
Views: 902
Reputation: 8845
In the end, this worked: (a combination of the wonderful answers of Rob Lourens and rob mayoff- thanks guys, and here, each of you have an upvote! :) )
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;
}
}
}
Upvotes: 0
Reputation: 385590
Suppose squareArray
contains three buttons A, B, and C with these frames:
A.frame == CGRectMake(10,10,10,10)
B.frame == CGRectMake(10,10,10,10)
C.frame == CGRectMake(20,10,10,10)
Note that A and B overlap, but A and B do not overlap C. Now consider what happens in your inner loop when button == B
.
On the first pass through the inner loop, b == A
, so you detect CGRectIntersectsRect(b.frame, button.frame)
and set shouldContinue = YES
.
On the second pass, b == B
which means [b isEqual:button]
, so you do nothing.
On the third (and final) pass, b == C
. You find that CGRectIntersectsRect(b.frame, button.frame)
is false (because B.frame
does not intersect C.frame
), and CGRectIntersectsRect(button.frame, playerRect)
is false, and !CGRectContainsRect(CGRectMake(10, 10, 300, 460), button.frame)
is false. So you set shouldContinue = NO
.
Then the inner loop exits. You test shouldContinue
, find that it's false, and exit the do/while loop. You have left button B overlapping button A.
Upvotes: 1
Reputation: 16099
It's not a problem with CGRectIntersectsRect
, it's a problem with your logic. When the inner loop finds a single UIButton that does not intersect button, you accept the random coords generated for button. You need to ensure that all the buttons in squareArray don't intersect button, not just one.
Also, what are the frames of the buttons initialized to? Maybe this would be a better solution:
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);
}
Note that I haven't tested that at all. Also, depending on the situation, this could loop forever if there is no valid position for a button. There may be a better solution than placing everything completely randomly, depending on your problem.
Upvotes: 1