3

我正在尝试使用http://www.raywenderlich.com/3325/how-to-make-a-simple-multiplayer-game-with-game-center上的教程使用 cocos2d 为 IOS 开发实时多人游戏-tutorial-part-22

一切正常,包括自动匹配随机玩家,但邀请朋友不起作用,因为其他设备无法收到邀请。

当我点击邀请朋友按钮,然后使用标准游戏中心界面选择一个朋友时,它说等待(永远)并且没有任何反应。我的朋友无法收到来自游戏中心的邀请(没有通知)。

我可以使用附近的朋友功能(在两个设备上都启用此功能时)邀请朋友,但禁用附近的朋友时没有邀请通知。

我花了几个小时在谷歌上搜索,找到了类似的案例,但没有解决方案。

关于可能答案的一些早期反馈:

  • 我使用两台设备(一台 iPhone 和一台 iPad),没有模拟器
  • iTunes Connect 上的所有设置都很好,包括多人游戏设置
  • 我验证了两个设备都使用不同的测试帐户连接到沙盒
  • 我已经检查了两台设备上游戏中心的通知设置
  • 我已经检查了所有代理/防火墙问题,并为这两种设备尝试了 WiFi 和蜂窝网络
  • 两个设备/帐户都启用了游戏邀请
  • 我已经检查了捆绑包 ID、应用程序版本 ID 等...
  • 两个设备都是IOS 6.x和App target version os IOS 5.0
  • 我没有关于游戏中心的其他问题(排行榜、随机配对等......一切都很好)
  • 如 Apple 文档中所述,我尽可能在对用户进行身份验证后立即调用inviteHandler 方法。

这是我的游戏中心助手类头文件:

#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>
@protocol GCHelperDelegate
- (void)matchStarted;
- (void)matchEnded;
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data
fromPlayer:(NSString *)playerID;
- (void)inviteReceived;
@end

@interface GCHelper : NSObject <GKMatchmakerViewControllerDelegate, GKMatchDelegate>{
BOOL gameCenterAvailable;
BOOL userAuthenticated;

UIViewController *presentingViewController;
GKMatch *match;
BOOL matchStarted;
id <GCHelperDelegate> delegate;

NSMutableDictionary *playersDict;

GKInvite *pendingInvite;
NSArray *pendingPlayersToInvite;
NSMutableArray *unsentScores;
}

@property (retain) GKInvite *pendingInvite;
@property (retain) NSArray *pendingPlayersToInvite;

@property (assign, readonly) BOOL gameCenterAvailable;

@property (retain) NSMutableDictionary *playersDict;

@property (retain) UIViewController *presentingViewController;
@property (retain) GKMatch *match;
@property (assign) id <GCHelperDelegate> delegate;

- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers
             viewController:(UIViewController *)viewController
                   delegate:(id<GCHelperDelegate>)theDelegate;

- (BOOL) reportAchievementIdentifier: (NSString*) identifier percentComplete: (float) percent;

+ (GCHelper *)sharedInstance;
- (void)authenticateLocalUser;

@end

这是游戏中心助手类的实现

#import "GCHelper.h"

@implementation GCHelper

@synthesize gameCenterAvailable;

@synthesize presentingViewController;
@synthesize match;
@synthesize delegate;
@synthesize playersDict;
@synthesize pendingInvite;
@synthesize pendingPlayersToInvite;

#pragma mark Initialization

static GCHelper *sharedHelper = nil;
+ (GCHelper *) sharedInstance {
    if (!sharedHelper) {
        sharedHelper = [[GCHelper 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;

        [GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite) {

            NSLog(@"Received invite");
            self.pendingInvite = acceptedInvite;
            self.pendingPlayersToInvite = playersToInvite;
            [delegate inviteReceived];

        };

    } else if (![GKLocalPlayer localPlayer].isAuthenticated && userAuthenticated) {
        NSLog(@"Authentication changed: player not authenticated");
        userAuthenticated = FALSE;
    }

}
- (void)lookupPlayers {

    NSLog(@"Looking up %d players...", match.playerIDs.count);
    [GKPlayer loadPlayersForIdentifiers:match.playerIDs withCompletionHandler:^(NSArray *players, NSError *error) {

        if (error != nil) {
            NSLog(@"Error retrieving player info: %@", error.localizedDescription);
            matchStarted = NO;
            [delegate matchEnded];
        } else {

            // Populate players dict
            self.playersDict = [NSMutableDictionary dictionaryWithCapacity:players.count];
            for (GKPlayer *player in players) {
                NSLog(@"Found player: %@", player.alias);
                [playersDict setObject:player forKey:player.playerID];
            }

            // Notify delegate match can begin
            matchStarted = YES;
            [delegate matchStarted];

        }
    }];

}

#pragma mark User functions

- (void)authenticateLocalUser {

    if (!gameCenterAvailable) return;

    NSLog(@"Authenticating local user...");
    if ([GKLocalPlayer localPlayer].authenticated == NO) {
        [[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:nil];
    } else {
        NSLog(@"Already authenticated!");
    }
}

- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController delegate:(id<GCHelperDelegate>)theDelegate {

    if (!gameCenterAvailable) return;

    matchStarted = NO;
    self.match = nil;
    self.presentingViewController = viewController;
    delegate = theDelegate;

    if (pendingInvite != nil) {

        [presentingViewController dismissModalViewControllerAnimated:NO];
        GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:pendingInvite] autorelease];
        mmvc.matchmakerDelegate = self;
        [presentingViewController presentModalViewController:mmvc animated:YES];

        self.pendingInvite = nil;
        self.pendingPlayersToInvite = nil;

    } else {

        [presentingViewController dismissModalViewControllerAnimated:NO];
        GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
        request.minPlayers = minPlayers;
        request.maxPlayers = maxPlayers;
        request.playersToInvite = pendingPlayersToInvite;

        GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
        mmvc.matchmakerDelegate = self;

        [presentingViewController presentModalViewController:mmvc animated:YES];

        self.pendingInvite = nil;
        self.pendingPlayersToInvite = nil;

    }

}

#pragma mark GKMatchmakerViewControllerDelegate

// The user has cancelled matchmaking
- (void)matchmakerViewControllerWasCancelled:(GKMatchmakerViewController *)viewController {
    [presentingViewController dismissModalViewControllerAnimated:YES];
}

// Matchmaking has failed with an error
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFailWithError:(NSError *)error {
    [presentingViewController dismissModalViewControllerAnimated:YES];
    NSLog(@"Error finding match: %@", error.localizedDescription);
}

// A peer-to-peer match has been found, the game should start
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)theMatch {
    [presentingViewController dismissModalViewControllerAnimated:YES];
    self.match = theMatch;
    match.delegate = self;
    if (!matchStarted && match.expectedPlayerCount == 0) {
        NSLog(@"Ready to start match!");
        [self lookupPlayers];
    }
}

#pragma mark GKMatchDelegate

// The match received data sent from the player.
- (void)match:(GKMatch *)theMatch didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID {
    if (match != theMatch) return;

    [delegate match:theMatch didReceiveData:data fromPlayer:playerID];
}

// The player state changed (eg. connected or disconnected)
- (void)match:(GKMatch *)theMatch player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state {
    if (match != theMatch) return;

    switch (state) {
        case GKPlayerStateConnected:
            // handle a new player connection.
            NSLog(@"Player connected!");

            if (!matchStarted && theMatch.expectedPlayerCount == 0) {
                NSLog(@"Ready to start match!");
                [self lookupPlayers];
            }

            break;
        case GKPlayerStateDisconnected:
            // a player just disconnected.
            NSLog(@"Player disconnected!");
            matchStarted = NO;
            [delegate matchEnded];
            break;
    }
}

// The match was unable to connect with the player due to an error.
- (void)match:(GKMatch *)theMatch connectionWithPlayerFailed:(NSString *)playerID withError:(NSError *)error {

    if (match != theMatch) return;

    NSLog(@"Failed to connect to player with error: %@", error.localizedDescription);
    matchStarted = NO;
    [delegate matchEnded];
}

// The match was unable to be established with any players due to an error.
- (void)match:(GKMatch *)theMatch didFailWithError:(NSError *)error {

    if (match != theMatch) return;

    NSLog(@"Match failed with error: %@", error.localizedDescription);
    matchStarted = NO;
    [delegate matchEnded];
}

- (void)reportScore:(int64_t)score forCategory:(NSString *)category {
    // Only execute if OS supports Game Center & player is logged in
    if ([self isGameCenterAvailable] && [GKLocalPlayer localPlayer].authenticated == YES)
    {
        // Create score object
        GKScore *scoreReporter = [[[GKScore alloc] initWithCategory:category] autorelease];

        // Set the score value
        scoreReporter.value = score;

        // Try to send
        [scoreReporter reportScoreWithCompletionHandler:^(NSError *error) {
            if (error != nil)
            {
                // Handle reporting error here by adding object to a serializable array, to be sent again later
                [unsentScores addObject:scoreReporter];
            }
        }];
    }
}

- (BOOL) reportAchievementIdentifier: (NSString*) identifier percentComplete: (float) percent {

    if ([self isGameCenterAvailable] && [GKLocalPlayer localPlayer].authenticated == YES)
    {
        GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier: identifier] autorelease];
        if (achievement)
        {
            achievement.percentComplete = percent;
            [achievement reportAchievementWithCompletionHandler:^(NSError *error)
             {
                 if (error != nil)
                 {
                     // Retain the achievement object and try again later (not shown).
                 }
             }];
        }

        return YES;
    }

    return NO;
}


@end

最后,这就是我从游戏层调用游戏中心的方式(我尝试了两种不同的选项,但都没有奏效)

选项1

[[GCHelper sharedInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController: [[[UIApplication sharedApplication] keyWindow] rootViewController] delegate: self];

选项 2

AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
        UINavigationController *viewController = [app navController];
        [[GCHelper sharedInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController:viewController delegate:self];

任何帮助将不胜感激。提前致谢...

4

2 回答 2

2

几个月来我一直在处理多人游戏,邀请对我来说一直是一个真正的问题....和你一样,我使用 Ray 的教程让我开始。我今天意识到 Ray 的代码中有一个错误,如果两个客户端都启动了 GKMatchmakerView,则邀请将不起作用。当您第一次收到邀请时,您需要将其关闭,其中包含以下内容:

        [gcdelegate.viewController  dismissViewControllerAnimated:YES
                                 completion:^{
                                     GKMatchmakerViewController *mmvc = [[GKMatchmakerViewController alloc] initWithInvite:pendingInvite];
                                     mmvc.matchmakerDelegate = self;
                                     [gcdelegate.viewController presentModalViewController:mmvc animated:YES];
                                     self.pendingInvite = nil;
                                     self.pendingPlayersToInvite = nil;
                                     boo_invite=true;
                                 }];
于 2013-05-27T04:23:30.810 回答
0

好的,它似乎再次工作,而我们的代码或网络设置没有任何更改。我打开了 Apple 支持、错误记录等的票……似乎其中一些有效……

现在我们了解到这是 Game Center 沙盒中的一个错误。在我看来,sanbox版的游戏中心不是很稳定,苹果的人对这项服务也没有给予足够的重视。也没有办法在互联网上检查系统状态。

我仍在继续与 Apple 支持人员讨论以了解原因,并在完成后在此处分享所有对话。

快乐的编码...

于 2013-05-03T13:22:27.247 回答