Game Kit Game Center and cocos2d

With the upcoming release of iOS 4.1 and GameCenter next week, I decided to try and add the new functionality to StackEm. Most of the implementation went without a hitch but there were some places I ran into issues and I thought I would share my experiences so others don’t run into the same issues.

One of the first things you will run into is how to add a ViewController to cocos2d. I scoured the cocos2d forums and found that other people had run into the issue but the solution wasn’t clear to me. Then in an ‘Aha!’ moment, it all became clear. You need to create a ViewController (a nil one is fine) and add it’s view property to the cocos2d glview.

Here’s what I mean, the following code will show the matchmaker:

UIViewController *tempVC=[[UIViewController alloc] init] ;
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = 2;
request.maxPlayers = 2;
request.playersToInvite = playersToInvite;

GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
mmvc.matchmakerDelegate = self;
[[[CCDirector sharedDirector] openGLView] addSubview:tempVC.view];
[tempVC presentModalViewController:mmvc animated:YES];

Now you need to create the delegate for GKMatchmakerViewController and hide the modal when they click close:


- (void)matchmakerViewControllerWasCancelled:(GKMatchmakerViewController *)viewController
{
[tempVC dismissModalViewControllerAnimated:YES];
[tempVC.view removeFromSuperview];
[tempVC.view.superview removeFromSuperview];
}

You’ll want to release that UIViewController in your onExit function:

-(void) onExit{
[super onExit];
[tempVC release];
}

Edit: Thanks to Matt for finding how to fix the memory leak and multi-touch after dismissing game center.

38 Responses to “Game Kit Game Center and cocos2d”

  1. Ivan
    September 11, 2010 at 4:05 am #

    Hi!
    Thanks for the entry!
    I’m trying to do a very simple GameCenter integration with cocos2d (only leaderboards) and i can’t find a example.
    In my try i can see the leaderboards but when i close the GC window i don’t know how to return the control to cocos2d 🙁
    I have tried your line: “[tempVC.view removeFromSuperview];” but don’t work for me.
    http://www.cocos2d-iphone.org/forum/topic/9450
    Thanks for the help!

    • Shawn Grimes
      September 11, 2010 at 2:37 pm #

      Make sure you are setting the leaderboard delegate:

      In your .h file:

      @interface MenuScene : CCLayer

      In your .m file:

      - (void) showLeaderboard
      {
      GKLeaderboardViewController *leaderboardController = [[[GKLeaderboardViewController alloc] init] autorelease];
      if (leaderboardController != nil)
      {
      leaderboardController.leaderboardDelegate = self;
      [[[CCDirector sharedDirector] openGLView] addSubview:tempVC.view];
      [tempVC presentModalViewController:leaderboardController animated: YES];

      }
      }

      Then use the delegate method:

      - (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
      {
      [tempVC dismissModalViewControllerAnimated:YES];
      [tempVC.view removeFromSuperview];
      }

  2. John
    September 17, 2010 at 3:35 pm #

    Hi

    Thanks for addressing this issue, I have been pulling my hair out for two evenings now and still can’t get it to work! 🙁

    I get: *** Assertion failure in -[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:], /SourceCache/UIKit_Sim/UIKit-1262.60.3/UIWindowController.m:182

    I’ll keep plugging away at it!

    Here’s the code, maybe you have an idea why it blows up?


    @implementation Leaderboard

    +(id) scene
    {
    CCScene *scene = [Leaderboard node];
    Leaderboard *layer = [Leaderboard node];

    [scene addChild: layer];
    return scene;
    }

    -(id) init
    {
    if( ( self=[super init] ) )
    {
    tempVC=[[UIViewController alloc] init];
    [self schedule:@selector(showLeaderboard:) interval:2];
    }
    return self;
    }

    - (void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
    {
    [tempVC dismissModalViewControllerAnimated:YES];
    [tempVC.view removeFromSuperview];

    [[CCDirector sharedDirector] replaceScene:[CCFadeTransition transitionWithDuration:(ccTime)1.25f
    scene:[MainMenu scene]]];
    }

    - (void) showLeaderboard:(ccTime)dt
    {
    [self unschedule:@selector(showLeaderboard:)];

    GKLeaderboardViewController *leaderboardController = [[[GKLeaderboardViewController alloc] init] autorelease];
    if (leaderboardController != nil)
    {
    leaderboardController.leaderboardDelegate = self;
    [[[CCDirector sharedDirector] openGLView] addSubview:tempVC.view];
    [tempVC presentModalViewController:leaderboardController animated: YES];
    }
    }

    -(void)dealloc
    {
    [tempVC release];
    [super dealloc];
    }
    @end

  3. John
    September 18, 2010 at 1:31 am #

    Doh!

    This was a silly error on my part.


    +(id) scene
    {
    CCScene *scene = [CCScene node];
    Leaderboard *layer = [Leaderboard node];

    [scene addChild: layer];
    return scene;
    }

    I had instantiated *scene incorrectly!

    Thanks for the original post Shawn.

  4. John
    September 24, 2010 at 9:56 am #

    I am currently trying to implement this but am getting lots of memory leaks when the leaderboard shows. Is anybody else getting this?

    Regards:

    John

  5. zues
    October 5, 2010 at 4:41 am #

    I have do everything like you,but I got a problem, when I call the game center, It isn’t appear. Here is my code:

    #import
    #import
    #import “cocos2d.h”
    @interface LeaderBoard : CCLayer
    {
    UIViewController *tempVC;
    }
    +(id) scene;
    @end

    #import “LeaderBoard.h”
    #import “HelloWorldScene.h”

    @implementation LeaderBoard

    +(id) scene
    {
    CCScene *scene = [CCScene node];
    LeaderBoard *layer = [LeaderBoard node];

    [scene addChild: layer];
    return scene;
    }

    -(id) init
    {
    if( ( self=[super init] ) )
    {
    tempVC=[[UIViewController alloc] init];
    [self schedule:@selector(showLeaderboard:) interval:2];
    }
    return self;
    }

    – (void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
    {
    [tempVC dismissModalViewControllerAnimated:YES];
    [tempVC.view removeFromSuperview];

    [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:(ccTime)1.25f
    scene:[HelloWorld scene]]];
    }

    – (void) showLeaderboard:(ccTime)dt
    {
    [self unschedule:@selector(showLeaderboard:)];

    GKLeaderboardViewController *leaderboardController = [[[GKLeaderboardViewController alloc] init] autorelease];
    if (leaderboardController != nil)
    {
    leaderboardController.leaderboardDelegate = self;
    [[[CCDirector sharedDirector] openGLView] addSubview:tempVC.view];
    [tempVC presentModalViewController:leaderboardController animated: YES];
    }
    }

    -(void)dealloc
    {
    [tempVC release];
    [super dealloc];
    }
    @end

    -(void) goToGameCenter:(id) sender
    {
    [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:(ccTime)1.25f
    scene:[LeaderBoard scene]]];
    }
    I use a buttom to call the GameCenter in the mainscene.Is anything wrong?

    I’m waiting for you help, thanks very much!

  6. zues
    October 5, 2010 at 9:09 am #

    the 0.99.5 sdk of cocos2d have a RootViewController, someone say it’s only use for the game center,but I don’t know how to use it. I use as above, but don’t have memory leak. why the cocos2d don’t give a exmaple for the game center? I have looked the GKTapper for tow day,but get nothing to use.

  7. zues
    October 5, 2010 at 9:32 am #

    @implementation Leaderboard

    +(id) scene
    {
    CCScene *scene = [CCScene node];
    Leaderboard *layer = [Leaderboard node];

    [scene addChild: layer];
    return scene;
    }

    -(id) init
    {
    if( ( self=[super init] ) )
    {
    tempVC=[[UIViewController alloc] init];
    [self schedule:@selector(showLeaderboard:) interval:2];
    }
    return self;
    }

    – (void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
    {
    [tempVC dismissModalViewControllerAnimated:YES];
    [tempVC.view removeFromSuperview];

    [[CCDirector sharedDirector] replaceScene:[CCFadeTransition transitionWithDuration:(ccTime)1.25f
    scene:[MainMenu scene]]];
    }

    – (void) showLeaderboard:(ccTime)dt
    {
    [self unschedule:@selector(showLeaderboard:)];

    GKLeaderboardViewController *leaderboardController = [[[GKLeaderboardViewController alloc] init] autorelease];
    if (leaderboardController != nil)
    {
    leaderboardController.leaderboardDelegate = self;
    [[[CCDirector sharedDirector] openGLView] addSubview:tempVC.view];
    [tempVC presentModalViewController:leaderboardController animated: YES];
    }
    }

    -(void)dealloc
    {
    [tempVC release];
    [super dealloc];
    }
    @end

    I use a buttom to call the gamecenter,but it’s not appear.

  8. zues
    October 5, 2010 at 8:56 pm #

    here is the .h file

    #import
    #import
    #import “cocos2d.h”

    @interface LeaderBoard : CCLayer
    {
    UIViewController *tempVC;
    }

    +(id) scene;

    @end

    • zues
      October 5, 2010 at 8:59 pm #

      #import
      #import
      I also add the foundation.h and gameKit.h,and add the gamekit.framework to my project.

    • Shawn Grimes
      October 5, 2010 at 9:09 pm #

      Admittedly I am a relative new guy to cocoa dev but I believe you need to specify tempVC as a property in your .h file, and then synthesize it in your .m file and make reference to self.tempVC

      My guess is that your tempVC in – (void) showLeaderboard:(ccTime)dt is nil and that’s why you aren’t seeing anything. Add a breakpoint at “[[[CCDirector sharedDirector] openGLView] addSubview:tempVC.view];” and see what the value of tempVC is there.

  9. zues
    October 6, 2010 at 12:36 am #

    I have watch the tempVC,It’s not nil .tempVC=0x7bf0460, UIResponder = 0x7bf0460, view=0x7b016f0, _nibBundle = 0x7816c50.I just write a test code,Is it must have do some other thing that I can test?

    • Shawn Grimes
      October 6, 2010 at 10:55 am #

      Hmmm, your code actually worked fine for me with the exception of your scene function, there was Leaderboard instead of LeaderBoard (case difference), but I’m guessing since you got it to compile, you found that already. Do you want zip up your project and send it to me adn I can get a closer look at what is going on? If so my, email is just shawn at shawnsbits.com

  10. zues
    October 12, 2010 at 6:15 am #

    hi shawn,my project have sent to your email shawn@shawnsbits.com, thank you for your help.

  11. zues
    October 12, 2010 at 6:17 am #

    oh no, the zip file is too big that I can’t sent to your email, the zip file is about 10M, Are you have any other email?

  12. zues
    October 18, 2010 at 6:59 am #

    Hi,Shawn.my code have no error,but I get this:Detected an attempt to call a symbol in system libraries that is not present on the iPhone:
    pthread_mutexattr_destroy$UNIX2003 called from function _ZN4llvm3sys5MutexC2Eb in image libLLVMContainer.dylib.How I can do?Becuse this,Iphone show a black scene.I’m waiting for your help ,thanks.

  13. corgano
    October 29, 2010 at 11:15 pm #

    Do you experience memory leaks when the leaderboard is displayed, as well? I can’t figure out where I could’ve made a mistake, everything is released nicely and the leaks only occur in the GameKit, UIKit and QuartzCore Librarys. No “obvious” trace to one of my lines.

    • Shawn Grimes
      November 4, 2010 at 1:03 pm #

      Yes, I do experience the memory leak. Not sure exactly what causes it, maybe gamecenter itself. But I haven’t found a better way to do it yet.

  14. Matt
    November 18, 2010 at 6:19 am #

    Hi not sure if you still check this, but Im going crazy over trying to get this to work. Game is getting close to done too 🙁 There is so little information out there. After fighting with Cocos2d orientation for a week, I finally got the leaderboard to display correctly. However, when I dismiss it, I lose multi-touch on my game scene. I also leak memory on the creation and destruction of the board. Here is some code for reference.

    .h file
    #import
    #import “SongSelectScene.h”
    #import “SimpleAudioEngine.h”
    #import “RootViewController.h”

    @interface TitleScreenScene : CCLayer {
    CCSprite *background;;
    CCLabelBMFont *labelTitle; //Title of Game
    CCLabelBMFont *labelPlay; //Button to start game
    CCLabelBMFont *labelLeaderBoard; //Access the Game Center leaderboard
    UIViewController *tempVC;
    }
    +(id) scene;
    – (void) authenticateLocalPlayer;
    – (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController;
    @end

    .m file
    ////////
    #import “TitleScreenScene.h”
    @implementation TitleScreenScene
    +(id) scene{
    CCScene *scene = [CCScene node];
    TitleScreenScene *layer = [TitleScreenScene node];
    [scene addChild: layer];
    return scene;
    }
    -(id) init{
    if((self = [super init])) {
    self.isTouchEnabled = YES;
    CGSize winSize = [[CCDirector sharedDirector] winSize];

    background = [CCSprite spriteWithFile:@”titlescreen.png” rect:CGRectMake(0, 0, winSize.width, winSize.height)];
    background.anchorPoint = ccp(0,0);
    [self addChild:background];

    CCMenuItem *menuItemPlay = [CCMenuItemImage itemFromNormalImage:@”play.png” selectedImage:@”play.png” target:self selector:@selector(menuItemPlayClicked:)];
    menuItemPlay.position = ccp(30, winSize.height / 2 – 80);
    menuItemPlay.anchorPoint = ccp(0,0);

    CCMenuItem *menuItemLeaderBoard = [CCMenuItemImage itemFromNormalImage:@”cd.png” selectedImage:@”cd.png” target:self selector:@selector(menuItemLeaderBoardClicked:)];
    menuItemLeaderBoard.position = ccp(winSize.width – 5, winSize.height – 5);
    CCMenu *menu = [CCMenu menuWithItems:menuItemPlay, menuItemLeaderBoard, nil];
    menu.position = CGPointZero;
    [self addChild:menu];
    }
    return self;
    }
    – (void) menuItemPlayClicked:(id)sender{
    [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:0.7f scene:[SongSelectScene node]]];
    }
    – (void) menuItemOptionsClicked:(id)sender{
    [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:0.7f scene:[OptionScreenScene node]]];
    }
    – (void) authenticateLocalPlayer{
    [[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error)
    {
    if (error == nil) {
    NSLog(@”Game Center: Player Authenticated!”);
    }
    else{
    NSLog(@”Game Center: Authentication Failed!”);
    }
    }];
    }
    – (void) menuItemLeaderBoardClicked:(id)sender{
    [self authenticateLocalPlayer];
    tempVC = [[UIViewController alloc] init];

    GKLeaderboardViewController *leaderboardController = [[[GKLeaderboardViewController alloc] init] autorelease];

    if (leaderboardController != nil){
    leaderboardController.leaderboardDelegate = self;
    [[[CCDirector sharedDirector] openGLView] addSubview:self.tempVC.view];
    [tempVC presentModalViewController:leaderboardController animated:YES];
    }
    }

    – (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController*)viewController{
    [tempVC dismissModalViewControllerAnimated:YES];
    [tempVC.view removeFromSuperview];
    [tempVC release];
    [[CCDirector sharedDirector] replaceScene:[TitleScreenScene node]];
    }
    – (void) dealloc{
    [super dealloc];
    }
    @end
    ///END code

    • Shawn Grimes
      November 18, 2010 at 12:09 pm #

      Matt,
      I don’t think you need:
      [[CCDirector sharedDirector] replaceScene:[TitleScreenScene node]];

      in your – (void)leaderboardViewControllerDidFinish function.

      As far as the memory leak, I experience it too. I think (but am not positive) that it is an actual leak in GameKit.

      • Matt
        November 19, 2010 at 9:35 pm #

        Thanks for the reply. Tried removing the replace scene, but Im still losing my multi-touch functionality after dismissing the board. I have no idea whats going on.

        • Shawn Grimes
          November 19, 2010 at 10:06 pm #

          Want me to setup a drop box and you can upload a project and I’ll take a deeper look?

          • Matt
            November 19, 2010 at 10:21 pm #

            Sure that be really awesome of you! I can make a sample project for you demonstrating the issue. Thanks for the help!

          • Shawn Grimes
            November 21, 2010 at 4:36 pm #

            Ok, so you’ve actually found a reproducible bug but I’m not sure if it is in cocos2d or in the GameKit Leaderboard Controller.

            When I present a normal view controller and dismiss it, it still detects multi-touch. When you dismiss the GameKit Leaderboard, you only get 1.

            Results in multi-touch being detected after testModal is dismissed

            - (void) menuItemClicked:(id)sender
            {
            	tempVC = [[UIViewController alloc] init];
            	testModal = [[modalVC alloc] init];
            	testModal.modalPresentationStyle=UIModalPresentationFormSheet;
            	[[[CCDirector sharedDirector] openGLView] addSubview:tempVC.view];
            	[tempVC presentModalViewController:testModal animated:YES];
            }
            

            Only 1 touch is detected after dismiss

            
            - (void) menuItemClicked:(id)sender
            {
            	tempVC = [[UIViewController alloc] init];
                    GKLeaderboardViewController *leaderboardController = [[[GKLeaderboardViewController alloc] init] autorelease];
            	if (leaderboardController != nil)
            	{
            		leaderboardController.leaderboardDelegate = self;
            		[[[CCDirector sharedDirector] openGLView] addSubview:tempVC.view];
            		[tempVC presentModalViewController:leaderboardController animated:YES];
            	}
            }
            

            You might want to try with the cocos2d forums.

        • Mike
          November 25, 2010 at 3:11 pm #

          I think multitouch loss is due to the fact that the tempVC.view is still a subview of a [[[CCDirector sharedDirector] openGLView] and that view has no multitouch enabled.

          When [tempVC dismissModalViewControllerAnimated:YES] is called tempVC.view superview is a UITransitionView not openGLView.

          Solution would be to use dismissModalViewControllerAnimated:NO, or clean up the openGLView a bit later with:

          for (UIView *v in [[CCDirector sharedDirector] openGLView].subviews) {
          [v removeFromSuperview];
          }

  15. Matt
    November 21, 2010 at 7:49 pm #

    Thanks for looking into it, I really appreciate it. I did find out one interesting thing though. This seems to not be an issue on the Ipad. Im using the same code, but it keep my multi-touch functionality. No idea why. Ill post this on the cocos2d board and see what I get. I take it you arent using multi-touch in your app?

    • Shawn Grimes
      November 23, 2010 at 4:17 pm #

      no multitouch for me, my games have all been single tap so far. That’s interesting about the ipad. I would like to look into that more. I’m really thinking it’s something that Game Kit isn’t doing correctly when it closes the modal view controller but I’m not positive.

      • Matt
        November 26, 2010 at 9:35 am #

        FIXED!

        Thanks to some help from the people on the Cocos2D board.

        [tempVC dismissModalViewControllerAnimated:YES];
        [tempVC.view.superview removeFromSuperview];

        This also fixed my memory leak on dismissal. Also to be notes, since I upgrade to 4.2, I no longer get the memory leak when the board is created. Good news all around! Thanks for the in-depth help. Greatly appreciate it.

        • Ashu
          November 29, 2010 at 3:39 pm #

          Hello,

          I see great discussion going on here about game Center. I am trying to implement Game Center too and it’s really a pain because I have no idea about iPhone App programming, I am into games…..

          I would be highly thankful if someone can show me some small example code about how to implement Game Center.

          Thanks a lot.

  16. Ashwin
    November 30, 2010 at 3:08 am #

    Hello,

    I had integrated game center in my game but when leaderboardViewControllerDidFinish method is called my portrait orientation is converted into landscape. Can anyone give me solution for the same.
    Here is my code.

    – (void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
    {
    [tempVC dismissModalViewControllerAnimated:NO];
    [tempVC.view removeFromSuperview];
    [tempVC release];

    }

    Thanks in advance..

    • Shawn Grimes
      November 30, 2010 at 10:16 am #

      Are you using a viewcontroller to handle your rotation changes or the CCDirector?

      My first recommendation would be to try this and see what happens:

               [tempVC.view.superview removeFromSuperview];
      

      We found that was a fix for a number of things such as memory leaks and losing multitouch. May fix this issue too.

  17. shuwee
    November 30, 2010 at 1:57 pm #

    Well thanks for all the posts….I have implemented Game Center to some extent. Right now my code authenticates the user and let him see the Leader Board and all that however how can I add score and test it? Any ideas will be highly appreciated.

    I also get this warning: Class DestinationSelectionLayer does not implement the GKLeaderboardViewControllerDelegate protocol.

    I am doing it for the first time and have no idea about this warning!!

    Thanks.

  18. shuwee
    November 30, 2010 at 2:01 pm #

    I am sorry to post another post so soon however my Game Center screen was showing up perfectly on simulator however when I tried to port it into iPhone….I get this error and it doesn’t run..

    dyld: Symbol not found: _OBJC_CLASS_$_GKAchievement
    Referenced from: /var/mobile/Applications/E3EEAF42-45F9-453A-9492-15DDB45FDFA9/Mike.app/Mike
    Expected in: /System/Library/Frameworks/GameKit.framework/GameKit

    Any idea?

    Thanks.

    • Shawn Grimes
      November 30, 2010 at 2:13 pm #

      Sounds like you don’t have the GameKit library on your device. Make Clean then rebuild. Also make sure your device is running iOS 4+

  19. shuwee
    November 30, 2010 at 2:17 pm #

    Ya sorry for posting that error. Here’s the solution for anyone who might face this issue.

    In XCode go to Project->Edit Active Target. Click on General tab, and in Linked Libraries select Weak link for GameKit.framework.

    @Thanks Shawn….You and Ray are so helpful.

  20. Marcela
    December 15, 2010 at 8:33 am #

    Thank you so much!

    This helped a lot.

Leave a Reply