There are at least 2 ways to implement this.
The first doesn't work in the simulator, because it uses accelerometer values only to detect a shake by a given threshold of movement. This works fine and has the benefit that you can adjust the sensitivity by adjusting the threshold value - see: http://stackoverflow.com/questions/3416536/how-to-detect-shake-on-the-iphone-using-cocos2d
The caveat is that this is not the native shake event - so you cannot use this if you want to use the "shake device" menu option of the simulator.
The second way is the Apple way which works on the simulator too.
The main thing to understand is that classes derived from UIView (which implement the UIResponder protocol) are capable of detecting events like "motion ended". But for reasons of software design you don't want the view(s) to handle those events therefore you need some sort of delegation which is natively done by forwarding the event to the first responder of the view. So you need to make sure your view controller is the first responder, reacts on "motion ended".
To get this to work you need to do the following (I assume you are using the standard cocos2d templates where you have the RootViewController):
* Make sure your your RootViewController can become first responder of your view by adding this in RootViewController.m:
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
Now add the methods you want your RootViewController to react to - in this case the "motion ended" method:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if ( event.subtype == UIEventSubtypeMotionShake ) {
CCLOG(@"shake it baby from the root view controller");
// send a message to the notification service where components can listen to
[[NSNotificationCenter defaultCenter] postNotificationName:@"MyiPhoneShakeEvent" object:nil];
}
if ( [super respondsToSelector:@selector(motionEnded:withEvent:)] ){
[super motionEnded:motion withEvent:event];
}
}
You can now try and run this on the simulator. If everything works as expected you should see a logging line printed in the console when you use the shake command in the simulator.
The next thing is to forward the event somewhere so that your sprites, layers or other controllers can react on it.
For that I have added the line
[NSNotificationCenter defaultCenter] postNotificationName:@"MyiPhoneShakeEvent" object:nil];
This should be self-explanatory. The idea is make a loose coupling by using the observer pattern.
Now you only need to register your objects that should act on shake to the notification center.
In my case I have a CCLayer subclass that handles all displayed sprites.
In its init method I added the line:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(resetGame)
name:@"MyiPhoneShakeEvent" object:nil];
This tells the notification center to call it (addObserver:self) by calling the method "resetGame" when the "MyiPhoneShakeEvent" event occurs.
resetGame looks like this:
- (void) resetGame {
// do something usefull
}
There is only one thing left you need to do - unregister the object from the notification center when its dealocation method gets called:
- (void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
//[...]
}
That's it. May seem confusing on the first sight, but I think it is quite straight forward.
Hope it helps. :-)