Unity3d GameCenter Leaderboard Tutorial

Hi, after pulling my hair out for a few days, I finally figured it out. These instructions will also work for the Storekit as well. For the StoreKit, you would just use storekit delegates like the leaderboard delegate.

Get the example native code sample from the resources here and do the following. This isn’t the most elegant code, but it does the job. One thing I did notice is, the leaderboard will not work when debugging. But works just fine when launched on its own.

Add the GameKit framework.

The next part is the key to adding subviews.

In AppController.mm

controller.view = view;
//[_window addSubview:view];
//CUSTOM VIEW CONTROLLER UIViewController
UIViewController *vc = [[UIViewController alloc] init]; 
[vc.view addSubview:view]; 
[vc.view setTag:999];
[_window addSubview:vc.view];

Next I made a class to deal with game center functions, in this case I’m only working with the leaderboard.

gameCenter.h

#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>


@interface gameCenter : NSObject <GKLeaderboardViewControllerDelegate>{
@private
    
}

- (void) reportScore: (int64_t) score forCategory: (NSString*) category;
- (void)postScore:(int64_t)score forCategory:(NSString *)category;
- (void) authenticateLocalPlayer;
- (BOOL)isGameCenterAvailable;
- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)vc;
- (void) retrieveScoresForPlayers:(NSArray *)players category:(NSString *)category range:(NSRange)range playerscope:(GKLeaderboardPlayerScope)playerscope timescope:(GKLeaderboardTimeScope)timescope;
- (void) leaderboardSelected: (id)sender : (UIViewController*)v;


@end

gameCenter.mm

#import "gameCenter.h"


@implementation gameCenter

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }
    
    return self;
}

- (void)dealloc
{
    [super dealloc];
}

- (void) reportScore: (int64_t) score forCategory: (NSString*) category 

{
    
    
    GKScore *scoreReporter = [[[GKScore alloc] initWithCategory:category] autorelease]; 
    
    scoreReporter.value = score;
    
    [scoreReporter reportScoreWithCompletionHandler: ^(NSError *error) 
     
     {
         if (error != nil)
         {
             // handle the reporting error
             //NSLog([error description]);
             
         }
         
         //[self callDelegateOnMainThread: @selector(scoreReported:) withArg: NULL error: error];
         
     }];
    
}

- (void)postScore:(int64_t)score forCategory:(NSString *)category 
{
    
    GKLeaderboard *query = [[GKLeaderboard alloc]init];
    query.category = category;
    
    if (query != nil)
        
    {
        
        [query loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
            
            if (error != nil){
                
                // Handle the error.
                //NSLog([error description]);
                
            }
            if (scores != nil){
                
                // Process the score.
                
                int64_t newScore = query.localPlayerScore.value + score;
                
                [self reportScore:newScore forCategory:category];
                
                
            }
            
            
            
        }];
        
        
    }
}

- (void) authenticateLocalPlayer
{
    
    if([self isGameCenterAvailable])
    {
        GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
        [localPlayer authenticateWithCompletionHandler:^(NSError *error)
         {
             
             if (localPlayer.isAuthenticated){
                 // Perform additional tasks for the authenticated player.
                 NSLog(@"PLAYER AUTHENICATED");
             }else{
                 NSLog(@"PLAYER NOT AUTHENICATED");
             }
             
             
             
         }];
        
        
    }
}


- (BOOL)isGameCenterAvailable
{
    // Check for presence of GKLocalPlayer class.
    BOOL localPlayerClassAvailable = (NSClassFromString(@"GKLocalPlayer")) != nil;
    
    // The device must be running iOS 4.1 or later.
    NSString *reqSysVer = @"4.1";
    NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
    BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
    return (localPlayerClassAvailable  osVersionSupported);
}

- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)vc
{
    [vc dismissModalViewControllerAnimated:YES];
    [vc.view.superview removeFromSuperview];
    [vc release];
}



//- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
//return (interfaceOrientation == UIInterfaceOrientationPortrait);
//}
- (void) retrieveScoresForPlayers:(NSArray *)players category:(NSString *)category range:(NSRange)range playerscope:(GKLeaderboardPlayerScope)playerscope timescope:(GKLeaderboardTimeScope)timescope
{
    GKLeaderboard* leaderboard = nil;
    if ([players count] > 0)
    {
        leaderboard = [[[GKLeaderboard alloc] initWithPlayerIDs:players] autorelease];
    }
    else 
    {
        leaderboard = [[[GKLeaderboard alloc] init] autorelease];
        leaderboard.playerScope = playerscope;
    }
    
    NSLog(@"retrieve scores");
    
    if (leaderboard !=nil)
    {
        leaderboard.timeScope = timescope;
        leaderboard.category = category;
        leaderboard.range = range;
        
        NSLog(@"leaderboard");
        
        [leaderboard loadScoresWithCompletionHandler:^(NSArray* scores, NSError* error)
         {
             //lasterror = error;
             if (error == nil)
                 NSLog(@"leaderboard present!");
             //[self showLeaderboard];
             
         }];
    }
    
}



- (void) leaderboardSelected: (id)sender : (UIViewController*)v
{
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;
    UIWindow* window = [UIApplication sharedApplication].keyWindow;
    GKLeaderboardViewController *leaderboardController = [[GKLeaderboardViewController alloc] init];   
    if (leaderboardController != nil) {
        leaderboardController.leaderboardDelegate = self;
        
        UIViewController *glView2 = [[UIViewController alloc] init];
        [window addSubview: glView2.view];
        [glView2 presentModalViewController: leaderboardController animated: YES];
        
        leaderboardController.view.transform = CGAffineTransformMakeRotation(0.0f);
        [leaderboardController.view setCenter:CGPointMake(screenSize.width/2, screenSize.height/2)];
        leaderboardController.view.bounds = CGRectMake(0, 0, screenSize.width, screenSize.height);
    }
 

}

@end

BonjourClientImpl.h

#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>
#import "gameCenter.h"
//#import "RootViewController.h"

@interface NetServiceBrowserDelegate : NSObject <GKLeaderboardViewControllerDelegate>
{
    // Keeps track of available services
    NSMutableArray *services;
	
	// Keeps track of search status
	NSString* status; 
    BOOL searching;
    gameCenter *gamecenter;
    
}



// NSNetServiceBrowser delegate methods for service browsing
+ (void)auth;
- (id)init;
- (void)dealloc;

@end

BonjourClientImpl.mm

#import "BonjourClientImpl.h"
#import "AppController.h"



@implementation NetServiceBrowserDelegate



- (id)init
{
    self = [super init];
    services = [[NSMutableArray alloc] init];
    searching = NO;
	status = @"Initializing";
    
    gamecenter = [[gameCenter alloc] init];
    
    return self;
}



- (void)dealloc
{
    [services release];
    
    [super dealloc];	
}

+ (void)auth
{
 
}



@end

//static NetServiceBrowserDelegate* delegateObject = nil;
//static NSNetServiceBrowser *serviceBrowser = nil;
gameCenter *gamecenter = [[gameCenter alloc] init];

void authenticate(){
    NSLog(@"AUTHING");
    [gamecenter authenticateLocalPlayer];
    [gamecenter leaderboardSelected:nil :nil];
}

void deallocGC(){
    [gamecenter release];
    gamecenter = nil;
}

// Converts C style string to NSString
NSString* CreateNSString (const char* string)
{
	if (string)
		return [NSString stringWithUTF8String: string];
	else
		return [NSString stringWithUTF8String: ""];
}

// Helper method to create C string copy
char* MakeStringCopy (const char* string)
{
	if (string == NULL)
		return NULL;
	
	char* res = (char*)malloc(strlen(string) + 1);
	strcpy(res, string);
	return res;
}

// When native code plugin is implemented in .mm / .cpp file, then functions
// should be surrounded with extern "C" block to conform C function naming rules
extern "C" {

	void _output ()
	{
		//NSLog(@"%d DEBUGGED STRING",[NetServiceBrowserDelegate getCount]);
         
    }
    
    void _authplayer()
    {
        [NetServiceBrowserDelegate auth];
        NSLog(@"AUTH PLAYER");
    }
    
    void _leaderboard()
    {
        authenticate();
        //[NetServiceBrowserDelegate auth];
        //[NetServiceBrowserDelegate leaderboardSelected:nil];
        NSLog(@"LEADER BOARD");
    }

	
}

Add this script to your Unity project, and attach to your main camera

SomeScript.cs

using UnityEngine;
using System.Runtime.InteropServices;

class SomeScript : MonoBehaviour {
   // This tells unity to look up the function FooPluginFunction
   // inside the static binary
   Rect windowRect = new Rect(20, 20, 120, 50);
	
   [DllImport ("__Internal")]
   private static extern void _output ();
	
   [DllImport ("__Internal")]
   private static extern void _authplayer ();
	
   [DllImport ("__Internal")]
   private static extern void _leaderboard ();

   void Awake () {
      //// Calls the native FooPluginFunction
      //// And prints 5 to the console
      //print (getOutput ());
   }
	


	void OnGUI()
	{
		// Register the window. Notice the 3rd parameter 
		windowRect = GUI.Window(0, windowRect, DoMyWindow, "My Window");
	}

	// Make the contents of the window
	void DoMyWindow(int windowID)
	{
		if(GUI.Button(new Rect(10,20,100,20), "Hello World")){
			Debug.Log("Got a click");
			getOutput ();
		}
	}
	
	// Stops lookup current lookup
	public static void getOutput()
	{
		// Call plugin only when running on real device
		if (Application.platform == RuntimePlatform.IPhonePlayer){
			_output();
			//_authplayer();
			_leaderboard();
		}
	}
	

}

Hi,

I want to implement the leaderboard in unity. As per your post,i am not able to figure out where to include the .mm and .h files in unity project?

Thanx

The .mm and .h files go on the Xcode project after you build it from Unity (File > Build settings and then click button Build). Only the C# script (SomeScript.cs) goes on the Unity project, attached to your main camera.

I need one simple Leaderboard in the Game Center and this small project just appeared to do the trick. Any expert coder willing to investigate the usability of these scripts with Unity 4.x and the latest Xcode? Thanks in advance.

wow, thanks fawzma for this, i ll test this but i m still a noob
i ll add you in the credit once my game is released :slight_smile: