美文网首页iOS技术专题
iOS开发之GameCenter使用

iOS开发之GameCenter使用

作者: KODIE | 来源:发表于2018-03-12 16:55 被阅读0次

    文章预读

    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


    Snip20180312_22.png

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


    Snip20180312_16.png

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


    Snip20180312_19.png
    Snip20180312_20.png

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


    Snip20180312_24.png

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

    #import <GameKit/GameKit.h>
    
    //验证授权
    -(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<GKScore*> *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<GKScore *> * _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<GKPlayer *> * _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 ()<GKGameCenterControllerDelegate>
    
    //显示排行榜 可以跳转到自定的 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<GKLeaderboard *> * _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开关的,见下图:


    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 &quot;无法完成所请求的操作,

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

    蓝瘦.gif

    以上!!!











    注意注意:


    GameCenter的验证流程:

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

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

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


    Snip20180330_42.png

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


    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输入账号系统的验证,如下图:


    Snip20180330_45.png

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


    Snip20180330_46.png

    对接游戏的登录系统

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


    相关文章

      网友评论

        本文标题:iOS开发之GameCenter使用

        本文链接:https://www.haomeiwen.com/subject/pimnfftx.html