Jan 142013
 

I was working on a cocos2d based tapping game and I wasn’t able to run my game twice. It was clear that I wasn’t shutting the 1st game down correctly or building the 2nd game correctly, or both!

There are a lot of tutorials on the web out there teaching you how to add UIKit buttons to your Cocos2D app, or how to launch Cocos2D from your UIKit based app. But I needed to do both. I wanted a UIViewController under my game and UIKit widgets on top of my game. I spent a lot of time reading and this is what I came up with.

First, building the Xcode project was a nightmare. I eventually used the cocos2d/box2d template and then ripped out the files I didn’t needed, and added all my original files back in. The AppDelegate.m file looks just like a non-cocos2d app should look. This goes against the grain of many of the tutorials which advise you to build your cocos2d environment in the AppDelegate. I struggled with that, didn’t have luck for most of a Friday and then on Monday I put in a Cocos2DSingleton and it pretty much ran first time.

Here is my GameViewController’s viewDidLoad method:

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:NO];
    
    TTCocos2DSingleton *shared = [TTCocos2DSingleton sharedCocos2D];
    CCGLView *glView = [shared currentGLView];

    [self.view insertSubview:glView atIndex:1];
}

There are a view things to note. GameViewController has game UIButtons, score UILabels, and other game type of UI widgets. This lets me do a lot of the game controls in Interface Builder, not laying them out by hand. Notice I hide the status bar since the game is full-screen.

I get my cocos2d instance via the singleton, get its glView and insert this into the GameViewController’s view at index 1. This puts it below all the game controls. I’ll show you the sharedCocos2D method later, lets look at viewWillAppear.

- (void) viewWillAppear:(BOOL)animated
{
    if(![[CCDirector sharedDirector]  runningScene]){
        CCScene *scene = [MyGameLayer scene];
        
        myGame = [MyGameLayer node];
        myGame.delegate = self;
        
        [scene addChild: myGame];
        
        [[CCDirector sharedDirector] runWithScene:scene];
    } else {
        // we have a scene already, replace the original to get a new game
        [[CCDirector sharedDirector] startAnimation];
        
        CCScene *scene = [MyGameLayer scene];
        
        myGame = [MyGameLayer node];
        myGame.delegate = self;
        
        [scene addChild: myGame];
        
        [[CCDirector sharedDirector] replaceScene:scene];
    }
}

Notice how we treat the first run differently from the second run. For the second, and subsequent runs, we replace the scene with a new one. This avoids all “restarting” problems. Also notice that I set a delegate. I use a delegate protocol to communicate between my game layer and my UIViewController.

My singleton pattern comes from the Duck Rowing blog which I must admit is a pretty awesome name for a blog. I’m not going to show all the singleton code here, this blog is about cocos2d, but here is how I build my cocos2d environment.

+ (TTCocos2DSingleton *) sharedCocos2D;
{
    static dispatch_once_t onceQueue;
    
    dispatch_once(&onceQueue, ^{
        if (sharedInstance) {
            return;
        }
        sharedInstance = [[TTCocos2DSingleton alloc]init];
        // Create an CCGLView with a RGB565 color buffer, and a depth buffer of 0-bits
        sharedInstance->glView = [CCGLView viewWithFrame:[[UIScreen mainScreen] bounds]
                             pixelFormat:kEAGLColorFormatRGB565	//kEAGLColorFormatRGBA8
                             depthFormat:0	//GL_DEPTH_COMPONENT24_OES
                      preserveBackbuffer:NO
                              sharegroup:nil
                           multiSampling:NO
                         numberOfSamples:0];
        [sharedInstance->glView setMultipleTouchEnabled:YES];
        [sharedInstance setupDirector];
    });
    return sharedInstance;
    
}

The singleton sets up the CCGLView, enables multi-touch and then sets up the director. (I put that in another method since I thought, erroneously, that I’d need to call it elsewhere. Turns out I didn’t need to.)

- (void)setupDirector
{
    CCDirectorIOS *director = (CCDirectorIOS*) [CCDirector sharedDirector];
    
    [director setView:glView];
    [director enableRetinaDisplay:YES];
    director.wantsFullScreenLayout = YES;
    [director setDisplayStats:NO];
    [director setAnimationInterval:1.0/60];
}

And in setupDirector we set the usual suspects needed for a cocos2d app. Now the game can be played multiple times, I have a full UIViewController/UINavController underneath it, and I have UIKit widgets on top of my game. Nirvana.