1. first of all, you should edit leader board and achievement in the iTunes Connect.It's very easy.
2. second add gamecentermanager.(I attached a zip file in the attachment,include four files which you need to add into your games.)
gamecentermanager.h
#import <Foundation/Foundation.h>
@class GKLeaderboard, GKAchievement, GKPlayer;
@protocol GameCenterManagerDelegate <NSObject>
@optional
- (void) processGameCenterAuth: (NSError*) error;
- (void) scoreReported: (NSError*) error;
- (void) reloadScoresComplete: (GKLeaderboard*) leaderBoard error: (NSError*) error;
- (void) achievementSubmitted: (GKAchievement*) ach error:(NSError*) error;
- (void) achievementResetResult: (NSError*) error;
- (void) mappedPlayerIDToPlayer: (GKPlayer*) player error: (NSError*) error;
@end
@interface GameCenterManager : NSObject
{
NSMutableDictionary* earnedAchievementCache;
id <GameCenterManagerDelegate, NSObject> delegate;
}
//This property must be attomic to ensure that the cache is always in a viable state...
@property (retain) NSMutableDictionary* earnedAchievementCache;
@property (nonatomic, assign) id <GameCenterManagerDelegate> delegate;
+ (BOOL) isGameCenterAvailable;
- (void) authenticateLocalUser;
- (void) reportScore: (int64_t) score forCategory: (NSString*) category;
- (void) reloadHighScoresForCategory: (NSString*) category;
- (void) submitAchievement: (NSString*) identifier percentComplete: (double) percentComplete;
- (void) resetAchievements;
- (void) mapPlayerIDtoPlayer: (NSString*) playerID;
@end
gamecentermanager.m
#import "GameCenterManager.h"
#import <GameKit/GameKit.h>
@implementation GameCenterManager
@synthesize earnedAchievementCache;
@synthesize delegate;
- (id) init
{
self = [super init];
if(self!= NULL)
{
earnedAchievementCache= NULL;
}
return self;
}
- (void) dealloc
{
self.earnedAchievementCache= NULL;
[super dealloc];
}
// NOTE: GameCenter does not guarantee that callback blocks will be execute on the main thread.
// As such, your application needs to be very careful in how it handles references to view
// controllers. If a view controller is referenced in a block that executes on a secondary queue,
// that view controller may be released (and dealloc'd) outside the main queue. This is true
// even if the actual block is scheduled on the main thread. In concrete terms, this code
// snippet is not safe, even though viewController is dispatching to the main queue:
//
// [object doSomethingWithCallback: ^()
// {
// dispatch_async(dispatch_get_main_queue(), ^(void)
// {
// [viewController doSomething];
// });
// }];
//
// UIKit view controllers should only be accessed on the main thread, so the snippet above may
// lead to subtle and hard to trace bugs. Many solutions to this problem exist. In this sample,
// I'm bottlenecking everything through "callDelegateOnMainThread" which calls "callDelegate".
// Because "callDelegate" is the only method to access the delegate, I can ensure that delegate
// is not visible in any of my block callbacks.
- (void) callDelegate: (SEL) selector withArg: (id) arg error: (NSError*) err
{
assert([NSThread isMainThread]);
if([delegate respondsToSelector: selector])
{
if(arg != NULL)
{
[delegate performSelector: selector withObject: arg withObject: err];
}
else
{
[delegate performSelector: selector withObject: err];
}
}
else
{
NSLog(@"Missed Method");
}
}
- (void) callDelegateOnMainThread: (SEL) selector withArg: (id) arg error: (NSError*) err
{
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self callDelegate: selector withArg: arg error: err];
});
}
+ (BOOL) isGameCenterAvailable
{
// check for presence of GKLocalPlayer API
Class gcClass = (NSClassFromString(@"GKLocalPlayer"));
// check if the device is running iOS 4.1 or later
NSString *reqSysVer = @"4.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
return (gcClass && osVersionSupported);
}
- (void) authenticateLocalUser
{
if([GKLocalPlayer localPlayer].authenticated == NO)
{
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error)
{
[self callDelegateOnMainThread: @selector(processGameCenterAuth:) withArg: NULL error: error];
}];
}
}
- (void) reloadHighScoresForCategory: (NSString*) category
{
GKLeaderboard* leaderBoard= [[[GKLeaderboard alloc] init] autorelease];
leaderBoard.category= category;
leaderBoard.timeScope= GKLeaderboardTimeScopeAllTime;
leaderBoard.range= NSMakeRange(1, 1);
[leaderBoard loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error)
{
[self callDelegateOnMainThread: @selector(reloadScoresComplete:error:) withArg: leaderBoard error: error];
}];
}
- (void) reportScore: (int64_t) score forCategory: (NSString*) category
{
GKScore *scoreReporter = [[[GKScore alloc] initWithCategory:category] autorelease];
scoreReporter.value = score;
[scoreReporter reportScoreWithCompletionHandler: ^(NSError *error)
{
[self callDelegateOnMainThread: @selector(scoreReported:) withArg: NULL error: error];
if (error!=nil)
{
}
}];
}
- (void) submitAchievement: (NSString*) identifier percentComplete: (double) percentComplete
{
//GameCenter check for duplicate achievements when the achievement is submitted, but if you only want to report
// new achievements to the user, then you need to check if it's been earned
// before you submit. Otherwise you'll end up with a race condition between loadAchievementsWithCompletionHandler
// and reportAchievementWithCompletionHandler. To avoid this, we fetch the current achievement list once,
// then cache it and keep it updated with any new achievements.
if(self.earnedAchievementCache == NULL)
{
[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error)
{
if(error == NULL)
{
NSMutableDictionary* tempCache= [NSMutableDictionary dictionaryWithCapacity: [scores count]];
for (GKAchievement* score in scores)
{
[tempCache setObject: score forKey: score.identifier];
}
self.earnedAchievementCache= tempCache;
[self submitAchievement: identifier percentComplete: percentComplete];
}
else
{
//Something broke loading the achievement list. Error out, and we'll try again the next time achievements submit.
[self callDelegateOnMainThread: @selector(achievementSubmitted:error:) withArg: NULL error: error];
}
}];
}
else
{
//Search the list for the ID we're using...
GKAchievement* achievement= [self.earnedAchievementCache objectForKey: identifier];
if(achievement != NULL)
{
if((achievement.percentComplete >= 100.0) || (achievement.percentComplete >= percentComplete))
{
//Achievement has already been earned so we're done.
achievement= NULL;
}
achievement.percentComplete= percentComplete;
}
else
{
achievement= [[[GKAchievement alloc] initWithIdentifier: identifier] autorelease];
achievement.percentComplete= percentComplete;
//Add achievement to achievement cache...
[self.earnedAchievementCache setObject: achievement forKey: achievement.identifier];
}
if(achievement!= NULL)
{
//Submit the Achievement...
[achievement reportAchievementWithCompletionHandler: ^(NSError *error)
{
[self callDelegateOnMainThread: @selector(achievementSubmitted:error:) withArg: achievement error: error];
}];
}
}
}
- (void) resetAchievements
{
self.earnedAchievementCache= NULL;
[GKAchievement resetAchievementsWithCompletionHandler: ^(NSError *error)
{
[self callDelegateOnMainThread: @selector(achievementResetResult:) withArg: NULL error: error];
}];
}
- (void) mapPlayerIDtoPlayer: (NSString*) playerID
{
[GKPlayer loadPlayersForIdentifiers: [NSArray arrayWithObject: playerID] withCompletionHandler:^(NSArray *playerArray, NSError *error)
{
GKPlayer* player= NULL;
for (GKPlayer* tempPlayer in playerArray)
{
if([tempPlayer.playerID isEqualToString: playerID])
{
player= tempPlayer;
break;
}
}
[self callDelegateOnMainThread: @selector(mappedPlayerIDToPlayer:error:) withArg: player error: error];
}];
}
@end
you need not change anything in the two files.just add them into your project.
3.(optional, you need do it if your game is landscape) add two files to control the orientation of leader board and achievement.
GKLeaderboardViewController-LandscapeOnly.h
#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>
@interface GKLeaderboardViewController(LandscapeOnly)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
@end
@interface GKAchievementViewController(LandscapeOnly)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
@end
GKLeaderboardViewController-LandscapeOnly.m
#import "GKLeaderboardViewController-LandscapeOnly.h"
@implementation GKLeaderboardViewController(LandscapeOnly)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
// Does it match my screenOrientation?
if ( UIInterfaceOrientationLandscapeRight == (UIDeviceOrientation)toInterfaceOrientation)
return YES;
return NO;
}
@end
@implementation GKAchievementViewController(LandscapeOnly)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
// Does it match my screenOrientation?
if ( UIInterfaceOrientationLandscapeRight == (UIDeviceOrientation)toInterfaceOrientation)
return YES;
return NO;
}
@end
4. add these code in the UntitledViewController.h
#import <GameKit/GameKit.h>
#import "GameCenterManager.h"
@class GameCenterManager;
add three Delegate to UntitledViewController definition , such as
@interface UntitledViewController : UIViewController<ADBannerViewDelegate, UIActionSheetDelegate, GKLeaderboardViewControllerDelegate,GKAchievementViewControllerDelegate, GameCenterManagerDelegate, SKProductsRequestDelegate>
then add a variable and two functions in the UntitletViewController, you should know a little grammar about objective-c,then you can add follow code correctly.
GameCenterManager* gameCenterManager;
}
@property (nonatomic, retain) GameCenterManager *gameCenterManager
- (void) showLeaderboard; // call it to show your leader board
- (void) showAchievements; // call it to show your achievements board.
that's all for UntitledViewController.h.
5. add these functions into the UntitledViewController.m file. It's very easy.
#pragma mark GameCenter View Controllers
- (void) showLeaderboard;
{
GKLeaderboardViewController *leaderboardController = [[GKLeaderboardViewController alloc] init];
if (leaderboardController != NULL)
{
leaderboardController.category = @"BallScore";
leaderboardController.timeScope = GKLeaderboardTimeScopeAllTime;
leaderboardController.leaderboardDelegate = self;
[self presentModalViewController: leaderboardController animated: NO];
leaderboardController.view.transform = CGAffineTransformMakeRotation(1.570796327f);
[leaderboardController.view setCenter:CGPointMake([[UIScreen mainScreen] bounds].size.width/2,
[[UIScreen mainScreen] bounds].size.height/2)];
}
}
- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
[self dismissModalViewControllerAnimated: YES];
[viewController release];
}
- (void) showAchievements
{
GKAchievementViewController *achievements = [[GKAchievementViewController alloc] init];
if (achievements != NULL)
{
achievements.achievementDelegate = self;
[self presentModalViewController: achievements animated: NO];
achievements.view.transform = CGAffineTransformMakeRotation(1.570796327f);
[achievements.view setCenter:CGPointMake([[UIScreen mainScreen] bounds].size.width/2,
[[UIScreen mainScreen] bounds].size.height/2)];
}
}
- (void)achievementViewControllerDidFinish:(GKAchievementViewController *)viewController;
{
[self dismissModalViewControllerAnimated: YES];
[viewController release];
}
- (IBAction) resetAchievements: (id) sender
{
[gameCenterManager resetAchievements];
}
you need not to change these except -(void) showLeaderboard. Pay attention to this line:
leaderboardController.category = @"BallScore"; you should change @"BallScore" to your own ID which you defined in the iTunes Connect.
6. be patient. almost come to an end. there is only two steps left. Check if the device os can support Game Center and authenticate local user by following code.It's very important.
// about score
if([GameCenterManager isGameCenterAvailable])
{
self.gameCenterManager= [[[GameCenterManager alloc] init] autorelease];
[self.gameCenterManager setDelegate: self];
[self.gameCenterManager authenticateLocalUser];
}
else
{
[self showAlertWithTitle: @"Game Center Support Required!"
message: @"The current device does not support Game Center, which this sample requires."];
}
you should add these code into - (void)viewDidLoad function. That's all for UntitledViewController.m .
7. the last one is very simple. You just need to know where to call the functions which you added just now.Before it , you should define a global variable for UntitledViewController. Then you can call the functions in your cpp file.
a. report a score to Game Center. remember to replace the category with your own.
[g_View.gameCenterManager reportScore:m_iScore forCategory:@"BallScore"];
b. submit a achievement to GameCenter. the first parameter is the category, and the second is the percent of finished.
[g_View.gameCenterManager submitAchievement: @"300points" percentComplete: 100];
c. call it to show leader board.
[g_View.gameCenterManager showLeaderBoard];
d. call it to show achievements
[g_View.gameCenterManager showAchievements];
That's all 7 steps. If you have any questions, you can post follow this thread. I'll answer as quickly as I can. For I'll know it immediately from my iPhone when there is a new post.
The Miracrea Games