Posted by: eygneph | 2010/05/03

Improvements in orientation notification handling of cocos2d iphone

I’ve been using cocos2d iphone in my iPhone/iPad projects for a while. It’s been proved to be an easy to use 2d engine and allow users to start programming gameplay immediately(almost!). Recently it added support for iPad. However, iPad game approval on Apple side seems to be more stricter than iPhone games, due to new items in iPad HIG, especially for orientation sensitive issues.

Due to the above reason, I plan to make my new game more orientation friendly. In cocos2d iphone, orientation changes can be achieved by registering UIDeviceOrientationDidChangeNotification event and call appropriate CCDirector methods:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
        // Enable orientation detection
	[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
	// Register orientation detection
	[[NSNotificationCenter defaultCenter]
	 addObserver:self selector:@selector(orientationDidChanged:) name:@"UIDeviceOrientationDidChangeNotification" object:nil];
-(void) orientationDidChanged:(NSNotification*)notification
	UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
	[[CCDirector sharedDirector] setDeviceOrientation:(ccDeviceOrientation)orientation];

However this orientation change occurs suddenly and have no transition animation . After some readings, I decided to roll my own. The orientation changes handler code does nothing more than calling appropriate OS functions and transforming the coordinates.
So here’s what I’m doing: make the gl* calls in CCDirector.applyLandscape as CGAffineTransform calls, which can be interpolated by elapsed time. Then convert CGAffineTransform matrix to GL matrix:

CGAffineTransform CGAffineTransformInterpolate(const CGAffineTransform *t0, const CGAffineTransform *t1, float factor)
	// clamp factor to [0, 1]
	if ( factor > 1 )
		factor = 1;
	if ( factor < 0 )
		factor = 0;

	return CGAffineTransformMake(t0->a*(1-factor) + t1->a*factor,
								 t0->b*(1-factor) + t1->b*factor,
								 t0->c*(1-factor) + t1->c*factor,
								 t0->d*(1-factor) + t1->d*factor,
								 t0->tx*(1-factor) + t1->tx*factor,
								 t0->ty*(1-factor) + t1->ty*factor);

// in your CCDirector.m:

- (void) setDeviceOrientation:(ccDeviceOrientation) orientation
	if( deviceOrientation_ != orientation ) {
		deviceOrientation_ = orientation;
		targetTransform_ = CGAffineTransformIdentity;
		elapsedSinceLastOrientationChange_ = 0;

		CGSize s = [openGLView_ frame].size;
		float w = s.width / 2;
		float h = s.height / 2;

		switch( deviceOrientation_) {
			case CCDeviceOrientationPortrait:
				[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:NO];
			case CCDeviceOrientationPortraitUpsideDown:
				[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortraitUpsideDown animated:NO];

				targetTransform_ = CGAffineTransformTranslate(targetTransform_, w, h);
				targetTransform_ = CGAffineTransformRotate(targetTransform_, CC_DEGREES_TO_RADIANS(180));
				targetTransform_ = CGAffineTransformTranslate(targetTransform_, -w, -h);
			case CCDeviceOrientationLandscapeLeft:
				[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO];

				targetTransform_ = CGAffineTransformTranslate(targetTransform_, w, h);
				targetTransform_ = CGAffineTransformRotate(targetTransform_, -CC_DEGREES_TO_RADIANS(90));
				targetTransform_ = CGAffineTransformTranslate(targetTransform_, -h, -w);
			case CCDeviceOrientationLandscapeRight:
				[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft animated:NO];

				targetTransform_ = CGAffineTransformTranslate(targetTransform_, w, h);
				targetTransform_ = CGAffineTransformRotate(targetTransform_, CC_DEGREES_TO_RADIANS(90));
				targetTransform_ = CGAffineTransformTranslate(targetTransform_, -h, -w);
				NSLog(@"Director: Unknown device orientation");

-(void) applyLandscape
	static float m[16];

	if ( elapsedSinceLastOrientationChange_ < 0.25f )
		currentTransform_ = CGAffineTransformInterpolate(&currentTransform_, &targetTransform_,
														 elapsedSinceLastOrientationChange_ / 0.25f);
		elapsedSinceLastOrientationChange_ += dt;
		currentTransform_ = targetTransform_;

	CGAffineToGL(&currentTransform_, m);

Now cocos2d can handle orientation with nice transition animation. I’ve uploaded the diff patch if you’re interested. It is based on cocos2d iphone 0.99.0.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: