Skip to content
July 15, 2010 / Tobias Hieta

Cocos2D and orientation madness

Our first product, OHM Chess HD, uses Cocos2D. Cocos2D is a very nice library/framework for creating 2D games for the iPhone/iPad platforms, modelled after the Cocos2D framework for making indie PC games.

The library is great, but since it was originally written for the older iPhone platforms before the iPad existed, there are some issues with using it on the iPad. Most significantly, we fought very hard to get the orientation to be displayed correctly when holding the iPad different ways. On the older iPhone platform, applications are assumed to be running in portrait mode with the home button at the bottom of the screen, and an app can optionally choose to run in the landscape orientation. On the iPad, however, all applications should preferrably be able to run in any of the four orientations. As we drew nearer to release and hadn’t managed to get the orientation situation sorted, we decided to settle for running in either of the landscape modes, with a not-so-graceful flip of the screen when detecting that the device had been rotated. Now with the initial release out of the way, we decided to revisit the orientation issue and also make sure that we could display UIKit windows much better for our next version.

So what is the problem?
Cocos2d internally handles orientation by transforming the whole GLScene with glRotate, so when you call:

[[CCDirector sharedDirector] setOrientation:kCDOrientationPortraitLeft];

it will for each update do:

glTranslatef(width,height,0);
glRotatef(90,0,0,1);
glTranslatef(-height,-width,0);

This is all fine, because the normal routine for a cocos2d program is to attach it’s OpenGL view directly to the window, the listen to notifications about device rotation, when a notification about rotation would arrive you would just call setOrientation again and cocos2d would flip the view again. There are however problems with this approach: first of all it happens without transitions (take up your iPad flip any program you have and you’ll see that it will actually rotate) and if you want to mix this with UIKit you can have big problems.

UIKit, Cocos2D and orientation
In OHM Chess HD we need to show different UIView’s (for example the About dialog is a UIWebView, which seems silly to implement any other way) on top of the cocos2d view. That lead to some really screwed up code for placing it in the OpenGL scene and make sure that the orientation stayed correct and we concluded that to work properly all UIView’s really want a UIViewController at the root. So we added a UIViewController as a root object to the Cocos2d view thinking that there should not have been any problem, the following screenshot is from that attempt:

Wow, that wasn’t at all what we wanted. so what happened? It took us unnamed number of hours to figure out that when UIViewController receives the rotation notifications it actually flips the view for us by calling:

self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(-90));

which is almost the same as the glRotate from above. Effectively this double flipped the view, first once internally in cocos2d and then in UIViewController. Weexperimented with this quite a bit and it was really frustrating, because when we got our scene in the correct orientation all UIKit windows where turned the wrong way.

A solution
The solution we opted for was to try to remove the code in cocos2d for flipping the scene. After reading the code we suspected that the whole scene was flipped in [CCDirector applyOrientation], so I removed that code. At first it seemed to have worked, our view was in the correct orientation and things didn’t crash, but on closer examination we realized that we was not able to press any elements in the scene. It turned out that there where additional places that used CCDirectory.deviceOrientation to switch around the coordinate system. After some code removal we got it to work! Another positive side effect of this is that our scene now get’s animated when you switch orientation. The code is now cleaned up as a boolean option to CCDirector called disableInternalRotation, when you set this on your director it will avoid flipping the scene. We have pushed the code to our cocos2d repository at github, in the norotation branch.

How to use this
If you want to use this in your tree you need to first checkout our norotation branch of cocos2d and then you need to create a UIViewController with the following code:

- (void)loadView {
	self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
	[self setupScene];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    /* OHM Chess HD only supports landscape mode, if you want to support more orientations
      * to return YES for them here and also add some more code below */
    return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}

/* if you want to support portrait orientation, you need to add this as well

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
	if (toInterfaceOrientation == UIInterfaceOrientationIsPortrait || toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
	{
		[[CCDirector sharedDirector] setOrientation:kCCDeviceOrientationPortrait];
	}
}
*/

- (void)viewDidLoad
{
	[[CCDirector sharedDirector] runWithScene:[SMenuScene node]];
}

- (void) setupScene
{
	if( ! [CCDirector setDirectorType:CCDirectorTypeDisplayLink] )
		[CCDirector setDirectorType:CCDirectorTypeDefault];

	[CCTexture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA8888];
	
        /* our new option that disable the code in cocos2d for internal rotation */
	[[CCDirector sharedDirector] setDisableInternalRotation:YES];
        /* we still need to set it to a landscape orientation in order to have the right internal layout */
	[[CCDirector sharedDirector] setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
	[[CCDirector sharedDirector] setAnimationInterval:1.0/30.0];
	[[CCDirector sharedDirector] setDisplayFPS:NO];

        /* note that I create the EAGLView with 1024,768 size, don't use self.view.frame, because that is in 768,1024 resolution */
	_glView = [EAGLView viewWithFrame:CGRectMake(0, 0, 1024, 768)
						  pixelFormat:kEAGLColorFormatRGBA8
						  depthFormat:GL_DEPTH_COMPONENT24_OES
				                  preserveBackbuffer:NO];
	
	[[CCDirector sharedDirector] setOpenGLView:_glView];
	[self.view addSubview:_glView];
}

A word of warning: OHM Chess HD is not using the portrait orientations, so this is very untested. If you encounter any bugs while in portrait mode (or any mode, for that matter), please let me know.

Advertisements

7 Comments

Leave a Comment
  1. Ricardo Quesada / Jul 15 2010 21:07

    Hi,

    I saw your comment on issue #13 (http://code.google.com/p/cocos2d-iphone/issues/detail?id=13)

    I think that v0.99.4 already fixes this issue by recalculating the viewport and screen size when the EAGLView is resized.

    v0.99.4 includes a test case: tests/bugs/Bug914.
    If that test doesn’t work for you, could you open a new issue on the cocos2d issue tracker ? thanks.

    • Tobias Hieta / Jul 15 2010 22:08

      Hi Ricardo,

      Great news if this is already fixed! I will try it out this weekend and file the bug report if it doesn’t work and also write a follow-up guide here on the blog if I get it to work. Thanks for your comment!

  2. CJ / Jul 16 2010 13:31

    This should work for iPad and sgx chip devices but doesn’t address the case of mbx hardware that will actually take a performance hit by allowing rotation on the eaglview/layer. What I do is essentially a combination of uiviewcontroller plus OpenGL matrix rotation so that after the uiview has rotated I reset it’s transformation manually to unrotate it and then apply a rotation in the OpenGL transformation matrix. This was the end result of a rewrite of ccdirector and eaglview so I cannot provide a patch for the latest cocos2d codebase 😦

    • Tobias Hieta / Jul 16 2010 17:55

      Ah yes, that should be noted as well, that my patches are only tested on the iPad. I will probably do a follow-up post when I have tested what riq suggested. Is your code available somewhere or is it tied into non-open code?

      • CJ Hanson / Jul 16 2010 20:45

        @Tobias I’m working on putting the code on github, but this is probably about a month out. The delay is because I basically removed the CCDirector from my game engine that was based on cocos2d and wrote my own UIView -> openGL bridge code which handles rotation properly using a UIViewController.

  3. Ricardo Quesada / Jul 16 2010 22:21

    @CJ Do you remember how fast/slow was your game when the EAGLView was controlled by a UIViewController compared to when it wasn’t using a UIViewController ?

Trackbacks

  1. How to handle a cocos2d scene in a UIViewController and not get screwed « Tobias weblog

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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: