I've created an app where images fall from the sky and I have to "catch" them on the bottom with a user-moveable basket. I put the falling images and the basket image in CGRectMake frames and used CGRectIntersectsRect to detect for collisions. However, it's not detecting every collision. For example, I'll have visually "caught" 10 objects, the two frames definitely intersected, but it will only say I caught 2. What am I doing wrong?
Here is the code for the bucket:
(void)viewDidLoad
{
[super viewDidLoad];
basketView = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"bucket.png"]];
basketView.frame = CGRectMake(130.0, 412.0, 30.0, 30.0);
[self.view addSubview:basketView];
}
Here is the falling object and collision code:
- (void)onTimer
{
UIImageView* appleView = [[UIImageView alloc] initWithImage:appleImage];
int applePosition = round(random() % 320); //random starting x coord
double scale = 1 / round(random() % 100) + 1.0; //random apple size
double speed = 1 / round(random() % 100) + 1.0; //random apple speed
appleView.frame = CGRectMake(applePosition, -100.0, 25.0 * scale, 25.0 * scale);
[self.view addSubview:appleView];
[UIView beginAnimations:nil context:(__bridge void *)(appleView)];
[UIView setAnimationDuration:3 * speed];
appleView.frame = CGRectMake(applePosition, 500.0, 25.0 * scale, 25.0 * scale);
// Test for landing in bucket
if (CGRectIntersectsRect(basketView.frame, appleView.frame)) {
collision++;
printf("%i\n", collision);
[appleView removeFromSuperview];
}
}
EDIT: I made the basket huge, and now I'm getting spammed numbers and no apples are showing up. So I'm assuming that it's only checking for intersection with the apple at its starting point, not during the actual animation. How can I make it check for intersection DURING the animation?
Obviously if your timer function spawns new objects, the test will be done exactly after the creation of the object, so if the object is not actually created in the basket, your hit test will always be negative.
Now, you need another timer which will test for collisions every 10th of a sec or so (depending on how fast you move your objects, this could be more or less). An even better option (form the performance point of view) is to use CADisplayLink which works pretty much like a timer only it allows you to synchronise your tests to the refresh rate of the display.
Related
My app is simple: I want to move a small uiimageview around a view of an iPad with the accelerometer (now Core Motion). So, I can move the uiimageview around with the accelerometer, but I am having trouble establishing working borders in landscape mode. So, here is my code. The problem is that the uiimageview tends to stick to the borders and some of the borders are not perfectly on the edge of the view. Here is my code, any help is greatly appreciated:
- (void)startMyMotionDetect
{
__block float stepMoveFactor = 15;
[self.motionManager
startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init]
withHandler:^(CMAccelerometerData *data, NSError *error)
{
dispatch_async(dispatch_get_main_queue(),
^{
CGRect rect = self.sumbarine.frame;
float movetoY = rect.origin.x + (data.acceleration.x * stepMoveFactor);
float maxY = self.view.frame.size.width-rect.size.width;
float movetoX = (rect.origin.y + rect.size.height)
- (data.acceleration.y * stepMoveFactor);
float maxX = self.view.frame.size.height;
if ( movetoX >0 && movetoX < maxX ) {
rect.origin.x += (data.acceleration.y * stepMoveFactor);
};
if ( movetoY > 0 && movetoY < maxY ) {
rect.origin.y += (data.acceleration.x * stepMoveFactor);
};
[UIView animateWithDuration:0 delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:
^{
self.sumbarine.frame = rect;
}
completion:nil
];
}
);
}
];
}
The sticking issue comes from your conditions for moving. Right now, you say, if "moveToX/Y" is > 0, and less than the width/height, then add Acceleration X/Y.
What happens if origin is == or >greater than "maxX/Y"? Answer: Nothing. And thus, the submarine image acts like it's stuck to a board. Probably it can move on the alternate axis, until it's finally stuck at a condition where it no longer can move.
You'll want to play around with your conditional rules. I do not know what type of actions/input you're trying to implement for gameplay, so I can't tell you what to do next. I'm assuming the sub is supposed to go up/down depending on some user gesture/action.
One last piece of advice. Accelerometers are based on G-Force... What this means for you is even though acceleration on some axis is positive when you move say left... There's also an inverse curve when you need apply the force needed to stop. Try looking at the motiongraphs app (available in documentation) to help you get a better feel/sense of this stuff.
And remember, have fun!
I'm currently creating a iPhone app that displays my speed heading location and a few other sensor readings for use on my bike. I've managed to get the tilt reading from the accelerator. what I'm trying to do is move an image either left or right depending on what the tilt reading is.
_______________________________
: === :
: = = :
: === :
-------------------------------
lets say the = signs is my image, I'm trying to get it to move either towards the left or right edge depending on the tilt angle.
any ideas? I've managed to move it by animations but I don't want it to continuously move if I'm leaning for a long period of time.
First I added the accelerator data using:
UIAccelerometer *theAccelerometer = [UIAccelerometer sharedAccelerometer];
theAccelerometer.updateInterval = 0.1; //in seconds
theAccelerometer.delegate = self;
Notice the timing is 0.1 seconds. You will want to match that timing to the animation below. I then set the animation up in the accelerometer:didAccelerate: delegate call:
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
NSInteger newX = 0;
newX = imageView.frame.size.width/2 + ((320-imageView.frame.size.width) * (acceleration.x+1)/2.0);
[UIView animateWithDuration:0.1 animations:^{
imageView.center = CGPointMake(newX, imageView.center.y);
}] ;
}
It is important to match the animation duration with the updateInterval of the UIAccelerometer so that you make sure to complete the animation before the next update comes through.
The important part here is probably the math to determine the newX position. I basically took half of the width of the image, because that is the left most absolute position. From there I knew I had a 320-imageView.frame.size.width amount of room that I could add. If I added the entire 320-imageView.frame.size.width then I would be at 320-imageView.frame.size.width/2 which is the right-most position. From there I just needed to break up the 320-imageView.frame.size.width chunk according to how much we have accelerated. The accelerator.x data is in the range -1 to 1, so my entire range is 2. So I added 1 to the accelerator.x to normalize it from 0-2. Then I divided by 2.0 so that I would have a range from 0-1. Now I had a multiplier that I could multiply my 320-imageView.frame.size.width range by. If it was 0 I would be at the left-most position. If it was 1 I would be at the right-most position, and anything in between would be broken up linearly. So the final term is:
imageView.frame.size.width/2 + ((320-imageView.frame.size.width) * (acceleration.x+1)/2.0);
to get the new x position of the image.
I hope that is what you are looking for! I tested it on a device and it is pretty smooth as long as you match up your durations.
If you are asking the question about animation. This is code apple recommnad to animate UIView. And you can change value for x and y coordinate.
imageFrame.origin.y = 200;
[UIView animateWithDuration:0.5
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{
datePickerView.frame = imageFrame;
}
completion:^(BOOL finished){
self.datePickerViewFlag = YES;
}];
well I wanted to create particles like (snow ) without openGL or cocs2D, and I found this sample code called snowfall and in this code there is this :
flakeImage = [UIImage imageNamed:#"flake.png"];
// start a timet that will fire 20 times per second
[NSTimer scheduledTimerWithTimeInterval:(0.5) target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
}
// Timer event is called whenever the timer fires
- (void)onTimer
{
// build a view from our flake image
UIImageView* flakeView = [[UIImageView alloc] initWithImage:flakeImage];
// use the random() function to randomize up our flake attributes
int startX = round(random() % 320);
int endX = round(random() % 320);
double scale = 1 / round(random() % 100) + 1.0;
double speed = 1 / round(random() % 100) + 1.0;
// set the flake start position
flakeView.frame = CGRectMake(startX, -100.0, 5.0 * scale, 5.0 * scale);
flakeView.alpha = 0.25;
// put the flake in our main view
[self.view addSubview:flakeView];
[UIView beginAnimations:nil context:flakeView];
// set up how fast the flake will fall
[UIView setAnimationDuration:5 * speed];
// set the postion where flake will move to
flakeView.frame = CGRectMake(endX, 500.0, 5.0 * scale, 5.0 * scale);
// set a stop callback so we can cleanup the flake when it reaches the
// end of its animation
[UIView setAnimationDidStopSelector:#selector(onAnimationComplete:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView commitAnimations]; }
- (void)onAnimationComplete:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
UIImageView *flakeView = context;
[flakeView removeFromSuperview];
// open the debug log and you will see that all flakes have a retain count
// of 1 at this point so we know the release below will keep our memory
// usage in check
NSLog([NSString stringWithFormat:#"[flakeView retainCount] = %d", [flakeView retainCount]]);
[flakeView release];
}
I wanted to know if this code can hurt performance or memory ( maybe because it use a timer)?And if it is not a good idea I heard about CAReplicator that we can use for iphone but the CAReplicatorDemo only work for mac on the apple documentation :/ sorry for my english I'm french :/
Sure, it will affect performance. You will not see it in Simulator though. UIImageView is too heavy to be created 20 times a second. You may try to use a pool of CALayer-s instead (and CAAnimation).
You can use a CAEmitterLayer to do particle effects on iOS 5. I've used it for flames and smoke before but it should work just fine for snow.
You can try my KHParticleView, it use cocos2d particle and add cocos2d view on the top. Check the code here: https://github.com/lephukhanhhuy/KHParticleView
I want to rotate five buttons on the circumference of an circle (circle with the center (120,120), which is actually center of square view i.e. 240*240) with radius 100. IS it possible to do so, with having interaction with the buttons which are rotating and a proper look.
i have tried '
x = round(cx + redious * cos(angl));
y = round(cy - redious * sin(angl));
NSString *X=[NSString stringWithFormat:#"%f",x];
[xval addObject:[NSString stringWithFormat:#"%#",X]];
NSString *Y=[NSString stringWithFormat:#"%f",y];
[yval addObject:[NSString stringWithFormat:#"%#",Y]];'
to calculate points and:
map.frame= CGRectMake([[fqx objectAtIndex:counter1]intValue],[[fqy objectAtIndex:counter1]intValue], 72, 37);
website.frame= CGRectMake([[fqx objectAtIndex:counter2]intValue],[[fqy objectAtIndex:counter2]intValue], 72, 37);
share.frame= CGRectMake([[fqx objectAtIndex:counter3]intValue],[[fqy objectAtIndex:counter3]intValue], 72, 37);
slideShow.frame= CGRectMake([[fqx objectAtIndex:counter4]intValue],[[fqy objectAtIndex:counter4]intValue], 72, 37);'
to rotate but it generate weird path.. in triangular way..
('map','share','slideShow','website') ar my buttons.. :P
This was too cute to pass up so here it is. This code is limited to one button rotating but I doubt you'll have trouble extending it to take multiple (hint: use ONE CADisplayLink and rotate all buttons at once in that).
- (void)setupRotatingButtons
{
// call this method once; make sure "self.view" is not nil or the button
// won't appear. the below variables are needed in the #interface.
// center: the center of rotation
// radius: the radius
// time: a CGFloat that determines where in the cycle the button is located at
// (note: it will keep increasing indefinitely; you need to use
// modulus to find a meaningful value for the current position, if
// needed)
// speed: the speed of the rotation, where 2 * M_PI is 1 lap a second
// b: the UIButton
center = CGPointMake(100, 100);
radius = 100;
time = 0;
speed = 2 * M_PI; // <-- will rotate CW 360 degrees per second (1 "lap"/s)
b = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
b.titleLabel.text = #"Hi";
b.frame = CGRectMake(0.f, 0.f, 100, 50);
// we get the center set right before we add subview, to avoid glitch at start
[self continueCircling:nil];
[self.view addSubview:b];
[self.view bringSubviewToFront:b];
CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:self
selector:#selector(continueCircling:)];
[dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
We also need the actual "continueCircling:" method, which is simply:
- (void)continueCircling:(CADisplayLink *)dl
{
time += speed * dl.duration;
b.center = CGPointMake(center.x + radius * cosf(time),
center.y + radius * sinf(time));
}
I'm sure there are other more nifty ways to solve this, but the above works at least. :)
Edit: I forgot to mention, you will need to add QuartzCore framework and
#import <QuartzCore/QuartzCore.h>
for CADisplayLink.
Edit 2: Found the PI constant (M_PI), so replaced 3.1415 with that.
Well here is my problem:
I have two images : flakeImage and ViewToRotate. What I want is that if flakeImage touches ViewToRotate, ViewToRotate.alpha=0.5; but when FlakeImage appears on the screen ViewToRotate.alpha=0.5; without touching it. I think it's a problem with my view beacause I have :
UIImageView* flakeView = [[UIImageView alloc] initWithImage:flakeImage];
here is the code :
UIImageView* flakeView = [[UIImageView alloc] initWithImage:flakeImage];
// use the random() function to randomize up our flake attributes
int startY = round(random() % 320);
// set the flake start position
flakeView.center = CGPointMake(490, startY);
flakeView.alpha = 1;
// put the flake in our main view
[self.view addSubview:flakeView];
[UIView beginAnimations:nil context:flakeView];
// set up how fast the flake will fall
[UIView setAnimationDuration:7 ];
// set the postion where flake will move to
flakeView.center = viewToRotate.center;
// set a stop callback so we can cleanup the flake when it reaches the
// end of its animation
[UIView setAnimationDelegate:self];
[UIView commitAnimations];
How can I solve this please ?
if someone could help me it would be very cool.
I have a bit of sophomoric experience with this, having written http://itunes.apple.com/us/app/balls/id372269039?mt=8. If you check that app out, you will see a bit of the same problem. This topic is a pretty deep rabbit hole. WHen I started that app, I didn't even know how to write a decent game loop. You will need that first because you need to do precise time-step calculations. AFA the collisions, you update your model and view separately, so if you update the model and objects overlap, you need to back them up until they don't collide and then update your view with the result. If you plan to have a lot of colliding objects, you may hit a wall using UIViews. To complicate things more, if your objects are round, CGRectIntersectsRect won't exactly work. That complicates the math a bit, but it's not too bad. With my app, I found it quite difficult to get the physics to look realistic. THe problem became that ball A and B overlap, so you back them up, then they now intersect other balls, etc, etc. This link is a good starting point, but there are quite a few examples of code out there that "almost" work.
CGRect has a intersection function. The frames of UIViews are CGRects.
if (CGRectIntersectsRect(view1.frame, view2.frame) == 1)
NSLog(#"The views intersect");
else
NSLog(#"The views do not intersect");
The problem I foresee is that if the rects have lots of whitespace, they will appear to intersect before they actually touch
Secondly, you should switch up to block animations. It's strongly encouraged
UIImageView* flakeView = [[[UIImageView alloc] initWithImage:flakeImage] autorelease];
// use the random() function to randomize up our flake attributes
int startY = round(random() % 320);
// set the flake start position
flakeView.center = CGPointMake(490, startY);
flakeView.alpha = 1;
// put the flake in our main view
[self.view addSubview:flakeView];
[UIView animateWithDuration:.7
animations:^ {
// set the postion where flake will move to
flakeView.center = viewToRotate.center;
};
Did this all from memory, no idea if there are errors.
Circular Collision:
a^2 + b^2 < c^2 means they collide
if(pow(view2.frame.origin.x - view1.frame.origin.x, 2) +
pow(view2.frame.origin.y - view1.frame.origin.y, 2) <
pow(view1.frame.size.width/2, 2))
{
//collision
}
else
{
//no collision
}
Again, all from memory, check for errors on your own