connect


三日ぐらい戦ってましたがようやく繋がりました。

まず、OpenGL ESのテンプレートにはViewControllerがないため、leaderboardController等を表示できません。そこで、新しくViewBasedのプロジェクトを作り、従来のOpenGL ESプロジェクトのEAGLView.mやES1Render.mをこのプロジェクトに追加します。そして、ViewController.xibに新規ビューを追加、クラス名をEAGLViewに変えます。あとは、ViewController.mのviewDidLoadで[glView startAnimation];を呼んでから、self.view=(UIView*)glView;と、現在をビューを、追加したEAGLViewで上書きしてやればOpenGLが動きます。

ゲームを起動した直後には、authenticateWithCompletionHandlerでユーザにログインを促します。

- (void) authenticateLocalPlayer
{
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error) {
if (error == nil)
{
}
else
{
// Your application can process the error parameter to report the error to the player.
}
}];
}

Leaderboardを表示するにはGKLeaderboardViewControllerを使います。

- (void) showLeaderboard
{
GKLeaderboardViewController *leaderboardController = [[GKLeaderboardViewController alloc] init];
if (leaderboardController != nil)
{
leaderboardController.leaderboardDelegate = self;
[self presentModalViewController: leaderboardController animated: YES];
}
}

- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
[self dismissModalViewControllerAnimated:YES];
}

マッチ選択画面を開くには次のようにします。

- (void)hostMatch
{
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = 2;
request.maxPlayers = 2;

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

[self presentModalViewController:mmvc animated:YES];
}

マッチの構築に失敗したら次のデリゲートが呼ばれます。

- (void)matchmakerViewControllerWasCancelled:(GKMatchmakerViewController *)viewController
{
[self dismissModalViewControllerAnimated:YES];
// implement any specific code in your application here.
}

- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFailWithError:(NSError *)error
{
[self dismissModalViewControllerAnimated:YES];
// Display the error to the user.
}

マッチの構築に成功したら、didFindMatchが呼ばれます。

- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)match
{
[self dismissModalViewControllerAnimated:YES];

match.delegate=self.match_callback;
self.my2Match = match; // Use a retaining property to retain the match.

// Start the game using the match.
NSLog(@"match begin");
}

マッチは今後も使うので、メンバ変数に格納しておきます。

@interface METHUSELAYZEDESTRUCTERViewController : UIViewController {
GKMatch *my2Match;
MatchCallback *match_callback;
}

@property (nonatomic, retain) IBOutlet GKMatch *my2Match;
@property (nonatomic, retain) IBOutlet MatchCallback *match_callback;

このままだとsetterがないと言われるので、ViewControllerで

@synthesize my2Match;

と宣言して、setterをコンパイラに自動生成させます。

マッチに代入するデリゲートは、新規クラスで、GKMatchDelegateを継承して作ります。

@interface MatchCallback : NSObject <GKMatchDelegate> {
bool matchStarted;
}

//match
- (void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state;
- (void)match:(GKMatch *)match connectionWithPlayerFailed:(NSString *)playerID withError:(NSError *)error;
- (void)match:(GKMatch *)match didFailWithError:(NSError *)error;
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID;

@end

これで、マッチ関連のイベントを受け取ることができます。

プレイヤが参加するごとにdidChangeStateが呼ばれます。match.expectedPlayerCount==0になったら全員がそろったのでゲームを開始します。

- (void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
{
NSLog(@"match state called");

switch (state)
{
case GKPlayerStateConnected:
// handle a new player connection.
break;
case GKPlayerStateDisconnected:
// a player just disconnected.
break;
}

NSLog(@"%d",match.expectedPlayerCount);

if (!matchStarted && match.expectedPlayerCount == 0)
{
matchStarted = YES;
NSLog(@"match start");
 }
}

sendDataToAllPlayersで全員に、sendData:data toPlayers:playerIDsで特定のユーザにメッセージを送れます。

[match sendDataToAllPlayers: data withDataMode: GKMatchSendDataUnreliable error:&error];

NSArray *playerIDs=[NSArray arrayWithObject:host_id];
[match sendData:data toPlayers:playerIDs withDataMode:GKMatchSendDataUnreliable error:&error];

送信データはNSData*なので、NSString*と相互変換します。

NSString *str= [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSData *data = [mes dataUsingEncoding:NSUTF8StringEncoding];

メッセージはdidReceiveDataに届きます。

- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID
{
NSString *str= [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}

自分のプレイヤのIDはGKLocalPlayerで取得することができます。

GKLocalPlayer *lp = [GKLocalPlayer localPlayer];
NSString *id=lp.playerID

マッチに参加しているプレイヤのIDはmatch.playerIDsに入ります。ただし、match.playerIDsは自分は含まれません。ですので、クライアントサーバモデルにしてプレイヤの中からホストを決定する場合は、playerIDが一番小さいのとかにするとよさそうです。

なお、GKMatchでは、稼働中のマッチへの乱入はできません。

-----------------------------------------------
参考までにメトセラのコードです。
MatchCallback.mm
MatchCallback.h
METHUSELAYZEDESTRUCTERViewController.m