Detect a collision on bottom of frame - ios

I am going out of my mind trying to detect a collision for the bottom of the ball when it touches the connect1 UIimageview
if (CGRectIntersectsRect(ball.frame, box.frame))
{
yesno.text=#"Found the spot";
}
**This obviously detects any part of the frame.
If anyone could point me in the correct direction I would be very appreciative.

Instead of looking at the intersection of the ball's frame and the box's frame, look for the intersection of the ball's frame with a rectangle whose top is aligned with the bottom of the box.
CGRect bottom = CGRectMake(box.frame.x, box.frame.y + box.frame.height, box.frame.width, 50.0);
if (CGRectIntersectsRect(ball.frame, bottom))
{
yesno.text=#"Found the spot";
}
Alternatively, assuming that the ball is normally inside the box, it's probably faster to simply check the position of the ball with respect to the box:
CGFloat ballBottom = ball.frame.y + ball.frame.height;
CGFloat boxBottom = box.frame.y + box.frame.height;
if (ballBottom > boxBottom) {
yesno.text=#"Found the spot";
}

Related

Rotating a view using another view's center as the anchor point

I have two image views. The first is the blueish arrow, and the second is the white circle, with a black dot drawn to represent the center of the circle.
I'm trying to rotate the arrow so it's anchor point is the black dot in the picture like this
Right now I'm setting the anchor point of the arrow's layer to a point calculated like this
CGFloat y = _userImageViewContainer.center.y - CGRectGetMinY(_directionArrowView.frame);
CGFloat x = _userImageViewContainer.center.x - CGRectGetMinX(_directionArrowView.frame);
CGFloat yOff = y / CGRectGetHeight(_directionArrowView.frame);
CGFloat xOff = x / CGRectGetWidth(_directionArrowView.frame);
_directionArrowView.center = _userImageViewContainer.center;
CGPoint anchor = CGPointMake(xOff, yOff);
NSLog(#"anchor: %#", NSStringFromCGPoint(anchor));
_directionArrowView.layer.anchorPoint = anchor;
Since the anchor point is set as a percentage of the view, i.e. the coords for the center are (.5, .5), I'm calculating the percentage of the height in arrow's frame where the black dot falls. But my math, even after working out by hand, keeps resulting in .5, which isn't right because it's further than half way down when the arrow is in the original position (vertical, with the point up).
I'm rotating based on the user's compass heading
CLHeading *heading = [notif object];
// update direction of arrow
CGFloat degrees = [self p_calculateAngleBetween:[PULAccount currentUser].location.coordinate
and:_user.location.coordinate];
_directionArrowView.transform = CGAffineTransformMakeRotation((degrees - heading.trueHeading) * M_PI / 180);
The rotation is correct, it's just the anchor point that's not working right. Any ideas of how to accomplish this?
I've always found the anchor point stuff flaky, especially with rotation. I'd try something like this.
CGPoint convertedCenter = [_directionArrowView convertPoint:_userImageViewContainer.center fromView:_userImageViewContainer ];
CGSize offset = CGSizeMake(_directionArrowView.center.x - convertedCenter.x, _directionArrowView.center.y - convertedCenter.y);
// I may have that backwards, try the one below if it offsets the rotation in the wrong direction..
// CGSize offset = CGSizeMake(convertedCenter.x -_directionArrowView.center.x , convertedCenter.y - _directionArrowView.center.y);
CGFloat rotation = 0; //get your angle (radians)
CGAffineTransform tr = CGAffineTransformMakeTranslation(-offset.width, -offset.height);
tr = CGAffineTransformConcat(tr, CGAffineTransformMakeRotation(rotation) );
tr = CGAffineTransformConcat(tr, CGAffineTransformMakeTranslation(offset.width, offset.height) );
[_directionArrowView setTransform:tr];
NB. the transform property on UIView is animatable, so you could put that last line there in an animation block if desired..
Maybe better use much easier solution - make arrow image size bigger, and square. So the black point will be in center of image.
Please compare attached images and you understand what I'm talking about
New image with black dot in center
Old image with shifted dot
Now you can easy use standard anchor point (0.5, 0.5) to rotate edited image

Why am I unable to detect when my UIView I push off screen using UIKit Dynamics is no longer visible?

I'm using UIKit Dynamics to push a UIView off screen, similar to how Tweetbot performs it in their image overlay.
I use a UIPanGestureRecognizer, and when they end the gesture, if they exceed the velocity threshold it goes offscreen.
[self.animator removeBehavior:self.panAttachmentBehavior];
CGPoint velocity = [panGestureRecognizer velocityInView:self.view];
if (fabs(velocity.y) > 100) {
self.pushBehavior = [[UIPushBehavior alloc] initWithItems:#[self.scrollView] mode:UIPushBehaviorModeInstantaneous];
[self.pushBehavior setTargetOffsetFromCenter:centerOffset forItem:self.scrollView];
self.pushBehavior.active = YES;
self.pushBehavior.action = ^{
CGPoint lowestPoint = CGPointMake(CGRectGetMinX(self.imageView.bounds), CGRectGetMaxY(self.imageView.bounds));
CGPoint convertedPoint = [self.imageView convertPoint:lowestPoint toView:self.view];
if (!CGRectIntersectsRect(self.view.bounds, self.imageView.frame)) {
NSLog(#"outside");
}
};
CGFloat area = CGRectGetWidth(self.scrollView.bounds) * CGRectGetHeight(self.scrollView.bounds);
CGFloat UIKitNewtonScaling = 5000000.0;
CGFloat scaling = area / UIKitNewtonScaling;
CGVector pushDirection = CGVectorMake(velocity.x * scaling, velocity.y * scaling);
self.pushBehavior.pushDirection = pushDirection;
[self.animator addBehavior:self.pushBehavior];
}
I'm having an immense amount of trouble detecting when my view actually completely disappears from the screen.
My view is setup rather simply. It's a UIScrollView with a UIImageView within it. Both are just within a UIViewController. I move the UIScrollView with the pan gesture, but want to detect when the image view is off screen.
In the action block I can monitor the view as it moves, and I've tried two methods:
1. Each time the action block is called, find the lowest point in y for the image view. Convert that to the view controller's reference point, and I was just trying to see when the y value of the converted point was less than 0 (negative) for when I "threw" the view upward. (This means the lowest point in the view has crossed into negative y values for the view controller's reference point, which is above the visible area of the view controller.)
This worked okay, except the x value I gave to lowestPoint really messes everything up. If I choose the minimum X, that is the furthest to the left, it will only tell me when the bottom left corner of the UIView has gone off screen. Often times as the view can be rotating depending on where the user pushes from, the bottom right may go off screen after the left, making it detect it too early. If I choose the middle X, it will only tell me when the middle bottom has gone off, etc. I can't seem to figure out how to tell it "just get me the absolute lowest y value.
2. I tried CGRectIntersectsRect as shown in the code above, and it never says it's outside, even seconds after it went shooting outside of any visible area.
What am I doing wrong? How should I be detecting it no longer being visible?
If you take a look on UIDynamicItem protocol properties, you can see they are center, bounds and transform. So UIDynamicAnimator actually modifies only these three properties. I'm not really sure what happens with the frame during the Dynamics animations, but from my experience I can tell it's value inside the action block is not always reliable. Maybe it's because the frame is actually being calculated by CALayer based on center, transform and bounds, as described in this excellent blog post.
But you for sure can make use of center and bounds in the action block. The following code worked for me in a case similar to yours:
CGPoint parentCenter = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
self.pushBehavior.action = ^{
CGFloat dx = self.imageView.center.x - parentCenter.x;
CGFloat dy = self.imageView.center.y - parentCenter.y;
CGFloat distance = sqrtf(dx * dx + dy * dy);
if(distance > MIN(parentCenter.y + CGRectGetHeight(self.imageView.bounds), parentCenter.x + CGRectGetWidth(self.imageView.bounds))) {
NSLog(#"Off screen!");
}
};

How to detect Collision between 2 UIViews

I have a UIImageView which contains my main Character, and I have made the UIImageView appear circle see below code for creating my character
copter = [[UIImageView alloc] initWithFrame:CGRectMake(100, 500, 90, 90)];
[copter setContentMode:UIViewContentModeScaleAspectFit];
copter.layer.cornerRadius = roundf(copter.frame.size.width/2.0);
copter.layer.masksToBounds = YES;
[copter startAnimating];
[[self view] addSubview:copter];
[self setBat:copter];
I am having trouble with my collision between my character and other objects in the game. The collision is being detected on the rectangle rather than the circle. I have searched everywhere for an answer to fix this but no luck.
Here is my collision code I am using:
self.batVelocity += [link duration]*FBDownardBatAccelerationIpad;
[[self copter] setFrame:CGRectOffset([[self copter] frame], 0, [self batVelocity])];
UIView *removeBlock = nil;
for (UIView *block in [self blocks]) {
[block setFrame:CGRectOffset([block frame], [link duration]*FBSidewaysVelocityIpad, 0)];
if (CGRectIntersectsRect([[self copter] frame], [block frame])) {
[self failed];
So basically I need the circle bounds of the character to collide with a rectangle object, not the rectangle bounds of which the circle is in. I hope that makes sense.
Thanks in advance
I'd use a CGPathRef or CGMutablePathRef. So as #user3386109 suggests, check for collision between rects first, then if you have a pathRef for your sprite or whatever it is you can use CGPathContainsPoint(). Quartz has a few other comparison functions for CGPathRef for Rects as well, and for comparing a path against a second path, check the docs. I find CGPathRef to be quite efficient, but don't forget to release it with a CGPathRelease() to match every CGPathCreate or CGPathCopy() function or the leaks can add up real fast..
Right, so first you check for overlapping rectangles (which you've already done). When you find an overlap, then you need to refine the collision test. For each corner of the offending rectangle, compute the distance from the center of your character to the corner of the offending rectangle. If the distance from the center to any of the corners is less than the radius of the circle, then you have a collision. To optimize the code a little bit, compute (dx * dx + dy * dy) and compare that to the radius squared. That way you don't have to compute any square roots.
Upon further review, you also need to do an edge check in addition to the corner check. For example, if the top y value is above the center of the circle and the bottom y value is below the center of the circle, then compute the difference in x between the rectangle left edge and the center of the circle, and if that distance is less than the radius, then a collision has occurred. Likewise for the other three edges of the rectangle.
Here's some pseudo-code for the corner checking
int dx, dy, radius, radiusSquared;
radiusSquared = radius * radius;
for ( each rectangle that overlaps the player rectangle )
{
for ( each corner of the rectangle )
{
dx = corner.x - center.x;
dy = corner.y - center.y;
if ( dx * dx + dy * dy < radiusSquared )
Collision!!!
}
}

Scale CCSprite from bottom right hand corner

So here's the gist of what I'm trying to do here.
I have an array of foreground sprites that I scroll forever as the player moves along. What I would like to do, is when the player starts passing a certain point on the Y axis, scale down those foreground sprites while still moving them.
I'd like to be able to scale the sprites from their bottom left hand corners when the player is going up, and I've got this working without any problems.
The real problem is that I'd also like to scale the sprites from their bottom right hand corners when the player is coming down. Now I thought that I could do this by setting each sprite's anchor point to 1,0 before scaling it, but that doesn't seem to work. The sprites still scale from their bottom left hand corners.
What am I missing here?
// do logic to identify the scale factor we want
for (CCSprite *sprite in foreground_sprites)
{
CGPoint old_anchor = sprite.anchorPoint;
[sprite setAnchorPoint:ccp(1,0)];
[sprite setScale:scale_factor];
[sprite setAnchorPoint:old_anchor];
}
Have you tried messing with this property?
ignoreAnchorPointForPosition(false);
I'm using cocos2d-x, there should be something similar to that
If I understand correctly, you want to scale from the bottom left while the player's Y position increases but scale using the bottom right while they are descending?
Well you can't just change the anchor point alone. The anchor point and position go hand in hand to position the sprite on the screen. So if you positioned the sprite on to the screen using an anchor point of (0,0) then if you want to switch it's anchor point to (1,0) while keeping it in the same location on the screen, you'll need to update the position.
CCSprite* sprite = ...;
sprite.anchorPoint = CGPointZero;
sprite.position = CGPointZero;
...
sprite.anchorPoint = CGPointMake(1.0f, 0.0f);
sprite.position = CGPointMake(sprite.position.x + (sprite.contentSize.width * sprite.scaleX * sprite.anchorPoint.x),
sprite.position.y + (sprite.contentSize.height * sprite.scaleY * sprite.anchorPoint.y));
Hopefully I understood your problem correctly and was able to help.

Change frame not by origin

I have an draggable UIImageView that at some point changes to another UIImageView frame. Problem is I need to make the transformation using the same clicked point in the first UIImageView. I simply do this:
_firstImageView.frameSize = _secondImageView.frameSize;
But the frame changes from the _firstImageView origin. I need to do the transformation from the point I clicked inside the _firstImageView:
CGPoint clickedPoint = [sender locationInView:self.view];
I had tried the layer.anchorPoint but that makes the imageView disappear, don't know why (I did first a conversionPoint from self.view to the _firstImageView reference system)
EDIT e.g for better explanation of problem:
I have a uiimage1 frame with height of 100. And another with 50.
If I click in the point (y=90) of uiimage1 and start dragging, there's an test intersection that I make and if intersects it changes that UIImage1 to the frame of UIImage2. But since the click was on y=90 and UIImage2 only has max y-height of 50, it changes the frame size by the UIImage1 origin. I continue dragging with the click point outside the new frame (that is only y-height=50 and point click is y=90). My question is: Can I change the frame not by its origins but by that point clicked position?
Thanks in advance
+(CGRect)getUpdatedFrame:(CGRect)frame byChangingCenterTo:(CGPoint)newCenter
{
float width = frame.size.width;
float height = frame.size.height;
return CGRectMake(newCenter.x-width/2, newCenter.y-height/2, width, height);
}
+(CGPoint)centerForRect:(CGRect)rect
{
return CGPointMake( rect.origin.x+rect.size.width/2 , rect.origin.y+rect.size.height/2 );
}

Resources