cocos2d iphone wait for action to complete/finish - iphone

I'm trying to force an action to complete before another action can be called. I've looked all over for different examples of timing delays and CCSequencing and this is what i have so far, but it still isn't working. I need the full .5 to come out before another action (whether it be left, right or down) can be called. Everything is working as long as you don't hit the buttons before an action finishes, thats when the previous action is cut short.
-(void) moveup{
CCNode *mySprite = [self getChildByTag:kTagSprite];
//moves sprite up 30 pixels in 1/2 second
[mySprite runAction: [CCMoveTo actionWithDuration:.5 position:ccp(mySprite.position.x, mySprite.position.y+30)]];
}
//this is inside CCtouchesbegan. upsprite is a box that gets clicked to move the object up
if(CGRectContainsPoint([upsprite boundingBox], point)){
//should call CCSequence of events, method moveup, and then CCDelayTime, forcing a pause
[self runAction:[CCSequence actions:[CCCallFuncND actionWithTarget:self selector:#selector(moveup) data:nil], [CCDelayTime actionWithDuration:.5], nil]];
}
I've tried pausing using [[CCDirector sharedDirector] pause] but this freezes everything, including the sprite I'm moving. The CCDelayTime doesn't seem to be working in my current example.
I know this question is kind of a duplicate of a couple others about ccsequencing, so if it needs to be marked as a dupe, that's fine, but I'd really like some help as it's really holding me back.

You could make a BOOL "lock" variable, and only run the action on "mySprite" :
if(lock) {[mySprite runAction:...];}
set the BOOL lock to NO in the beginning and everytime you begin the action set it to "YES"...then add a CCCallFunc to the CCSequence to set the lock back to "NO"

Related

How can I synchronize both movement and an animation using cocos2d?

I need help getting a CCSprite to animate and move in a synchronized fashion repeatedly. For example I need to move the sprite from one game board space to the next while walking. While I can call each action right after one another, I have found that when run in rapid succession the two don't fire at the same time causing either an additional animation cycle to run between moves or movements to not be aligned with the start/end of the animation.
Here are examples of how I'm creating the animation and movement actions.
CCAnimation *animRunUR_ = [CCAnimation animationWithFrame:#"actor_run_ur" frameCount:8 delay:runAnimDuration_];
CCAction *_actionAnimRunUR = [[CCSequence actions:
[CCAnimate actionWithAnimation:animRunUR_],
[CCCallFunc actionWithTarget:self selector:#selector(animationFinished)],
nil] retain];
CCAction *_action = [[CCSequence actions:
[CCMoveTo actionWithDuration:duration_ position:gridbox_.center],
[CCCallFunc actionWithTarget:self selector:#selector(nextMovement)],
nil] retain];
Each of the above actions are called directly on the CCSprite as follows.
[self.sprite runAction:_action];
[self.sprite runAction:_actionAnimRunUR];
The duration of the animation is calculated to be equal to the amount of time it takes to move to the next game board space. In addition, these are fairly straight forward actions but I'm really having a hard time keeping them synced as they continually repeat.
Any best practices or suggestions would be greatly appreciated as I'm sure this is a common issue in game development with sprites that continually animate while moving. Then trying to keep smooth transitions between things like walk, run, and jump without split second pauses between actions. Thanks for your feedback and suggestions.

hot to disable touch handling in CCLayer in cocos2d

I've got a CCLayer subclass i'm using to display some sprites and to show some animations. Also it has a CCMenu with some items. When user selects some of the menu item i want to run an animation and then to show another scene. But i want user not to be able to touch anything on the screen while animation is running.
Of course, i can just disable handling touches in my callbacks, but maybe there is more simple way - just to disable all touch handling for a while ?
Disable touch dispatcher before animation running and enable touch dispatcher after animation stopped. Here is the code snippet:
[[CCDirector sharedDirector] touchDispatcher].dispatchEvents = NO;
CCAnimation* animation = [CCAnimation animationWithFrame:#"numberexplode" frameCount:5 delay:0.2];
CCAnimate* animate = [CCAnimate actionWithAnimation:animation];
CCCallBlock* completion = [CCCallBlock actionWithBlock:^{
[[CCDirector sharedDirector] touchDispatcher].dispatchEvents = YES;
}];
CCSequence* sequence = [CCSequence actions:animate, completion, nil];
[self runAction:sequence];
You want to look at the CCTouchDispatcher singleton class. If you add a targeted touch handler that swallows touches (and does nothing) then you won't get any touches handled. As far as I can tell there's no way to totally disable touches.
Alternatively you can make a new CCLayer that's on top of everything else (I think z order really high will do this), and make it clear, and have it do nothing with touches.
hope that helps.

Is it possible to differentiate between a long press and a tap on a button?

Can we call different actions / delegates in response to the two different events of
A tap on a UIButton
A tap-and-hold on a UIButton
?
Yes, it's reasonably easy to implement this using a UILongPressGestureRecognizer (on iPhone OS 3.2+). A long press will be handled by the gesture recognizer, and a short tap will pass through to the button's normal action.
For example, I subclassed UIButton and added the following method for specifying a long touch action to go along with a tap (longPressGestureRecognizer is an instance variable):
- (void)setLongTouchAction:(SEL)newValue
{
if (newValue == NULL)
{
[self removeGestureRecognizer:longPressGestureRecognizer];
[longPressGestureRecognizer release];
longPressGestureRecognizer = nil;
}
else
{
[longPressGestureRecognizer release];
longPressGestureRecognizer = nil;
longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:[[self allTargets] anyObject] action:newValue];
[self addGestureRecognizer:longPressGestureRecognizer];
}
}
I then could do the following to set both short tap and long press actions that will be handled by the same target:
[undoButton addTarget:self action:#selector(performUndo:) forControlEvents:UIControlEventTouchUpInside];
[undoButton setLongTouchAction:#selector(showUndoOptions:)];
As you can see, this is useful for the undo buttons you see in title bars of many iPad applications.
Brad Larson's answer looks pretty good but here's another one that might give you a bit more flexibility/control of what you want or might want to do.
You subclass UIButton, you override the touchesBegan and touchesEnded methods so that when the user starts a touch you call
[self performSelector:#selector(detecetedLongTap) withObject:nil afterDelay:1.0];
and in the touchesEnded you call:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(detecetedLongTap) object:nil];
to cancel the event if the finger was lifted too soon.
You can get full code for this in this blog post:
http://www.isignmeout.com/adding-long-tap-functionality-uibutton/
The best solution I can think of, is to create another class, and subclass UIButton. Then on Interface Builder (if that's what you're using), you can set the button's class to the custom class you just created.
So in this new class, you have to override a method called
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
This is basically telling you that someone pressed down on your button. The touches is an NSSet and it holds all the information for all the fingers that are pressing down on the screen. If you only are interested in the one that's pressing on the button itself, you'll probably have something like:
NSSet *myTouches = [event touchesForView:self.view];
So now that you have the touches that correspond to your button, you have to find out how many times the user tapped on that button. You do that with something like:
NSUInteger numTaps = [[myTouches anyObject] tapCount];
If this number is 2, that means the user just double tapped your button. Now comes the next part. How do you know if the user is holding that button? Well when the user lets go of the screen, another method gets called. You'll need to override that one as well:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
This is where you know if the person has stopped touching the screen or if his finger is still on it. If his finger is still on it, then this event hasn't been called yet.
Now enough with the background
Here's my suggestion to you. I suggest you override the touchesBegan: method and check if the number of taps in the button is 2. If so, then start a timer that does what you need it to do, for as long as you need it to be done, and then on the touchesEnded: method, you'll go ahead and stop that timer, and this way you will have done whatever it is that you needed to do, for as long as you needed to do it OR as long as the user has held on to the button.
I hope this helps, obviously I didn't write the whole code for you, you'll have to experiment and research that stuff, but if you have any questions, I'll be happy to lend a helping hand :)

How can I fix 'sticky' touchesMoved in my openGLES app?

I have my openGL scene rendering using the detach thread method
//This is at the end of my init method
SEL selector = #selector(preMainLoop);
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:selector object:nil];
[thread start];
[thread release];
}
-(void) preMainLoop
{
while (isRunning) {
NSAutoreleasePool *loopPool = [NSAutoreleasePool new];
[self performSelectorOnMainThread:#selector(mainLoop) withObject:nil waitUntilDone:YES];
[loopPool release];
}
}
When I start getting touch events, I want to update my scene accordingly. But it seems like the scene is updating much faster than the iPhone is registering the touch events. For testing I'm trying to just drag a box around the screen based on the current position of a UITouch (updating the position during touchesMoved). I also have another box that moves independently, unaffected by touches.
The indepent box moves around smoothly, at a nice 60 frames per second. The touch box has 'jerky' movement which leads me to believe that the render loop is shoving out touch events or something to that affect.
Any help appreciated!
This forum thread has a long discussion comparing and contrasting NSTimer vs. MainLoop. In particular, I think you are looking for the following:
//Yield to system calls (touches, etc.) for one ms.
while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002, FALSE) == kCFRunLoopRunHandledSource);
However since you are doing this on the same single thread as everything else, I personally doubt you will be seeing any performance improvement over a properly configured NSTimer, and you may even take a hit. Your mileage may vary, but it might be worth testing both ways and doing some metrics before you launch. It's an easy enough thing to test.

how to stop UIAccelerometer?

I have implemented application in which there is one speedo meter and three buttons.
1)Start 2)stop 3)review
When i press the start button than UIAccelerometer is start
when i press stop button than UIAccelerometer is stop.
when i press review button than in next view it will show hisry of time.
My problem is that:
When i press the start button than apliuction is terminated.
how to start and stop UIAccelerometer ??
Use this to start the accelerometer measurement via delegate method accelerometer:didAccelerate:
[UIAccelerometer sharedAccelerometer].delegate = self;
And this to stop it
[UIAccelerometer sharedAccelerometer].delegate = nil;
To "start" and "stop" accelerometer (i.e. to register and ignore the accelerometer inputs), set the UIAccelerometer delegate to the class which will use the data (on start) or set the delegate to nil (to stop). If you set the delegate to nil, the accelerometer data will not be used by your app.
In case the issue is something else (an error in your code etc), please post what you have tried till now and you'll get more helpful answers.
Comment out the UIAccelerometer code and then try start, stop, review, and start to see if it still crashes.