Collision Being Detected before Collision Takes Place - objective-c

In the following code I try and to detect if the code is actually detecting collision. But before the bullet and line collides, the debugger says "Collided!"
//Collision Detector
-(BOOL)viewsDoCollide {
if(CGRectIntersectsRect(_bullet.frame, _line.frame)) {
return YES;
} else {
return NO;
}
}
//So that the turrets shoot
- (IBAction)shooterButton:(id)sender {
[self mover:(id)sender];
}
- (void)mover:(id)sender {
CGRect bulletFrame = _bullet.frame;
CGRect lineFrame = _line.frame;
[UIView animateWithDuration:2 animations:^{
CGRect newFrame = CGRectMake(bulletFrame.origin.x + lineFrame.origin.x - 14, bulletFrame.origin.y, bulletFrame.size.width, bulletFrame.size.height);
_bullet.frame = newFrame;
}];
bool collisionDetector = [self viewsDoCollide];
if (collisionDetector == YES) {
NSLog(#"Collided!");
}
}
I presume the collision is "taking place" before it does because I have a set position for the bullet. How do I "shoot" the bullet without using [UIView animateWithDuration]? Or, how do I actually get the code to detect the collision when it actually happens?

Your collision detection code is not within the animation that is running. You've asked the OS to animate from one frame to another, which will happen after this function has returned (on the UI thread).
So, you're detecting collisions before the animation has started.
If you want to do a 2D game, I recommend using something like Cocos2D. It has high-level support for what you want to do.

Related

Completion Blocks Objective-C

I'm trying to write a completion handler in IOS with a block and am not sure exactly why it's not working.
This is what I have so far
typedef void(^myCompletion)(BOOL);
-(void) showAnswer {
[self showAnswer:^(BOOL finished) {
if (finished) {
LnbScene *scene = (LnbScene *)gameScene.lnbScene;
//once the tiles position have been updated then move to the next riddle
[scene nextRiddle];
}
}];
}
// This method should update the position of tiles on the scene
-(void) showAnswer:(myCompletion) compblock{
LnbScene *scene = (LnbScene *)gameScene.lnbScene;
NSArray *tilesArray = [scene.tilesBoundary children];
for (Tile *tile in tilesArray) {
if (tile.positionInAnswer != 17) {
[tile removeFromParent];
[scene.spacesBoundary addChild:tile];
CGPoint targetPoint = CGPointMake(tile.targetPoint.x, tile.targetPoint.y + 6);
tile.position = targetPoint;
}
}
compblock(YES);
}
And I am calling the showAnswer method from a Scene as follows:
GameViewController *gVC = (GameViewController *)self.window.rootViewController;
[gVC showAnswer];
As I step through the code I don't encounter any errors and the sequence proceeds as expected ie. The tile positions are changed and then the completion handler triggers to the nextRiddle method. Only problem is that none of the interface is updated. Is there something I am missing?
I've tested that the tile repositioned code works by taking out the completion block and I view it in the interface and get the desired results. So I'm pretty sure the problem lies in how I'm implementing the blocks. I've never written a block before so I'm really in the dark.
-(void) showAnswer {
LnbScene *scene = (LnbScene *)gameScene.lnbScene;
NSArray *tilesArray = [scene.tilesBoundary children];
for (Tile *tile in tilesArray) {
if (tile.positionInAnswer != 17) {
[tile removeFromParent];
[scene.spacesBoundary addChild:tile];
CGPoint targetPoint = CGPointMake(tile.targetPoint.x, tile.targetPoint.y + 6);
tile.position = targetPoint;
}
}
}
I think the problem seems to be a conceptual mistake - that the "flow" of code
in the -(void) showAnswer:(myCompletion) comp block method truthfully represents the flow of UI elements in time in the presentation layer.
However in practice it is not that straightforward. The UI layer is rendered with 60 Hz framerate. That means you see 60 (frames) screens per second and contents of each of those screens needs to be calculated in advance, processed, flattened, rendered.
That means 1 frame (screen) cycle/pass lasts approx. 16 milliseconds. If I remember correctly you as a programmer have 11 milliseconds to provide all the relevant code so that it can be processed into visual info. This chunk of code is processed all at once which is the answer to our problem.
Imagine if you had a method that would say
{
//some other UI code...
self.view.backgroundColor = [UIColor greenColor];
self.view.backgroundColor = [UIColor yellowColor];
//some other UI code...
}
Now imagine we are inside 1 such 16 millisecond pass.
This piece of code would be processed in advance before any rendering happens and the resultative background colour will be yellow. Why? Because in the cycle preparation phase this code is processed and the framework interprets the green colour as pointless because the value is immediately overwritten by a yellow one.
Thus instructions for the renderer will contain only the yellow background information.
Now back to your actual code. I think all your code is processed fast enough to fit into that 11 milisec window, and the problem is that you do not really wait for the swapping of tiles because there is that block with YES parameter as part of the method and that one already slides to whatever is next.
So the rendered does get instructions to simply move on the the next thing. And it does not animate.
Try putting this "tile swapping" into the "animation" block
[UIview animateWithDuration:....completion] method..and put your block into the completion block. Yes you will have a block in a block but thats fine.
[UIView animateWithDuration:0.4f
animations:^{
//tile swapping code
}
completion:^(BOOL finished)
{
complBlock(YES);
}];
I am not completely sure if it will work but let's try.
The UI relate method should call on main thread. U can try this:
[self showAnswer:^(BOOL finished) {
if (finished) {
LnbScene *scene = (LnbScene *)gameScene.lnbScene;
dispatch_async(dispatch_get_main_queue(), ^{
[scene nextRiddle];
});
}
}];

objective-c animations - how to set end position, direction and type

I've seen many examples and read tutorial on animations,
all examples look +- the same like so:
- (void)showAnimation
{
[UIView animateWithDuration:0.3f animations:^{
backgroundView.alpha = 1.0f;
}];
[UIView animateWithDuration:1.0f
delay:0.0f
usingSpringWithDamping:0.4f
initialSpringVelocity:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^{
CATransform3D init = CATransform3DIdentity;
alertView.layer.transform = init;
}
completion:^(BOOL finished) {
if( [self.delegate respondsToSelector:#selector(alertViewDidAppear:)] && finished) {
[self.delegate alertViewDidAppear:self];
}
}];
}
what I'm failing to understand is in the animation block where:
1. I set the beginning position
2. set the end position
3. the direction of movement
4. type of animation (fly-in/ turn/ fade-in/appear etc.)
There is no "starting position". There is no "direction of movement" or "type of animation". Animation is a change over time. The view is a certain way at the time your code runs. UIView animation has 6 possible view properties. You change any of those in the animation block (which states the time) and the change is animated - that is, instead of the change just happening, kaboom, it is performed over the given time.
That's all there is to it (as far as UIView class-method animation is concerned).

Repeating UIButton animation and change background

Can someone please tell me why this doesn't work?:
//Changing the Images
- (void) squareOneColour {
NSUInteger r = arc4random_uniform(5);
[self.squareOne setBackgroundImage:[self.colorArray objectAtIndex:r] forState:UIControlStateNormal];
}
//Moving buttons
- (void) squareOneMover {
NSUInteger r = arc4random_uniform(3);
[self.squareOne setHidden:NO];
CGPoint originalPosition = self.squareOne.center;
originalPosition.y = -55;
[self.squareOne setCenter:originalPosition];
CGPoint position = self.squareOne.center;
position.y += 760;
[UIView animateWithDuration:r + 3
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
[self.squareOne setCenter:position];
}
completion:^(BOOL complete) {
if (complete) {
[self squareOneColour];
[self squareOneMover];
}
}
];
}
I am trying to get a UIButton to animate down the screen and when the animation is finished, for it to repeat but with a different background. The code above seems it should work but the second animation is never completed. (The UIButton is animated once, then everything stops). I have tried putting the code shown below (into the completion block), with an outside NSUInteger method creating a random number for r but it never works. But it does work when I replace r with a different number, like 1 or 2.
I found the answer. After playing around with it, I found that the function I wanted the UIButton to do was only fit for a UIImageView. I guess I'll have to make an invisible button over the UIImageView
You're stepping on your own feet. Step out to the next cycle of the runloop and all will be well:
[self squareOneColour];
dispatch_async(dispatch_get_main_queue(), ^{
[self squareOneMover];
});

Objective-C: Issue with CGRect .frame intersect/contains

I have two UIImageViews located about center of a horizontal screen and when the user clicks a button another UIImageView, located off screen, slides in from the right. I'm am just trying to detect when the view being brought onto the screen collides with the two static views. The problem is when I run my code and check the CGRect frames they are returned from where the views start, not end, regardless of where I place the frame calls in my method or even if I place them outside the method in a separate one. I'm a bit new to Obj-C and I understand that Core Animation runs on a separate thread and I am guessing that is why I am getting the starting values. (Correct me if I am wrong here).
So, I guess the question here is how do I detect collisions when one item is static and another is animated. Here's my code (feel free to clean it up):
- (IBAction)movegirl
{
//disabling button
self.movegirlbtn.enabled = NO;
//getting graphics center points
CGPoint pos = bushidogirl.center;
CGPoint box1 = topbox.center;
CGPoint box2 = bottombox.center;
//firing up animation
[UIView beginAnimations: nil context: NULL];
//setting animation speed
[UIView setAnimationDuration:2.0 ];
//setting final position of bushidogirl, then resetting her center position
pos.x = 260;
bushidogirl.center = pos;
//running animations
[UIView commitAnimations];
//playing gong sound
[self.audioplayer play];
//enabling button
self.movegirlbtn.enabled = YES;
[self collisiondetection: bushidogirl : topbox];
}
- (void)collisiondetection:(UIImageView *)item1 : (UIImageView *)item2
{
CGRect item1frame = item1.frame;
CGRect item2frame = item2.frame;
NSLog(#"Item1 frame = %d and Item 2 frame = %d", item1frame, item2frame);
}
How about:
if (CGRectIntersectsRect(item1frame, item2frame))
{
// collision!!
}

Show bounding box of UIImageView in UIView

I have written a class extending UIImageView in order to allow me dynamically generate bricks on screen. The brick is a 20x10 PNG.
Here is my codes:
- (id) initBrick:(NSInteger *)str x:(float)ptX y:(float)ptY {
int brickIndex = arc4random() % 10 + 1;
NSString *filename = [NSString stringWithFormat:#"brick%d.png", brickIndex];
UIImage *brickImage = [UIImage imageNamed:filename];
CGRect imageRect = CGRectMake(0.0f, 0.0f, 20.0f, 10.0f);
[self initWithFrame:imageRect];
[self setImage:brickImage];
self.center = CGPointMake(ptX, ptY);
self.opaque = YES;
self.isDead = NO;
return self;
}
Then, I have a simple collision detection function in the same class:
- (BOOL)checkHit:(CGRect)frame {
if(CGRectIntersectsRect(self.frame, frame)) {
isDead = YES;
return YES;
} else {
return NO;
}
}
But the collision detection is not performed well.
The bounding box seems a bit lower than my image.
How to show the bounding box in order to allow me to check the collision?
If the code is unclear, I can supply more information.
You could set the background color to be sure the problem is not caused by the image. But if the image is simple opaque rectangle, it should be fine. I’d set a breakpoint in the checkHit method, see what self.frame gives and think for a while, it can’t be too hard.
And as for the checkHit method, you should either rename it to checkAndSetHit, or (better) do not set the dead flag there:
- (BOOL) checkHit: (CGRect) frame
{
return CGRectIntersectsRect(self.frame, frame);
}
The code would read even a tiny little bit better if you renamed it to hitsFrame or intersectsFrame, but that’s nitpicking.