iOS开发之GameCenter使用

Game Center Configuration Guide for iTunes Connect

iOS游戏开发之Game Center实战

iOS开发长文--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总

请看以上文章的GameCenter部分

Game Center是由苹果发布的在线多人游戏社交网络,通过它游戏玩家可以邀请好友进行多人游戏,它也会记录玩家的成绩并在排行榜中展示,同时玩家每经过一定的阶段会获得不同的成就。这里就简单介绍一下如何在自己的应用中集成Game Center服务来让用户获得积分、成就以及查看游戏排行和已获得成就。

准备工作:

①首先要创建一个Explicit App ID,不能创建Wildcard App ID,然后默认就勾选了GameCenter这个功能
②然后创建描述文件
③然后在ITC中创建刚刚那个BundleID的应用,这个应用可以不用提交
④创建沙盒测试用户,这个应该是iOS9.0还是iOS10.0之前的,现在是不需要添加沙盒测试用户了。如果要看测试数据,还需要到手机设置==>GameCenter==>sandbox

 

iOS开发之GameCenter使用_第1张图片

Snip20180312_22.png

⑤在ITC应用的Features里面找到GameCenter,然后配置GameCenter

 

iOS开发之GameCenter使用_第2张图片

Snip20180312_16.png

以下是排行榜的配置:其中重要的是排行榜ID,项目中要配置

 

iOS开发之GameCenter使用_第3张图片

Snip20180312_19.png

 

iOS开发之GameCenter使用_第4张图片

Snip20180312_20.png

⑥接下来就是工程中的配置了,第一步先打开Capability这个功能

 

iOS开发之GameCenter使用_第5张图片

Snip20180312_24.png

 

⑦接下来就是添加代码了:在需要的位置导入:

#import 
 
//验证授权
-(void)authPlayer{
    GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
    
    localPlayer.authenticateHandler = ^(UIViewController * __nullable viewController, NSError * __nullable error){
        if ([[GKLocalPlayer localPlayer] isAuthenticated]) {
            NSLog(@"%@",@"已经授权!");
        }else if(viewController){
            [self presentViewController:viewController animated:YES completion:nil];
        }else{
            if (!error) {
                NSLog(@"%@",@"授权OK");
            } else {
                NSLog(@"没有授权");
                NSLog(@"AuthPlayer error :%@",error);
            }
        }
    };
}
// 上传分数给 gameCenter
-(void)saveHighScore{
    if ([GKLocalPlayer localPlayer].isAuthenticated) {
        //得到分数的报告
        GKScore *scoreReporter = [[GKScore alloc] initWithLeaderboardIdentifier:@"你的排行榜ID,ITC中找"];
        scoreReporter.value = 1000;
        NSArray *scoreArray = @[scoreReporter];
        //上传分数
        [GKScore reportScores:scoreArray withCompletionHandler:nil];
    }
}
//下载 game center 某一排行榜中的分数及排名情况
- (void)downLoadGameCenter{
    if ([GKLocalPlayer localPlayer].isAuthenticated == NO) {
        NSLog(@"没有授权,无法获取更多信息");
        return;
    }
    GKLeaderboard *leaderboadRequest = [GKLeaderboard new];
    //设置好友的范围
    leaderboadRequest.playerScope = GKLeaderboardPlayerScopeGlobal;
    //指定那个区域的排行榜
    NSString *type = @"today";
    if ([type isEqualToString:type]) {
        leaderboadRequest.timeScope = GKLeaderboardTimeScopeToday;
        
    }else if([type isEqualToString:@"week"]){
        leaderboadRequest.timeScope = GKLeaderboardTimeScopeWeek;
        
    }else if([type isEqualToString:@"all"]){
        leaderboadRequest.timeScope = GKLeaderboardTimeScopeAllTime;
        
    }
    //哪一个排行榜
    NSString *ID = @"你的排行榜ID,ITC中找";
    leaderboadRequest.identifier = ID;
    //从那个排名到那个排名
    NSInteger location = 1;
    NSInteger length = 10;
    leaderboadRequest.range = NSMakeRange(location, length);
    //请求数据
    [leaderboadRequest loadScoresWithCompletionHandler:^(NSArray * _Nullable scores, NSError * _Nullable error) {
        if (error) {
            NSLog(@"请求分数失败");
            NSLog(@"error = %@",error);
        }else{
            NSLog(@"请求分数成功");
            //定义一个可变字符串存放用户信息
            NSMutableString *userInfo = [NSMutableString string];
            NSString *rankBoardID = nil;
            for (GKScore *score in scores) {
                NSLog(@"");
                //得到排行榜的 id
                NSString *gamecenterID = score.leaderboardIdentifier;
                NSString *playerName = score.player.displayName;
                NSInteger scroeNumb = score.value;
                NSInteger rank = score.rank;
                NSLog(@"排行榜 = %@,玩家名字 = %@,玩家分数 = %zd,玩家排名 = %zd",gamecenterID,playerName,scroeNumb,rank);
                [userInfo appendString:[NSString stringWithFormat:@"玩家名字 = %@,玩家分数 = %zd,玩家排名 = %zd",playerName,scroeNumb,rank]];
                [userInfo appendString:@"\n"];
                rankBoardID = gamecenterID;
            }
            //弹框展示
            [self popShowViewWithTitileName:[NSString stringWithFormat:@"%@ 排行榜的信息",rankBoardID] andInfo:userInfo];
        }
    }];
}
- (void)getAllOnlineFriends {
    if ([GKLocalPlayer localPlayer].isAuthenticated == NO) {
        NSLog(@"没有授权,无法获取好友信息");
        return;
    }
    [[GKLocalPlayer localPlayer] loadFriendPlayersWithCompletionHandler:^(NSArray * _Nullable friendPlayers, NSError * _Nullable error) {
        //定义一个可变字符串存放用户信息
        NSMutableString *userInfo = [NSMutableString string];
        
        for (GKPlayer *player in friendPlayers) {
            NSString *name = player.displayName;
            NSString *al = player.alias;
            //NSString *ID = player.guestIdentifier;
            NSString *ID = @"";
            [userInfo appendString:[NSString stringWithFormat:@"好友名字 = %@,nickName = %@%@",name,al,ID]];
            [userInfo appendString:@"\n"];
        }
        [self popShowViewWithTitileName:@"好友信息" andInfo:userInfo];
    }];
}
//遵循代理
@interface ViewController ()

//显示排行榜 可以跳转到自定的 game 排行榜 和 跳转到那个时间段
- (void)gameCenter{
    if ([GKLocalPlayer localPlayer].isAuthenticated == NO) {
        NSLog(@"没有授权,无法获取展示中心");
        return;
    }
    UIViewController *vc = [self.view.window rootViewController];
    GKGameCenterViewController *GCVC = [GKGameCenterViewController new];
    //跳转指定的排行榜中
    [GCVC setLeaderboardIdentifier:@"你的排行榜ID"];
    //跳转到那个时间段
    NSString *type = @"all";
    if ([type isEqualToString:@"today"]) {
        [GCVC setLeaderboardTimeScope:GKLeaderboardTimeScopeToday];
    }else if([type isEqualToString:@"week"]){
        [GCVC setLeaderboardTimeScope:GKLeaderboardTimeScopeWeek];
    }else if ([type isEqualToString:@"all"]){
        [GCVC setLeaderboardTimeScope:GKLeaderboardTimeScopeAllTime];
    }
    GCVC.gameCenterDelegate = self;
    [vc presentViewController:GCVC animated:YES completion:nil];
}

//实现代理:
#pragma mark -  GKGameCenterControllerDelegate
- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController{
    [gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
}
// 上传成就
- (void)uploadAchievment {
    if ([GKLocalPlayer localPlayer].isAuthenticated == NO) {
        NSLog(@"没有授权,上传不了成就");
        return;
    }
    GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:@"challenge1"];
    [achievement setPercentComplete:10];
    [GKAchievement reportAchievements:@[achievement] withCompletionHandler:^(NSError * _Nullable error) {
        if (error) {
            NSLog(@"%@",error);
        }else{
            NSLog(@"上传成就成功");
        }
    }];
}
//下载 game center 中的所有排行榜的榜单内容,但是只是显示了每一个排行榜的内容,可以拼接下做一个TableView来显示
- (void)downloadGameCenter {
    if ([GKLocalPlayer localPlayer].isAuthenticated == NO) {
        NSLog(@"没有授权,无法获取更多信息");
        return;
    }
    [GKLeaderboard loadLeaderboardsWithCompletionHandler:^(NSArray * _Nullable leaderboards, NSError * _Nullable error) {
        //定义可变字符串
        NSMutableString *leadBoardInfo = [NSMutableString string];
        for (GKLeaderboard *lb in leaderboards) {
            NSString *ID = lb.identifier;
            NSString *leadBoardTittle = lb.title;
            
            [lb loadScoresWithCompletionHandler:^(NSArray *scores, NSError *error) {
                NSMutableString *stringM = [NSMutableString string];
                if (!error) {
                    for (NSObject *score in scores) {
                        GKScore *newScore = (GKScore *)score;
                        
                        GKPlayer *player = newScore.player;
                        NSString *playerID = player.playerID;
                        NSString *alias = player.alias;
                        NSString *displayName = player.displayName;
                        NSString *guestId = player.guestIdentifier;
                        
                        NSInteger rank = newScore.rank;
                        
                        NSDate *date = newScore.date;
                        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
                        [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss zzz"];
                        NSString *currentDateString = [dateFormatter stringFromDate:date];
                        
                        NSString *leaderboardID = newScore.leaderboardIdentifier;
                        NSInteger value = newScore.value;
                        NSInteger formattedValue = newScore.formattedValue;
            
                        NSLog(@"score = %@",score);
                        [stringM appendFormat:@"\nLeaderboardID:%@, PlayerID:%@,  NickName:%@, DisplayName:%@, GuestID:%@, Rank:%zd, Value:%zd, Time:%@",leaderboardID,playerID,alias,displayName,guestId,rank,value,currentDateString];
                    }
                }
                [self popShowViewWithTitileName:leadBoardTittle andInfo:stringM];
                                }];
            [leadBoardInfo appendString:[NSString stringWithFormat:@"排行榜id = %@,排行榜标题 = %@",ID,leadBoardTittle]];
            [leadBoardInfo appendString:@"\n"];
        }
        //[self popShowViewWithTitileName:@"所有的排行榜" andInfo:leadBoardInfo];
    }];
}
//Private method 弹框
-(void)popShowViewWithTitileName:(NSString *)tittleName andInfo:(NSString*)info{
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:tittleName message:info preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
    [self presentViewController:alert animated:true completion:nil];
}

所需参数

①具备GameCenter功能的描述文件
②成就ID和排行榜ID

注意问题

①看下手机的版本,如果是路径:设置==>GameCenter==>Sandbox能找到的话那么我们就需要开启这个Sandbox,如果路径没有表示的是新系统的,就不需要了。
②我们在测试的功能的时候都需要先授权,但是授权一次就OK,然后才能测试出功能
③如果我们测试的时候用两台设备测试,但是我们查看排行榜的数据的时候只能看到自己设备的数据,那可能是由于数据还没有统计过来,需要一点时延,可能几个小时后数据就能同步了,放心妥妥的睡一觉,明天第二天再来测试就能显示两台设备跑的数据了...
④另外如果发布的时候是需要勾选GameCenter开关的,见下图:

 

iOS开发之GameCenter使用_第6张图片

Snip20180328_40.png

调试过程中报错解决:
①问题一
报错如下:

Error Domain=GKErrorDomain Code=15 "未能完成所请求的操作,因为 Game Center 未识别此应用程序。" 

问题解决:
①工程中开启Capability这个功能项
②对比下BundleId和ITC后台的BundleID是否一致
③再检查看itunesconnect里是不是没有添加过排行榜或者成就设置,必须要至少添加一条
④设置里面开启GameCenter.

②问题二:
报错如下:

 [Error] _authenticateUsingAlert:Faied to authenticate player with existing credentials.
Error: Error Domain=GKErrorDomain Code=6 "未能完成所请求的操作,因为本地玩家尚未通过认证。"
UserInfo={NSLocalizedDescription=未能完成所请求的操作,因为本地玩家尚未通过认证。

原因:这是由于GameCenter进入后台关闭导致的...
解决办法:再次进入后台,再进入前台,配合验证bolck中的代码如果viewController有值那么就展示此viewController就会出现系统的输入GameCenter id 的界面,下面有“注意注意”中有截图,所需配合的代码如下:

if(viewController){
    //这个是GameCenter没有开启的时候
    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:viewController animated:YES completion:nil];
}

问题参考:
Unity iOS Game Center帐号验证(包括python后端)
解决 Error Domain=GKErrorDomain Code=15 "无法完成所请求的操作,

文章参考:
iOS Game Center 登陆验证实现
IOS: How to authenticate the GKLocalPlayer on my 'third party server'.
服务器端验证 Apple Game Center GKLocalPlayer 签名(PHP描述)

iOS开发之GameCenter使用_第7张图片

蓝瘦.gif

以上!!!











注意注意:


GameCenter的验证流程:

①GameCenter通过以下方法验证登录:

[GKLocalPlayer localPlayer].authenticateHandler = ^(UIViewController * __nullable viewController, NSError * __nullable error){
//code here...
}

这个方法内部通过判断error和viewController来判定是否弹出登录框,如果viewController的值为nil,那么表明GameCenter的开关并没有关,是通过验证的,如下图:

 

iOS开发之GameCenter使用_第8张图片

Snip20180330_42.png

 

如果viewController不为nil,那么表明GameCenter的开关是关闭的,如下图:

 

iOS开发之GameCenter使用_第9张图片

Snip20180330_43.png


如果error为nil,并且[[GKLocalPlayer localPlayer] isAuthenticated]值为YES的话,一般就是验证成功的.
具体的验证方法如下:

        GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
        //NSLog(@"localPlayer %@",localPlayer);
        localPlayer.authenticateHandler = ^(UIViewController * __nullable viewController, NSError * __nullable error){
            if ([[GKLocalPlayer localPlayer] isAuthenticated]) {
                NSLog(@"%@",@"已经授权!");
                
                //这个是在本地服务器上验证登录信息的,具体参考本小节结束给出
                [[GKLocalPlayer localPlayer] generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error) {
                    if (error) {
                        NSLog(@"ERROR: %@",error);
                    }
                }];
                if (localPlayer.playerID && localPlayer.playerID.length) {
                    [KODGCPlayerInfo defaultUserInfo].playerID = [GKLocalPlayer localPlayer].playerID;
                    [KODGCPlayerInfo defaultUserInfo].isFirstAuthenticationFlag = NO;
                }
            }else if(viewController){
                //这个是GameCenter没有开启的时候
                [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:viewController animated:YES completion:nil];
            }else{
                if (!error) {
                    NSLog(@"%@",@"授权OK");
                } else {
                    NSLog(@"没有授权");
                    NSLog(@"AuthPlayer error :%@",error);
                }
            }
        };

②因为GKLocalPlayer是单例,并且苹果在内部把这个验证的block只传一次,但是会多次调用这个block信息,那么在何时会调用这个信息呢,请看以下说明:
1>程序一启动,只要点击登录按钮传入这个验证block,就会开始进行验证,然后如果验证成功,那么就会在屏幕上方弹出一个信息条,显示某某GameCenter账号已经登录了...
2>只要在程序中你再次点击这个登录按钮再次传入这个验证block是不会再进行验证的,因为你这个账号一直在使用没有问题...
3>但是如果我们让此程序进入后台再回到前台,那么传入的验证block将再次被调用。即每进入一次后台再回到前台就会调用一次验证block.
4>如果我们在进入后台的时候将GameCenter开关给关掉了,那么再回到前台的时候调用验证block,打印错误信息,但是此时的ViewController为nil,错误信息如下:

[Error] _authenticateUsingAlert:Faied to authenticate player with existing credentials.
Error: Error Domain=GKErrorDomain Code=6 "未能完成所请求的操作,因为本地玩家尚未通过认证。"
UserInfo={NSLocalizedDescription=未能完成所请求的操作,因为本地玩家尚未通过认证。

如果此时你再次进入后台,什么都不做,再次回到前台,那么会弹出GameCenter输入账号系统的验证,如下图:

 

iOS开发之GameCenter使用_第10张图片

Snip20180330_45.png

 

5>如果我们进入后台,直接将GameCenter关掉,然后再开启GameCenter再验证另外一个Apple ID,再次回到前台,此时会在界面上出现第二个账号登录的横幅弹框。

 

iOS开发之GameCenter使用_第11张图片

Snip20180330_46.png

对接游戏的登录系统

我们只需要每次调用验证block的时候,如果验证是成功的,并且localPlayer的playerID是有值的,那么我就记录这个localPlayer,然后保持最新的,如果用户点击登录,那么直接用记录的那个localPlayer去登录就行了,因为如果用户的行为是自己主动去后台切换了那个GameCenter的ID,那么意味着用户要切换GameCenter的ID从而来切换游戏里面的登录角色等等,那么此时用户再次进入游戏的时候,就应该主动去找到切换登录的按钮,再次点击登录即可,那么此时登录的账户就是最新的localPlayer了。

你可能感兴趣的:(iOS,XCode)