Detect shake event

This topic contains 4 replies, has 3 voices, and was last updated by  fidel_karsto 3 years ago.

Viewing 5 posts - 1 through 5 (of 5 total)
Author Posts
Author Posts
December 29, 2010 at 6:01 pm #227207

Unforgotten
@unforgotten

I am a beginner in Cocos2d and iPhone programming. The game that I am creating needs to detect shake gesture. I have been searching information on the Internet and I found this site that adds shake recognition. After I tried to implement these files into the cocos2d template, it did not manage to detect any shake. The link is http://www.softvelopment.com/index.php/blogs/2010/03/19/3-adding-shake-recongnition-to-cocos2d-iphone-library-

AppDelegate.h

#import <UIKit/UIKit.h>
#import "ShakeEnabledUIWindow.h"

@class RootViewController;

@interface shakingAppDelegate : NSObject <UIApplicationDelegate> {
ShakeEnabledUIWindow *window;
RootViewController *viewController;
}

@property (nonatomic, retain) ShakeEnabledUIWindow *window;

@end

HelloWorldScene

-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init] )) {

// create and initialize a Label
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];

// ask director the the window size
CGSize size = [[CCDirector sharedDirector] winSize];

// position the label on the center of the screen
label.position = ccp( size.width /2 , size.height/2 );

// add the label as a child to this Layer
[self addChild: label];
ShakeDispatcher * dispatcher = [ShakeDispatcher sharedInstance];
[dispatcher addShakeListener:self];

}
return self;
}

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if ( event.subtype == UIEventSubtypeMotionShake )
{
NSLog(@"Shake detected");
}
}

Is there any other way to detect shake gesture? Your help is greatly appreciated!

December 29, 2010 at 9:08 pm #308873

itlgames
Moderator
@itlgames

Have you tried this on the device or the simulator? I noticed the simulator has an option “Shake Gesture” which doesn’t really work for me. I implemented my own shake gesture pretty easy just detecting a fast and big increase on the accelerometer values, works a treat on the device, but not on the simulator.

December 30, 2010 at 5:09 am #308874

Unforgotten
@unforgotten

I do not have a device to test the application. Has anyone managed to use the iphone simulator to detect shake gesture?

December 30, 2010 at 5:24 pm #308875

itlgames
Moderator
@itlgames

You need a device to test the accelerometer, as far as I know, there are some code to allow you use a device accelerometer on your simulator, but again you need a device. Besides, if you are planning to develop iPhone apps, you better off getting a real device, as the behavior is different in some aspects, like performance and memory.

April 1, 2011 at 5:15 pm #308876

fidel_karsto
Participant
@fidel_karsto

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. :-)

Viewing 5 posts - 1 through 5 (of 5 total)

You must be logged in to reply to this topic.