Cocos2d doubts on CCDirector pause method - iphone

I have seen several posts (E.g. this one) on this but I still don't get it.
If I call the CCDirector pause method it sets the animation interval to 1/4 and the value isPaused_ to YES (see code below). In the CCDirector.m class the isPaused_ variable
doesn't seem to be used much apart of in the pause and resume methods.
I thus decided to call also the stopAnimation method but in some posts this is not mentioned:
[[CCDirector sharedDirector] stopAnimation]
It doesn't apparently stop accelerometer data and input data to be sent to the main scene. It kind of does make sense to me because the developer of the Game might want to allow the user to resume by shaking the iPhone or tapping a resume button. Is this the reason behind this choice?
Also, why is the animation interval set to 1/4 and why things even with this value don't move (apart of my player entity that moves using accelerometer input)?
Thanks a lot!
-(void) pause
{
if( isPaused_ )
return;
oldAnimationInterval_ = animationInterval_;
// when paused, don't consume CPU
[self setAnimationInterval:1/4.0];
[self willChangeValueForKey:#"isPaused"];
isPaused_ = YES;
[self didChangeValueForKey:#"isPaused"];
}

Pausing CCDirector reduces framerate to 4 fps in order to conserve both battery and CPU cycles. The latter is necessary if you use UIKit views, in some cases it is necessary to pause the director in order for UIKit animations to animate smoothly.
What pausing the director also does is that it stops updating all nodes. Specifically the CCScheduler doesn't fire any scheduled selectors or update methods. It also prevents touches from being passed on to nodes, because they are relayed through the CCTouchDispatcher which is also paused when the director is paused.
But as you noticed, the accelerometer isn't paused. That's because cocos2d doesn't provide a wrapper for UIAccelerometer and therefore you get these notifications directly from iOS, ignoring the pause status of the director. If you then change the position of nodes inside the didAccelerate method or another method called directly from it, that node will change its position despite director being paused.
This is but one reason why director's pause isn't really suitable for a "pause game" feature. Another issue is that pausing simply would prevent any pause menu built with cocos2d features would also be paused, which kind of defies the purpose.
Then startAnimation and stopAnimation are simply extreme measures that prevent cocos2d from updating the screen altogether. This is normally only used in situations where either the cocos2d view is removed temporarily from the view hierarchy, or hidden, or some UIKit view is going fullscreen. In that sense stopAnimation is like a suspend feature.

Related

How to save data with a delay?

I'm wondering if iOS allows one to do the following:
I have a puzzle game and I've been working on saving data when the user returns to the home screen. To do this, using NSNotificationCenter, I had one of my game classes to observe [UIApplication sharedApplication]'s ApplicationWillResignActive method so when that happens I save my game state. But if the user decides to exit while animations are going on, the game will save a midState where the model values are still changing and that will often cause crashes. My question is if it is possible to somehow delay the saving process (even though it is on the background) until the animations are complete (or some variable equals 1)?
My first idea is to create scheduled event with NSTimer to try to save until everything is set. Any ideas would be appreciated. Thank you
You can use performSelector:withObject:afterDelay:
// Call doSomething after a 1 second delay
[self performSelector:#selector(doSomething) withObject:nil afterDelay:1.0f];
Rather than trying to delay the saving, especially in ApplicationWillResignActive, you should look into how to stop the animation and record the expected final values of the animation. Depending on the method of animation (UIView static methods, block based, 3rd party) there is usually a way to stop them, and since you define what the animation does you should already know the final state.
In the case of block based animations:
How to cancel UIViews block-based animation?

How to respond to any kind of interruption?

My app is playing a pretty complex animation. It's like a flipbook.
What I do is: I have a huge loop with selectors, and after every delayed call the next one is called.
Now someone calls the user and the device suddenly shows up this fat green status bar and maybe some big pick-up-the-phone-call overlay. Or: The alarm clock rings, and a big alert sheet appears in front of just about everything.
It would be great to just pause the whole animation in case of ANY interruption. Probably I've also missed like 5 more possible interruptions.
How are you doing that? How do you get notified for all interruptions and then call one single -stopEverything method?
Whenever the app becomes inactive, the UIApplicationWillResignActiveNotification local notification will be posted. In the opposite suituation, the UIApplicationDidBecomeActiveNotification notification will be posted.
Your animation logic can listen to this and respond appropriately. There is an +setAnimationsEnabled: method to kill all current and future animations, but there isn't a documented "global pause" method.
Depending on the animation, you might be better off using CoreAnimation directly by using a single CAKeyframeAnimation on the view's -layer rather than having one animation's completion selector start another animation.

How can I accurately time animations on an iPhone inside the normal flow of a program?

I've been using CALayer animations in my program, primarily to move around CALayers with animation on my screen. My problem is that I don't want to exit the layer-moving (card dealing, as it happens) method until all of the cards are done dealing. How can this be done cleanly?
Presume that my layer-moving method is called by different methods at different times, so it's better to let the normal flow of the program bring me back to the starting place, rather than trying to go back and forth with delegation.
Trying something simple like udelay() didn't work because it appears to block the display of the animation too.
Any ideas? Bonus if it's something I could use to pause for the completion of a built animation too (specifically, the animated dismissal of a modalViewController in what I'm working on now, but it'd be nice to have mechanisms that would regular pause, then continue on when various Animated: things occur).
CAAnimation has a delegate method animationDidStop:finished: and UIView has setAnimationDidStopSelector:. You can pause what you are doing at the beginning of the animation, and use these to resume. For example, if you want to pause a timer, set timerRunning = NO right at the animation code, then timerRunning = YES in the didStop method.

touchesMoved behavior in iPhone and Simulator

The function touchesMoved behaves differently in iPhone and simulator.
The repeating interval (refresh rate) of the function touchesMoved is much faster than simulator. Is there a way to deal with the difference?
Often people are finding this to be a problem because they are doing something intensive in the touchesMoved handler and when events arrive very frequently, it makes the interface appear to lag.
A relatively simple way to deal with this is: First of all, in the touchesMoved handler, store the touch position in a variable that represents the position of whatever is tracking the finger.
Return from the touchesMoved handler immediately. Create an NSTimer object and set your view controller as a delegate of it and have that do whatever re-drawing/view moving behaviour used to be in your touchesMoved handler. Thus, you get a near constant movement regardless of the time between touchesMoved events.
If you're really advanced you can use a custom NSRunLoop instead of using a timer, but that's more than I can explain here :) The general idea is: don't be doing everything in the touch event handlers.

Timer and animation events trumping TouchesEnded events

I've implemented a tap-and-hold handler using an NSTimer that I first set in the TouchesBegan overload.
However, what I actually want is for an action to be continuously performed in quick-fire succession while the touch is being held. So, on timer expiry I call a handler to do the work, which then sets another timer and the cycle continues until the TouchesEnded comes in and cancels it, or another terminating condition is met.
This works fine, until my handler code triggers an animation to go off at the same time.
Now we have animation events and timer events going off, and in all that we need to handle TouchesEnded as well.
What I am finding is that, if the animation is triggered, and I set my timer to less than 0.025 seconds, my TouchesEnded event doesn't come through until the timer cycle stops (the other terminating condition). Setting a slower timer, or not triggering the animation, make it work (TouchedEnded comes in straight away), but are not what I want.
Obviously this is all on the device (release build - no NSLogs) - in the sim it all works fine
Is there any way of setting the relative priorty of these events - or is it likely I'm missing something else obvious here?
[Update]
I've worked around this in this instance by doing the continuous part without visual feedback until it's done (which from this users perspective is instant). I think this is ok for now. I'd still like to hear any more thoughts on this (Jeffrey's idea was good), but I'm not waiting on tenterhooks now.
Try writing your own Timer-type class by spawning off onto a thread. Example:
BOOL continue = YES; //outside of your #implementation
-(void)doLoop
{
while(continue){
[NSThread sleepForTimeInterval:.025];
[self performSelectorOnMainThread:#selector(whateverTheFunctionIs) waitUntilDone:YES];
}
}
and this would be started by [NSThread detatchNewThreadSelector:#selector(doLoop) toTarget:self withObject:nil]. This is not exactly threadsafe, but you can choose to wrap the boolean into a NSNumber and then do #synchronize on it if you so choose. Alternatively, after I wrote that little snippet I realized it would be better to do a check against the current NSTime instead of sleepForTimeInterval: but you get the point. :)