国内关于Cocos2dx中使用Game Center的教程太少了,而且都缺少很多关键步骤。
我现在使用我的切身经验来总结一下Game Center的使用技巧。
这篇文章中包含了Game Center中排行榜和成就的使用方法、一些关于OC和C++混编的问题,以及测试的技巧。
1.创建OC接口代码文件:NSCGameCenter.h和NSCGameCenter.mm,我们需要说明的是,这两个文件必须要使用Objective-C++ Source的文件类型,现在我把这两个文件的内容贴出来(内容比较多,所以折叠了):
这两个文件不要放到Cocos2dx的Resources文件夹中,因为不应该参与其它平台工程的变异。
这些代码来自于文章,并做了少许修改:http://cocos2d.9tech.cn/news/2013/1202/39024.html
NSCGameCenter.h:
#import <Foundation/Foundation.h> #import <GameKit/GameKit.h> @interface NSCGameCenter : NSObject < GKLeaderboardViewControllerDelegate, GKAchievementViewControllerDelegate, GKMatchmakerViewControllerDelegate, GKMatchDelegate> { BOOL gameCenterAvailable; BOOL userAuthenticated; } @property (assign, readonly) BOOL gameCenterAvailable; @property (nonatomic, copy) NSString* leaderboardName; //本地成就字典,用来保存已经下载下来的成就,以避免重复从gamecenter获取成就状态 @property (nonatomic, retain) NSMutableDictionary* achievementDictionary; + (NSCGameCenter *)sharedGameCenter; - (void) authenticateLocalUser; - (void) registerForAuthenticationNotification; - (void) showLeaderboard; - (void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController; //上传分数到Gamecenter - (void) reportScore:(int64_t)score forCategory:(NSString*)category; //从gamecenter下载前X个分数 - (void) retrieveTopXScores:(int)number; - (void) showAchievementboard; - (void) achievementViewControllerDidFinish:(GKAchievementViewController *)achievementController; - (void) loadAchievement; - (void) clearAchievements; - (void) reportAchievement:(NSString*)id percent:(float)percent; - (void) unlockAchievement:(GKAchievement*)achievement percent:(float)percent; - (GKAchievement*) getAchievementForID:(NSString*)id; @end
NSCGameCenter.mm:
#import "NSCGameCenter.h" @implementation NSCGameCenter @synthesize gameCenterAvailable; //静态初始化 对外接口 static NSCGameCenter *sharedHelper = nil; static UIViewController* currentModalViewController = nil; + (NSCGameCenter *) sharedGameCenter { if (!sharedHelper) { sharedHelper = [[NSCGameCenter alloc] init]; } return sharedHelper; } //用于验证 - (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); } - (id)init { if ((self = [super init])) { gameCenterAvailable = [self isGameCenterAvailable]; if (gameCenterAvailable) { NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(authenticationChanged) name:GKPlayerAuthenticationDidChangeNotificationName object:nil]; } } return self; } //后台回调登陆验证 - (void)authenticationChanged { if ([GKLocalPlayer localPlayer].isAuthenticated &&!userAuthenticated) { NSLog(@"Authentication changed: player authenticated."); userAuthenticated = TRUE; } else if (![GKLocalPlayer localPlayer].isAuthenticated && userAuthenticated) { NSLog(@"Authentication changed: player not authenticated"); userAuthenticated = FALSE; } } - (void)authenticateLocalUser { if (!gameCenterAvailable) return; NSLog(@"Authenticating local user..."); if ([GKLocalPlayer localPlayer].authenticated == NO) { GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer]; [localPlayer setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError *error) { if(localPlayer.isAuthenticated) { NSLog(@"LOGIN SUCCEED!"); }else{ NSLog(@"LOGIN FAILED!"); }})]; } else { NSLog(@"Already authenticated!"); } } - (void) registerForAuthenticationNotification { NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(authenticationChanged) name:GKPlayerAuthenticationDidChangeNotificationName object:nil]; } //显示排行榜 - (void) showLeaderboard { if (!gameCenterAvailable) return; GKLeaderboardViewController *leaderboardController = [[GKLeaderboardViewController alloc] init]; if (leaderboardController != nil) { leaderboardController.leaderboardDelegate = self; UIWindow *window = [[UIApplication sharedApplication] keyWindow]; currentModalViewController = [[UIViewController alloc] init]; [window addSubview:currentModalViewController.view]; [currentModalViewController presentModalViewController:leaderboardController animated:YES]; } } //关闭排行榜回调 - (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController{ if(currentModalViewController !=nil){ [currentModalViewController dismissModalViewControllerAnimated:NO]; [currentModalViewController release]; [currentModalViewController.view removeFromSuperview]; currentModalViewController = nil; } } //上传分数到排行榜 - (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) { NSLog(@"上传分数出错."); } else { NSLog(@"上传分数成功"); } }]; } //下载分数 - (void) retrieveTopXScores:(int)number { GKLeaderboard *leaderboardRequest = [[GKLeaderboard alloc] init]; if (leaderboardRequest != nil) { leaderboardRequest.playerScope = GKLeaderboardPlayerScopeGlobal; leaderboardRequest.timeScope = GKLeaderboardTimeScopeAllTime; leaderboardRequest.range = NSMakeRange(1,number); leaderboardRequest.category = _leaderboardName; [leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) { if (error != nil){ // handle the error. NSLog(@"下载失败"); } if (scores != nil){ // process the score information. NSLog(@"下载成功...."); //NSArray *tempScore = [NSArray arrayWithArray:leaderboardRequest.scores]; //for (GKScore *obj in tempScore) { // NSLog(@" playerID : %@",obj.playerID); // NSLog(@" category : %@",obj.category); // NSLog(@" date : %@",obj.date); // NSLog(@" formattedValue : %@",obj.formattedValue); // NSLog(@" value : %d",obj.value); // NSLog(@" rank : %d",obj.rank); // NSLog(@"**************************************"); //} } }]; } } //汇报成就 //通过ID汇报成就 - (void) reportAchievement:(NSString*)id percent:(float)percent { if ( !gameCenterAvailable ) { NSLog(@"ERROR: GameCenter is not available currently"); return; } GKAchievement* achievement = [[[GKAchievement alloc] initWithIdentifier:id] autorelease]; [self unlockAchievement:achievement percent:percent]; } //通过成就指针汇报成就 - (void) unlockAchievement:(GKAchievement *)achievement percent:(float)percent { if ( achievement != nil ) { achievement.percentComplete = percent; achievement.showsCompletionBanner = YES; [achievement reportAchievementWithCompletionHandler:^(NSError* error){ if ( error != nil) { NSLog(@"上传成就错误,错误提示为\n%@", error); } else { NSLog(@"上传成就成功\n"); [self displayAchievement:achievement]; } }]; } } //显示成就 - (void) showAchievementboard { if (!gameCenterAvailable) return; GKAchievementViewController *achievementController = [[GKAchievementViewController alloc] init]; if (achievementController != nil) { achievementController.achievementDelegate = self; UIWindow *window = [[UIApplication sharedApplication] keyWindow]; currentModalViewController = [[UIViewController alloc] init]; [window addSubview:currentModalViewController.view]; [currentModalViewController presentModalViewController:achievementController animated:YES]; } } //关闭成就回调 - (void)achievementViewControllerDidFinish:(GKAchievementViewController *)viewController{ if(currentModalViewController !=nil){ [currentModalViewController dismissModalViewControllerAnimated:NO]; [currentModalViewController release]; [currentModalViewController.view removeFromSuperview]; currentModalViewController = nil; } } //打印某个成就 - (void) displayAchievement:(GKAchievement*)achievement { if ( achievement == nil) return; NSLog(@"completed:%d", achievement.completed); NSLog(@"lastReportDate:%@", achievement.lastReportedDate); NSLog(@"percentComplete:%f", achievement.percentComplete); NSLog(@"identifier:%@", achievement.identifier); } //加载所有成就。经测试,只能获取到已经上传过的成就状态。 - (void) loadAchievement { if ( self.achievementDictionary == nil ) { self.achievementDictionary = [[NSMutableDictionary alloc] init]; } [GKAchievement loadAchievementsWithCompletionHandler:^(NSArray* achievements, NSError* error){ if ( error == nil && achievements != nil ) { NSArray* tempArray = [NSArray arrayWithArray:achievements]; for(GKAchievement* tempAchievement in tempArray) { [self.achievementDictionary setObject:tempAchievement forKey:tempAchievement.identifier]; [self displayAchievement:tempAchievement]; } } }]; } //通过成就id生成成就。首先从本地成就字典查询,查询不到则生成一个。 - (GKAchievement*) getAchievementForID: (NSString*) id { if ( self.achievementDictionary == nil ) { self.achievementDictionary = [[NSMutableDictionary alloc] init]; } GKAchievement *achievement = [self.achievementDictionary objectForKey:id]; if (achievement == nil) { achievement = [[[GKAchievement alloc] initWithIdentifier:id] autorelease]; [self.achievementDictionary setObject:achievement forKey:achievement.identifier]; } [self displayAchievement:achievement]; return [[achievement retain] autorelease]; } //初始化所有成就状态。经测试,这段代码暂时无效。 - (void) clearAchievements { NSEnumerator* enumerator = [self.achievementDictionary objectEnumerator]; for(NSObject* obj in enumerator) { [self unlockAchievement:(GKAchievement*)obj percent:0.0]; } } @end
2.在AppController.mm中添加引用,并使用当前用户登陆到Game Center:
在文件开头加入以下代码:
#import "NSCGameCenter.h"
在didFinishLaunchingWithOptions中添加以下代码以登陆:
[[NSCGameCenter sharedGameCenter] authenticateLocalUser];
注意:需要特别说明的是,NSCGameCenter.h的引用,千万不能添加到头文件"*.h"中,这样会出现很多的错误,必须要添加到".mm"文件中,并且但凡要使用NSCGameCenter.h的文件都必须是Objective-C++ Source类型的,因此下面使用提交分数等操作的C++文件也必须改为Objective-C++ Source类型的文件。
3.在Cocosdx中的类文件中添加引用,并使用Game Center的相关功能
在这这一步骤中首先要做的事是修改文件类型,见上方的“注意”。
首先我们在类文件例如"sceneStarter.mm"(注意不是"sceneStarter.h")中添加:
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "NSCGameCenter.h" #endif
此时,我们将会发现工程中出现了许多错误,例如"Reference to 'Size' is ambiguous."或者"Reference to 'Rect' is ambiguous."等等,这是因为Cocos2dx中定义的类型与Foundation中定义的类型名称相同而发生冲突,因此我们需要为这些类型加上命名空间:
//Size类型名发生冲突: Size screenSize; //Reference to 'Size' is ambiguous. //应该为其添加命名空间: cocos2d::Size screenSize;
到这一步,错误都调试OK了,可以开始使用Game Center提供的功能了,我们再次仅以Leaderboard为例:
(1)打开排行榜界面:
[[NSCGameCenter sharedGameCenter] showLeaderboard];
(2)提交一个分数:
这段代码的参数需要解释的是,reportScore代表分数值,forCategory代表排行榜名称。
[[NSCGameCenter sharedGameCenter] reportScore:1000 forCategory:@"Global_Learderboard"];
代码是如此的简单,谢谢苹果带我装逼带我飞。
4.到此,代码的测试就完成了。但是我们还需要真机调试,我们需要在iPhone或iPad上进行一些简单地设置:
首先,打开"设置"->"Game Center",将"沙盒"开关打开,并重新输入密码登陆即可开始沙盒测试。
但是需要注意,仅有以"Signed Distribution Build"方式编译的App需要进行上述操作(参考Game Center Programming Guide),如果不进行该操作,将会出现以下错误:
Game Center unavailable(player is not signed in).