How To Integrate Cocos2D and UIKit

Cocos2D is a great framework, but sometimes it’s handy to implement some of your game with UIKit. For example, it’s often useful to design your main menu, settings pages, and the like with UIKit and just use Cocos2D for your main game logic. You also might find it handy to use UIKit controls on top […] By Ray Wenderlich.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Connecting the Cocos2D View

The first thing we need to do is to make a few modifications to RootViewController, to re-add the code to set up Cocos2D that we removed from the AppDelegate.

Open up RootViewController.m and make the following modifications:

// Add to top of file
#import "HelloWorldLayer.h"

// Add these new methods
- (void)setupCocos2D {
    EAGLView *glView = [EAGLView viewWithFrame:self.view.bounds
        pixelFormat:kEAGLColorFormatRGB565	// kEAGLColorFormatRGBA8
        depthFormat:0                        // GL_DEPTH_COMPONENT16_OES
    ];
    glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self.view insertSubview:glView atIndex:0];
    [[CCDirector sharedDirector] setOpenGLView:glView];
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:1];
    [[CCDirector sharedDirector] runWithScene:scene];
}

- (void) viewWillAppear:(BOOL)animated
{
    [self.navigationController setNavigationBarHidden:YES animated:NO];
    [super viewWillAppear:animated];
}

- (void)viewDidLoad {
    [super viewDidLoad];    
    [self setupCocos2D];
}

// Add to end of viewDidUnload
[[CCDirector sharedDirector] end];

The code inside setupCocos2D is the code we removed from the AppDelegate. It creates an EAGLView, and adds it as a subview of the view controller’s view (at the bottom of the stack). It tells Cocos2D to use the new view, and tells it to run the HelloWorldLayer scene.

Note that since we’re calling this in viewDidLoad, we need to clean things up in viewDidUnload by calling [[CCDirector sharedDirector] end]. Remember views can get unloaded in low memory conditions when they are not visible.

The Cocos2D didn’t create a XIB for the RootViewController, but it is going to be convenient to have one, so let’s go ahead and get that set up too. Control-click on the MaskedCal group, click New File, select iOS\User Interface\Empty, and click Next. Select iPhone for Device Family, click Next, name the new file RootViewController.xib, and click Save.

This will be pretty simple to set up for now:

  • Select File’s Owner, and in the Identity Inspector set the class to RootViewController.
  • Then drag a View from the Object Library into the Objects list.
  • In the Attributes Inspector, set the Orientation to Landscape.
  • Finally, control-click on File’s Owner, and drag a line from the view outlet to the View you just added.

Connecting a view controller's root view in Interface Builder

OK – our RootViewController is fully set up, now we just need to make tapping the button display it!

Open MainMenuViewController.xib, and bring up the Assistant Editor. Make sure it’s set to Automatic, and select the View so MainMenuViewController.h shows up in the Assistant Editor.

Then, control-drag from the View button down below the @interface. Set the Connection to Action, set the name to viewTapped, and click Connect:

Connecting a button to an action in Interface Builder

Next we need to make an instance variable and property for the RootViewController. Open up MainMenuViewController.h and modify the file to look like the following:

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

@interface MainMenuViewController : UIViewController {
    RootViewController *_rootViewController;
}

@property (retain) RootViewController *rootViewController;

- (IBAction)viewTapped:(id)sender;

@end

Next open MainMenuViewController.m and make the following changes:

// Add to top of file
@synthesize rootViewController = _rootViewController;

// Add new methods
- (void)viewWallpapers:(id)arg {
    if (_rootViewController == nil) {
        self.rootViewController = [[[RootViewController alloc] initWithNibName:nil bundle:nil] autorelease];
    }
    [self.navigationController pushViewController:_rootViewController animated:YES];
}

- (IBAction)viewTapped:(id)sender {
    [self viewWallpapers:nil];
}

- (void)dealloc
{
    [_rootViewController release];
    _rootViewController = nil;
    [super dealloc];
}

Pretty simple – to display the RootViewController with the Cocos2D subview, we just need to create the view controller and push it onto the navigation controller stack!

Note that when we call initWithNibName, we can pass in nil and it will look for a nib with the same name as the view controller (i.e. RootViewController.xib).

Compile and run, and now you can tap the View button to load the Cocos2D scene!

Cocos2D scene displayed from another view controller!

Overlaying UIKit Views

So far so good, but there’s no way to get back to the main menu!

Let’s overlay a UIKit button on top of the screen that you can tap to go back. You can use this same technique for any other sorts of controls you might need – from UITextFields to UISliders or more.

Open up RootViewController.xib, and drag to Round Rect buttons into your view. Set them up with the following settings:

  • Button 1: X=0, Y=256, Width=64, Height=64, Custom, Image=Home.png
  • Button 2: X=396, Y=256, Width=64, Height=64, Custom, Image=Message.png

Also, click the main view and set the background color to black so the buttons show up more clearly in Interface Builder. At this point your screen shoud look like this:

Adding buttons to the root view controller

Next, control-drag from the Home button down inside your assistant editor’s RootViewController.h, right before the @end. Change the Connection to Action, the name to homeTapped, and click Connect.

Connecting an action for the button

Then switch to RootViewController.m and add the following inside the homeTapped method:

[self.navigationController popViewControllerAnimated:YES];

Compile and run, and you should be able to tap the home button to go back to the main menu!

Home button added to the app

Adding Gesture Recognizers

It is often useful to use gesture recognizers in your Cocos2D games, as they can often make things much easier than trying to detect the gestures yourself.

Let’s add the following gesture recognizers to this scene:

  • A tap gesture recognizer, to go to the next page (like it does now).
  • A swipe left gesture recognizer, to go to the next page.
  • A swipe right gesture recognizer, to go to the previous page.
  • A double tap gesture recognizer, which we’ll make hide the UIButton overlays later (but will just print a messge for now).

First, let’s remove the old touch handling code. Go to HelloWorldLayer.m, and delete the ccTouchBegan method, the registerWithTouchDispatcher method, and the line that says isTouchEnabled to YES.

Next switch to HelloWorldLayer.h and add some member variables and properties for the gesture recognizers:

// Add inside @interface
UITapGestureRecognizer * _tapRecognizer;
UITapGestureRecognizer * _doubleTapRecognizer;
UISwipeGestureRecognizer * _swipeLeftRecognizer;
UISwipeGestureRecognizer * _swipeRightRecognizer;

// Add after @interface
@property (retain) UITapGestureRecognizer * tapRecognizer;
@property (retain) UITapGestureRecognizer * doubleTapRecognizer;
@property (retain) UISwipeGestureRecognizer * swipeLeftRecognizer;
@property (retain) UISwipeGestureRecognizer * swipeRightRecognizer;

Next switch to HelloWorldLayer.m and make the following changes:

// Add after @implementation
@synthesize tapRecognizer = _tapRecognizer;
@synthesize doubleTapRecognizer = _doubleTapRecognizer;
@synthesize swipeLeftRecognizer = _swipeLeftRecognizer;
@synthesize swipeRightRecognizer = _swipeRightRecognizer;

// Then add these new methods
- (void)onEnter {
    self.doubleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)] autorelease];
    _doubleTapRecognizer.numberOfTapsRequired = 2;
    [[[CCDirector sharedDirector] openGLView] addGestureRecognizer:_doubleTapRecognizer];
    
    self.tapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)] autorelease];
    [_tapRecognizer requireGestureRecognizerToFail:_doubleTapRecognizer];
    [[[CCDirector sharedDirector] openGLView] addGestureRecognizer:_tapRecognizer];
    
    self.swipeLeftRecognizer = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleLeftSwipe:)] autorelease];
    _swipeLeftRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
    [[[CCDirector sharedDirector] openGLView] addGestureRecognizer:_swipeLeftRecognizer];    
    
    self.swipeRightRecognizer = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightSwipe:)] autorelease];
    _swipeRightRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
    [[[CCDirector sharedDirector] openGLView] addGestureRecognizer:_swipeRightRecognizer];    
}

- (void)onExit {
    [[[CCDirector sharedDirector] openGLView] removeGestureRecognizer:_tapRecognizer];
    [[[CCDirector sharedDirector] openGLView] removeGestureRecognizer:_doubleTapRecognizer];
    [[[CCDirector sharedDirector] openGLView] removeGestureRecognizer:_swipeLeftRecognizer];
    [[[CCDirector sharedDirector] openGLView] removeGestureRecognizer:_swipeRightRecognizer];
}

// Add to dealloc
[_tapRecognizer release];
_tapRecognizer = nil;
[_doubleTapRecognizer release];
_doubleTapRecognizer = nil;
[_swipeLeftRecognizer release];
_swipeLeftRecognizer = nil;
[_swipeRightRecognizer release];
_swipeRightRecognizer = nil;

onEnter is called when a scene is first displayed, and onExit is called when it disappears. In these we setup and tear down the gesture recognizers, respectively.

Notice that to create a gesture recognizer you use the following pattern:

  1. Create the gesture recognizer, specifying the target to receive the callback (this scene) and the callback method to be called (which we’ll write next).
  2. Specify any parameters you want on the gesture recognizers, like numberOfTapsRequired, or swipe direction.
  3. Add the gesture recognizer to the view – in this case the openGL view managed by CCDirector.

There’s also one interesting bit you might notice – when we set up the single tap gesture recognizer, we say that for it to count, the double tap gesture recognizer has to fail. If you didn’t have this, the single tap gesture recognizer would be called during the first tap of a double tap, which we don’t want for this app.

Next add the gesture recognizer callbacks, right before onEnter:

- (void)handleTap:(UITapGestureRecognizer *)tapRecognizer {
    CCLOG(@"Tap!");
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:calendarNum+1];
    [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInR transitionWithDuration:0.25 scene:scene]];
}

- (void)handleDoubleTap:(UITapGestureRecognizer *)doubletapRecognizer {
    CCLOG(@"Double Tap!");    
}

- (void)handleLeftSwipe:(UISwipeGestureRecognizer *)swipeRecognizer {
    CCLOG(@"Swipe Left!");
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:calendarNum+1];
    [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInR transitionWithDuration:0.25 scene:scene]];    
}

- (void)handleRightSwipe:(UISwipeGestureRecognizer *)swipeRecognizer {
    CCLOG(@"Swipe Right!");
    CCScene *scene = [HelloWorldLayer sceneWithCalendar:calendarNum-1];
    [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInL transitionWithDuration:0.25 scene:scene]];    
}

As you can see, these are pretty simple methods, and just transition between the scenes. Compile and run, and you should now be able to advance with taps or swipes!

Also, try double tapping and look in the log to verify that it detected the double tap. We’ll implement this next, to make it hide the HUD for a full-screen view of the wallpaper!

Contributors

Over 300 content creators. Join our team.