How to stop cocos2d accumulating animation delta - ios

I have a weird situation where if my app goes to the background and comes back to the foreground the animations are accumulated somehow and applied super fast. e.g. a CCRotateBy catches up for the time the app was in the background and spins the sprites really fast.
My app delegate has the correct application state handling I think..
- (void)applicationWillResignActive:(UIApplication *)application {
[[CCDirector sharedDirector] pause];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[CCDirector sharedDirector] resume];
[[OALAudioSession sharedInstance] forceEndInterruption];
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
[[CCDirector sharedDirector] purgeCachedData];
}
-(void) applicationDidEnterBackground:(UIApplication*)application {
[[CCDirector sharedDirector] stopAnimation];
}
-(void) applicationWillEnterForeground:(UIApplication*)application {
[[CCDirector sharedDirector] startAnimation];
[[OALAudioSession sharedInstance] forceEndInterruption];
}
- (void)applicationWillTerminate:(UIApplication *)application {
CCDirector *director = [CCDirector sharedDirector];
[[director view] removeFromSuperview];
[director end];
}
- (void)applicationSignificantTimeChange:(UIApplication *)application {
[[CCDirector sharedDirector] setNextDeltaTimeZero:YES];
}
What is causing this to happen?

Its one of the problem we face with cocos2d 2.0.
TEMP_FIX: I used manual pause for animation.
-(void) applicationWillResignActive:(UIApplication *)application
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"AppEnterBackground" object:nil];
if( [navController_ visibleViewController] == director_ )
[director_ pause];
}
//In game layer
-(void)onEnter
{
[super onEnter];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(AppEnterBackground:) name:#"AppEnterBackground" object:nil];
}
-(void)onExit
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"AppEnterBackground" object:nil];
[super onExit];
}
-(void)AppEnterBackground:(NSNotification*)notif
{
[self pauseSchedulerAndActions];
[mHero pauseSchedulerAndActions];
for(MyCoin *coin in mCoinArr)
{
[coin pauseSchedulerAndActions];
}
}
Similar way you can resume.

Related

Push a ViewController from a Cocos2d scene

I am loading a cocos2d game inside an Storyboard. I implemented a CocosViewController that loads the cocos2d scene (IntroLayer) and IntroLayer that starts the game. I would like to go back to CocosViewController or open a new viewcontroller when the timer (countdown) is zero in the game.
I tried calling [[CCDirector sharedDirector] popScene]; when the countdown arrives to zero and with CocosViewController *cocos = [[CocosViewController alloc] init]; [cocos.navigationController dismissViewControllerAnimated:YES completion:nil];
in onExit method but the app crashes when the countdown arrives to zero, any suggestion?
My CocosViewController set up cocos2d
-(void)setupCocos2d {
CCDirector *director = [CCDirector sharedDirector];
//[[[self navigationController] navigationBar] setHidden:YES];
if([director isViewLoaded] == NO)
{
// Create the OpenGL view that Cocos2D will render to.
CCGLView *glView = [CCGLView viewWithFrame:self.view.bounds
pixelFormat:kEAGLColorFormatRGB565
depthFormat:0
preserveBackbuffer:NO
sharegroup:nil
multiSampling:NO
numberOfSamples:0];
// Assign the view to the director.
director.view = glView;
// Initialize other director settings.
[director setAnimationInterval:1.0f/60.0f];
[director enableRetinaDisplay:YES];
}
// Set the view controller as the director's delegate, so we can respond to certain events.
director.delegate = self;
// Add the director as a child view controller of this view controller.
[self addChildViewController:director];
// Add the director's OpenGL view as a subview so we can see it.
[self.view addSubview:director.view];
[self.view sendSubviewToBack:director.view];
// Finish up our view controller containment responsibilities.
[director didMoveToParentViewController:self];
// Run whatever scene we'd like to run here.
NSArray *parameters = [[NSArray alloc] initWithObjects:#"3", #"sound", #"countdown", nil];
if(director.runningScene)
[director replaceScene:[IntroLayer scene];
else
[director pushScene:[IntroLayer scene];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"cocos2d controller viewdidload");
[self setupCocos2d];
}
-(void)viewDidUnload {
NSLog(#"did unload");
[[CCDirector sharedDirector] setDelegate:nil];
[[CCDirector sharedDirector] end];
}
IntroLayer creates the scene for the game. This is my onExit method:
-(void) onExit{
NSLog(#"Introscene exit");
[super onExit];
}
And this is the game. I want to load a viewcontroller when the game finished.
-(void)update:(ccTime)dt{
if (myTime > 0) {
myTime -= (float)dt;
[timeLabel setString:[NSString stringWithFormat:#"%.2f", myTime]];
} else {
myTime = 0;
[timeLabel setString:[NSString stringWithFormat:#"0.00"]];
[[CCDirector sharedDirector] popScene];
}
if (clicks < currentClicks) {
clicks = currentClicks;
//[clicksLabel setString:[NSString stringWithFormat:#"%i", clicks]];
}
}
-(void) onExit
{
[super onExit];
[[CCDirector sharedDirector] stopAnimation];
CocosViewController *cocos = [[CocosViewController alloc] init];
[cocos.navigationController dismissViewControllerAnimated:YES completion:nil];
// [cocos.navigationController popViewControllerAnimated:YES];
//AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//[app.navController popViewControllerAnimated:YES];
//dismissModalViewControllerAnimated:YES
NSLog(#"game exit");
}
For pop IntroLayer To ViewConrtroller As below code
[[[CCDirector sharedDirector] navigationController] popViewControllerAnimated:YES];

iPad UIKeyboard Hide / Resign / Dismiss Button Event Handler?

Can you see the button in the bottom right of the iPad keyboard?
Well I want access to the event handler for that button, Because, I have some logic that I need to administer when specifically THAT button is pressed.
My attempted solution included using the UIKeyboardWillHideNotification notification like so:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(hideKeyboardTapped:)
name:UIKeyboardWillHideNotification
object:nil];
However this is called everytime the keyboard is dismissed, including when search cancel button is pressed, with a gesture tap on the background, etc; all of which have slightly different logic.
So I just need to have one set of logic for when that specific "hide keyboard" button is pressed.
The UIKeyboard is activated by a UISearchBar (for those of you who need extra info).
Any help would be greatly appreciated; and I'll answer any questions you may have.
There is not an official, Apple-sanctioned way to do this.
If you don't mind mucking around with private classes, which could get you rejected from the App Store, there is an unsupported method. Basically, you listen for changes to the keyboard's activeKey property, and if the new active key has a name of "Dismiss-Key", then the dismiss key was tapped:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"activeKey"])
{
id activeKey = [object valueForKey:keyPath];
NSString *keyName = [activeKey valueForKey:#"name"];
if ([keyName isEqualToString:#"Dismiss-Key"])
{
[self didTapDismissKey];
}
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)keyboardDidShow:(NSNotification *)notification
{
// Search for the UIKeyboardLayoutStar class, which seems to manage layout for the keyboard, and observe its activeKey property
UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
[self traverseViewHierarchyWithView:topWindow block:^(UIView *view, BOOL *stop) {
if ([view isKindOfClass:NSClassFromString(#"UIKeyboardLayoutStar")])
{
[view addObserver:self forKeyPath:#"activeKey" options:0 context:NULL];
}
}];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
// Don't forget to remove the controller as an observer!
UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
[self traverseViewHierarchyWithView:topWindow block:^(UIView *view, BOOL *stop) {
if ([view isKindOfClass:NSClassFromString(#"UIKeyboardLayoutStar")])
{
[view removeObserver:self forKeyPath:#"activeKey"];
}
}];
}
- (BOOL)traverseViewHierarchyWithView:(UIView *)view block:(void (^)(UIView *, BOOL *))block
{
BOOL stop = NO;
block(view, &stop);
if (stop)
{
return YES;
}
for (UIView *subview in view.subviews)
{
if ([self traverseViewHierarchyWithView:subview block:block])
{
return YES;
}
}
return NO;
}
- (void)didTapDismissKey
{
NSLog(#"Dismiss key tapped");
}
Tested on the iOS 7 iPad simulator and it works.
I don't know if you can put a listener to a single button of the keyboard, but you can try to set some Booleans on the views, that can turn on/off the events that you want or don't want to handle (I would approach it like that), for example:
Add a custom notification observer/listener at the class where you
want to execute the method of the event.
Add the Apple default Keyboard Hide listener on the controller where the notification will be triggered.
Then, on the views that can trigger the event... Set the booleans to activate/deactivate the event action
#Some Other Class
-(void)viewWillAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(doSomething)
name:#"doSomethingNotification"
object:nil];
}
-(void)viewWillDisappear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"doSomethingNotification" object:nil];
}
#Trigger Class
-(void)viewWillAppear:(BOOL)animated{
self.isNotificationNeeded = NO;
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(onKeyboardHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
-(void)viewWillDisappear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
-(void)onKeyboardHide:(id)sender{
//When your event is Happening
if (self.isNotificationNeeded == YES) {
NSNotification *n = [NSNotification notificationWithName:#"doSomethingNotification" object:nil];
[[NSNotificationCenter defaultCenter] postNotification:n];
self.isNotificationNeeded = NO;
}
}
-(void)userClickedCancelButton{
//If this is a keyboard hide event that you don't want to trigger
self.isNotificationNeeded = NO;
}
//SearchBarDelegate
-(BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar{
self.isNotificationNeeded = YES; //If your event can happen from here
return YES;
}

iOS : How to detect Shake motion?

I added the following code to my appDelegate.m
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake )
{
// User was shaking the device. Post a notification named "shake".
[[NSNotificationCenter defaultCenter] postNotificationName:#"shake" object:self];
}
}
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}
- (void)shakeSuccess
{
// do something...
}
and then I added :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// INIT THE BACKGROUND PATH STRING
[self refreshFields];
[self.window addSubview:navController.view];
[self.window makeKeyAndVisible];
***[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(shakeSuccess) name:#"shake" object:nil];***
return YES;
}
When I start my app on my iPhone, the method named "shakeSuccess" doesn't called.
What should I do to implement this function in my app?
any idea?
This might help you...
https://stackoverflow.com/a/2405692/796103
He says that you should set the UIApplication's applicationSupportsShakeToEdit to
YES. and override 3 methods in your VC:
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
The rest of your code is fine. (the -motionEnded:withEvent:)
You could do something like this...
Firstly...
Set the applicationSupportsShakeToEdit property in the App's Delegate:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
application.applicationSupportsShakeToEdit = YES;
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
Secondly...
Add/Override canBecomeFirstResponder, viewDidAppear: and viewWillDisappear: methods in your View Controller:
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
Thirdly...
Add the motionEnded method to your View Controller:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
// your code
}
}
That should work if the first answer did not and this is only quickly typed not tested:)
If you want to be able to detect the shake motion across the app, the best way is to override the class of your application with a custom class.
And then implement this in your custom application class
#implementation PSApplication
- (void)sendEvent:(UIEvent *)event
{
if ( event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake ) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"shakeNotification" object:nil];
}
[super sendEvent:event];
}
#end
I extended UIApplication class and added class reference to main:
MyApplication.h
#interface MyApplication : UIApplication
#end
MyApplication.m
#implementation MyApplication
- (void) sendEvent:(UIEvent *)event
{
if( event && (event.subtype==UIEventSubtypeMotionShake))
{
AppDelegate *objAppDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
[objAppDelegate doWhatEver];
[super sendEvent:event];
}
else
{
[super sendEvent:event];
}
}
#end
And the last step in main.m
int main(int argc, char *argv[])
{
return UIApplicationMain(argc, argv, NSStringFromClass([MyApplication class]), NSStringFromClass([AppDelegate class]));
}
This works in all cases.

Black screen on iPad retina display

I've a problem with a little game application running on iPad. When I lunch the simulator iOS 5.1 with iPad retina display, the application start with my default image #x2 and after show the image go to a black screen. In normal iPad iOS 5.1 simulator run well. Xcode tell me only this:
2012-04-25 14:24:26.978 AppName[83696:10a03] cocos2d: Frame interval: 1
2012-04-25 14:24:26.980 AppName[83696:10a03] cocos2d: surface size: 1536x2048
Any idea?
Thank you
#Smamim Hossain: Same error I put my full Appdelegate.m
#import "cocos2d.h"
#import "AppDelegate.h"
#import "GameConfig.h"
#import "Intro.h"
#import "RootViewController.h"
#implementation AppDelegate
#synthesize window;
- (void) removeStartupFlicker
{
//#if GAME_AUTOROTATION == kGameAutorotationUIViewController
//#endif // GAME_AUTOROTATION == kGameAutorotationUIViewController
}
- (void) applicationDidFinishLaunching:(UIApplication*)application
[director setProjection:kCCDirectorProjection2D]
{
// Init the window
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
[CCDirector setDirectorType:kCCDirectorTypeDefault];
[director setProjection:kCCDirectorProjection2D]
CCDirector *director = [CCDirector sharedDirector];
// Init the View Controller
viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
viewController.wantsFullScreenLayout = YES;
//
// Create the EAGLView manually
// 1. Create a RGB565 format. Alternative: RGBA8
// 2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition
//
//
EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8
depthFormat:0 // GL_DEPTH_COMPONENT16_OES
];
// // Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices
if( ! [director enableRetinaDisplay:YES] )
CCLOG(#"Retina Display Not supported");
#if GAME_AUTOROTATION == kGameAutorotationUIViewController
[director setDeviceOrientation:kCCDeviceOrientationPortrait];
#else
[director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
#endif
[director setAnimationInterval:1.0/60];
[director setDisplayFPS:NO];
[director setDepthTest:NO];
// make the OpenGLView a child of the view controller
[viewController setView:glView];
// make the View Controller a child of the main window
[window addSubview: viewController.view];
[window makeKeyAndVisible];
// Default texture format for PNG/BMP/TIFF/JPEG/GIF images
// It can be RGBA8888, RGBA4444, RGB5_A1, RGB565
// You can change anytime.
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
// Removes the startup flicker
[self removeStartupFlicker];
// Run the intro Scene
[[CCDirector sharedDirector] runWithScene: [Intro scene]];
}
- (void)applicationWillResignActive:(UIApplication *)application {
[[CCDirector sharedDirector] pause];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[CCDirector sharedDirector] resume];
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
[[CCDirector sharedDirector] purgeCachedData];
}
-(void) applicationDidEnterBackground:(UIApplication*)application {
[[CCDirector sharedDirector] stopAnimation];
}
-(void) applicationWillEnterForeground:(UIApplication*)application {
[[CCDirector sharedDirector] startAnimation];
}
- (void)applicationWillTerminate:(UIApplication *)application {
CCDirector *director = [CCDirector sharedDirector];
[[director openGLView] removeFromSuperview];
[viewController release];
[window release];
[director end];
}
- (void)applicationSignificantTimeChange:(UIApplication *)application {
[[CCDirector sharedDirector] setNextDeltaTimeZero:YES];
}
- (void)dealloc {
[[CCDirector sharedDirector] end];
[window release];
[super dealloc];
}
#end
try this line of code at
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
[director setProjection:kCCDirectorProjection2D];
hope this will fix your issue.

Resume game cocos2d

I used he code below to handle pause and resume buttons in my game
To Pause:
-(void)pauseTapped{
...
[[SimpleAudioEngine sharedEngine] pauseBackgroundMusic];
[[CCDirector sharedDirector] pause];
...
}
To Resume:
-(void)resumeGame: (id)sender {
...
[self removeChild:pauseMenu cleanup:YES];
[[SimpleAudioEngine sharedEngine] resumeBackgroundMusic];
[[CCDirector sharedDirector] resume];
...
}
The problem is if the used click pause then enterBackground (click Home button) mode
when he returns, the game automatically resume and the pause menu still exist
Any idea will be great
UPDATE:
the AppDelegate code:
- (void)applicationWillResignActive:(UIApplication *)application {
[[CCDirector sharedDirector] pause];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[CCDirector sharedDirector] resume];
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
[[CCDirector sharedDirector] purgeCachedData];
}
-(void) applicationDidEnterBackground:(UIApplication*)application {
[[CCDirector sharedDirector] stopAnimation];
}
-(void) applicationWillEnterForeground:(UIApplication*)application {
[[CCDirector sharedDirector] startAnimation];
}
- (void)applicationWillTerminate:(UIApplication *)application {
CCDirector *director = [CCDirector sharedDirector];
[[director openGLView] removeFromSuperview];
[viewController release];
[window release];
[director end];
}
- (void)applicationSignificantTimeChange:(UIApplication *)application {
[[CCDirector sharedDirector] setNextDeltaTimeZero:YES];
}
- (void)dealloc {
[[CCDirector sharedDirector] end];
[window release];
[super dealloc];
}
Thank you
You should add a property to your app delegate that will keep track if pause was caused by user tapping pause button or automatically.
Inside YourAppDelegate.h:
#property (nonatomic) BOOL userPaused;
And inside YourAppDelegate.h:
#synthesize userPaused;
Then inside your scene's pause method, add:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.userPaused = YES;
And inside your scene's resume method, add:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.userPaused = NO;
Now you can edit your app delegate's -applicationWillResignActive: method to only resume if userPaused is not set to YES.
- (void)applicationDidBecomeActive:(UIApplication *)application {
if (!self.userPaused) {
[[CCDirector sharedDirector] resume];
}
}

Resources