cocos2d-x Game Center 연동. GKLeaderboardView를 띄워보자

cocos2d-x 프로젝트에 Game Center를 연동해 보겠습니다. 그전에 선행작업들이 이미 끝나 있어야합니다.

먼저 GameKit.framework를 추가해줘야합니다. Project를 선택하고 TARGETS에서 개발중인 App선택 후 Build Phases탭을 선택합니다. Link Binary With Libraries를 선택 후 GameKit을 입력해 framework를 찾아 Add시킵니다.

ios용 작업이기 때문에 프로젝트 네비게이터에서 ios폴더에 마우스 우클릭 -> New File을 선택합니다. 또는 command + n을 눌러도 됩니다.

iOS -> Cocoa Touch -> Objective-C class를 선택합니다.

 원하시는 Class 이름을 입력합니다. 저는 GameCenter.mm으로 입력했습니다. mm으로 한 이유는 cocos2d-x의 CCLOG와 CCMessageBox를 사용하기 위해서이구요 간단한 mm파일에 대한 설명을 링크를 통해 확인하시기 바랍니다. Subclass of는 NSObject로 선택합니다.

 저장 폴더를 선택합니다. ios로 설정해줬습니다.

프로젝트 네비게이터에 GameCenter.h와 mm 파일이 추가되었습니다.

이제 소스를 추가 및 수정합니다. HelloWorld 샘플에 붙이는 간단한 버젼입니다. GKLeaderboardViewController에 대한 처리만 되어있고 그외 GKAchievementViewController, GKMatchmakerViewController등은 없습니다. 자세한 개발 내용 참고는 iOS Developer Library를 참고하세요. 그리고 제가 실수 했던 내용들도 포함해서 정리한 것이라 헷갈릴 수도 있겠습니다.

먼저 GameCenter.h

#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>
///< iTunes Connect에 설정한 리더보드 Id
#define LeaderboardID @"testLBId"

@interface GameCenter : NSObject
<GKLeaderboardViewControllerDelegate>

+ (BOOL) startGameCenter;
///< 게임센터 사용가능한가?
+ (BOOL) isGameCenterAPIAvailable;
///< 접속인증
+ (void) authenticateLocalPlayer;
///< 점수 요청
+ (void) reportScore:(int64_t)score;

+ (void) showLeaderboard;

@end


//GameCenter.mm

#import "GameCenter.h"
#include "cocos2d.h"
#import "EAGLView.h"

USING_NS_CC;

@implementation GameCenter

+ (BOOL) startGameCenter
{
    if( [self isGameCenterAPIAvailable] == NO )
    {
        CCLOG("startGameCenter Faile");
        return NO;
    }
    [self authenticateLocalPlayer];
    CCLOG("startGameCenter OK");
    return YES;
}

+ (BOOL) isGameCenterAPIAvailable
{
    // Check for presence of GKLocalPlayer class.
    BOOL localPlayerClassAvailable = (NSClassFromString(@"GKLocalPlayer")) != nil;
    
    // The device must be running iOS 4.1 or later.
    NSString *reqSysVer = @"4.1";
    NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
    BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
    
    return (localPlayerClassAvailable && osVersionSupported);
}

+ (void) authenticateLocalPlayer
{
    GKLocalPlayer* localPlayer = [GKLocalPlayer localPlayer];
    [localPlayer authenticateWithCompletionHandler:^(NSError *error) {
        if( localPlayer.isAuthenticated )
        {
            /*
             ///< NSString char* 컨버팅을 해야...
             CCLOG("Alias : %s", localPlayer.alias);
             CCLOG("Player ID : %s", localPlayer.playerID);
            */
            NSLog(@"Alias : %@", localPlayer.alias);
            NSLog(@"Player ID : %@", localPlayer.playerID);
        }
    }];
}

+ (void) reportScore:(int64_t)score
{
    GKScore* scoreReporter = [[[GKScore alloc] initWithCategory:LeaderboardID] autorelease];
    scoreReporter.value = score;
    [scoreReporter reportScoreWithCompletionHandler:^(NSError *error) {
        if( error != nil )
        {
            CCMessageBox("reportScore Error", "Game Center");
        }
    }];
}

UIViewController* tempUIView;
+ (void) showLeaderboard
{   
    GKLeaderboardViewController* pController = [[[GKLeaderboardViewController alloc] init] autorelease];

    if( pController != nil )
    {
        pController.leaderboardDelegate = self;
        
        tempUIView = [[UIViewController alloc] init];
        [[EAGLView sharedEGLView] addSubview:tempUIView.view];
        [tempUIView presentModalViewController:pController animated:YES];
    }
}

- (void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
    [viewController dismissModalViewControllerAnimated:YES];
    [viewController.view.superview removeFromSuperview];
    [viewController release];
}


마지막으로 AppController.mm
///< 추가

#import "GameCenter.h"


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
...
...

///< 추가

if( [GameCenter startGameCenter] == YES )
{
      //[GameCenter showLeaderboard];

}

cocos2d::CCApplication::sharedApplication().run();


이제 빌드 후 실행하면 이렇게 나올 수도 있고 아닐수도 있는데, 이건 Game Center Account가 없어서 그런거니 만들면 됩니다.

기존것이나 새로 만들어 로그인 했는데 위와 같이 나온다면 iTunes Connect와 Xcode 프로젝트 설정상 Bundle identifier를 다르게 설정한 것입니다.


위 2개의 Bundle ID와 Bundle identifier부분이 같아야합니다. 같게 변경 후 다시 실행하면 새로운 HelloWorld가 빌드되어 생성되고 아래와 같은 화면을 볼 수 있습니다.

WestWood Forever는 제 닉네임에요. 여기까지 오셨으면 일단 Game Center 초기화 정도까진 끝난 단계입니다.

이제 위에서 주석을 달았던

[GameCenter showLeaderboard];
를 주석을 풀어 빌드 후 실행해보면

Leaderboard가 보이긴 하는데 세로 사이즈인 것 마냥 나온다면 회전을 시켜줘야했던거 같네요

//      예전 버전에서는 회전해줘야 했던거 같은데 회전 안해줘도  되는군.
        tempUIView.view.frame = CGRectMake(0, 0, 320, 480);
        tempUIView.view.transform = CGAffineTransformMakeRotation(CC_DEGREES_TO_RADIANS(90));
        
        tempUIView.view.center = [[EAGLView sharedEGLView] window].center;
        tempUIView.view.bounds = CGRectMake(0, 0, 480, 320);
        [[EAGLView sharedEGLView] addSubview:tempUIView.view];

예전엔 이렇게 했던거 같은데 지금은

        [[[EAGLView sharedEGLViewwindowaddSubview:tempUIView.view];

이거 한줄이면 되네요. 차이점은 EGLView의 window에다가 addSubview를 하는 것인데 구글링을 해보니 예전에는 EGLView에 다들 하던데 cocos2d와 cocos2d-x 차이인 건지는 잘 모르겠네요. 아무튼 회전시킬필요 없이 한줄이면 됩니다.

 자 이제 회전도 제대로 되었겠다 리더보드 같은 녀석이 뜨네요. 근데 원래 이리 썰렁한건가? 했었고... Done를 누르는 순간,

뭥미? 이건 Resource에 파일명이 잘 못되었을때 아님 autorelease관련 refcount관리 제대로 안되었을때 나타나던 증상인데말이죠? 이거뭐 callstack도 제대로 안나오고... 한참 헤메인결과...

- (void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
    [viewController dismissModalViewControllerAnimated:YES];
    [viewController.view.superview removeFromSuperview];
    [viewController release];
}

이놈이 호출이 안되고 있네요 뭐지?
@protocol GKLeaderboardViewControllerDelegate
@required
// The leaderboard view has finished
- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController;
@end

보면 딜리게이트에 - 로 되어있는데, 제가 만든 GameCenter는 죄다 + ( static 멤버 함수 )마냥 해서 그런가 싶어 +로 해주고 하니 잘 호출이 되는군요. 하지만

    [viewController release];

이놈이 또 문제였고 위에서 autorelease를 안했던 녀석인
    [tempUIView release];

이걸로 대체했더니 이제 에러는 없더군요. 하지만

Done 한번 눌러서 나갔다가 다시 들어오면 어라? 화면이 더 화려해졌네여? 이역시 해결을 했는데요,


AppController.mm에

if( [GameCenter startGameCenter] == YES )
{
      [GameCenter showLeaderboard];

}

이 부분이 문제였습니다. 샘플 테스트 한다고 바로 초기화 하자마자 리더보드 좀 보자했더니 생긴 문제였는데요, 일단 startGameCenter만 하고 Helloworld에 있는 close버튼 클릭했을 때 기존
/*
    CCDirector::sharedDirector()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
     */

이것은 주석하고
    [GameCenter showLeaderboard];
으로 대체했고

HelloWorldScene.cpp를 Objective-C 소스도 알아먹을 수 있게 mm으로 변경했습니다. 당연히 제일 위에는
#include "GameCenter.h"
추가했구요.

이렇게 해서 리더보드까지 띄워봤는데요, 실수 했던 내용들 먼저 나오고 추후 수정 내용이 나와 헷갈리실 수도 있겠네요. 여기서 좀더 이쁘게 꾸미고 다듬어야 하겠지요. 이것은 샘플 수준이니까요. 자세한 것은 애플 개발자 센터 가이드를 참고하시구요.

cocos2d-x가 그렇듯 Objective-C단은 ios용 Game Center를 붙게, Android는 Java로 GREE SDK를 붙게 한 후 중간에 c++딴 매핑단계를 만들어 사용하기 쉽게? 하는게 제 개인 목표기도 합니다.

cocos2d-x에 도전과제인 Achievements 연동은 따로 정리를 했습니다. 링크를 참고하세요. 나머지 언급 안했던 GKMatchmaker에 대한 것은 cocos2d기반 포스팅이긴 하지만  다른분들 링크로 대신합니다.


마지막으로 위 소스들 최종 정리된 것으로 남겨봅니다.

//GameCenter.h


#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>

#define LeaderboardID @"testLBId"

@interface GameCenter : NSObject
<GKLeaderboardViewControllerDelegate>

+ (BOOL) startGameCenter;
///< 게임센터 사용가능한가?
+ (BOOL) isGameCenterAPIAvailable;
///< 접속, 인증
+ (void) authenticateLocalPlayer;
///< 점수 요청
+ (void) reportScore:(int64_t)score;

+ (void) showLeaderboard;

@end



//GameCenter.mm

#import "GameCenter.h"
#include "cocos2d.h"
#import "EAGLView.h"

USING_NS_CC;

@implementation GameCenter

+ (BOOL) startGameCenter
{
    if( [self isGameCenterAPIAvailable] == NO )
    {
        CCLOG("startGameCenter Faile");
        return NO;
    }
    [self authenticateLocalPlayer];
    
    CCLOG("startGameCenter OK");
    return YES;
}

+ (BOOL) isGameCenterAPIAvailable
{
    // Check for presence of GKLocalPlayer class.
    BOOL localPlayerClassAvailable = (NSClassFromString(@"GKLocalPlayer")) != nil;
    
    // The device must be running iOS 4.1 or later.
    NSString *reqSysVer = @"4.1";
    NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
    BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
    
    return (localPlayerClassAvailable && osVersionSupported);
}

+ (void) authenticateLocalPlayer
{
    GKLocalPlayer* localPlayer = [GKLocalPlayer localPlayer];
    [localPlayer authenticateWithCompletionHandler:^(NSError *error) {
        if( localPlayer.isAuthenticated )
        {
            /*
             ///< NSString char* 컨버팅을 해야...
             CCLOG("Alias : %s", localPlayer.alias);
             CCLOG("Player ID : %s", localPlayer.playerID);
            */
            NSLog(@"Alias : %@", localPlayer.alias);
            NSLog(@"Player ID : %@", localPlayer.playerID);
        }
    }];
}

+ (void) reportScore:(int64_t)score
{
    GKScore* scoreReporter = [[[GKScore alloc] initWithCategory:LeaderboardID] autorelease];
    scoreReporter.value = score;
    [scoreReporter reportScoreWithCompletionHandler:^(NSError *error) {
        if( error != nil )
        {
            CCMessageBox("reportScore Error", "Game Center");
        }
    }];
}

    UIViewController* tempUIView;
+ (void) showLeaderboard
{   
    GKLeaderboardViewController* pController = [[[GKLeaderboardViewController alloc] init] autorelease];

    if( pController != nil )
    {
        pController.leaderboardDelegate = self;
        
        tempUIView = [[UIViewController alloc] init];
        [[[EAGLView sharedEGLView] window] addSubview:tempUIView.view];
        [tempUIView presentModalViewController:pController animated:YES];
    }
}

+ (void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
    [viewController dismissModalViewControllerAnimated:YES];
    [viewController.view.superview removeFromSuperview];
    [tempUIView release];
}

@end



//AppController.mm


[[UIApplication sharedApplication] setStatusBarHidden: YES];
    
     ///< 추가
[GameCenter startGameCenter];
    
cocos2d::CCApplication::sharedApplication().run();


//HelloWorldScene.mm   cpp를 mm으로 변경한다

///< 추가
#include "GameCenter.h"

void HelloWorld::menuCloseCallback(CCObject* pSender)
{
    /*
    CCDirector::sharedDirector()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
     */
    [GameCenter showLeaderboard];
}

댓글

이 블로그의 인기 게시물

'xxx.exe' 프로그램을 시작할 수 없습니다. 지정된 파일을 찾을 수 없습니다.

goorm IDE에서 node.js 프로젝트로 Hello World Simple Server 만들어 띄워보기

애드센스 수익을 웨스턴 유니온으로 수표대신 현금으로 지급 받아보자.