Mobage Native SDK を cocos2d-x で使ってみた(iOS 編)

monma_smile.JPG

いつもお世話になっております。テクニカルコンサルティンググループの monma と申します。

業務といたしましては、パートナーアライアンス統括部というパートナーデベロッパー様に近いポジションで、技術コンサルティングを行わせて頂いております。また、社内でも社内開発者とデベロッパー様との架け橋、2nd Party の技術サポート、ドキュメント整備等を日々の業務として行っています。

DeNA に入社したのは 2011 年 4 月で、ほぼ丸 3 年に渡りパートナー様とコミュニケーションさせて頂いておりますので、ご存知の方もいらっしゃるのではないかと思います。

私もこちらのブログに記事を書かせて頂けることとなりましたので、今後共どうぞよろしくお願いします。

0. 前置き

「突然ですが、皆様はゲーム開発にはどんなゲームエンジンを使用していますか?」

default_download.jpg

今回は Objective-C で動作する Mobage Native SDK for iOS を cocos2d-x 上で動かしてみましたので、Mobage にログインできるようになるまでを書いていきたいと思います。

※この記事では、ただ cocos2d-x の Native コード側で Mobage Native SDK を動かすのではなくクロスプラットフォームで互換性のある実装をしていくための方法の解説となります。また、この記事の内容はそのうち cocos2d-x 用 Mobage SDK のサンプルコードとして公開させていただこうと考えています。

1. 前提条件

cocos2d-x での Native 間連携は、いろいろな記事でも見かけますが、CocosDenshion を参考にするのが一番手っ取り早いです。この記事の内容も CocosDenshion を参考にしています。迷ったら CocosDenshion に戻ってくると道が開けるはず。。

参考資料:http://www.slideshare.net/doraemonsss/20130314-cocos2d-x

また、cocos2d-x iOS では、mm ファイルに直接 C++ のコードを実装することができるため、Android に比べると実装は簡単です。Android 編に関しては、後日掲載させていただきます。

※ 記事の内容は、以下の環境での動作を確認しています。

  • Mac OSX 10.8.5
  • Mobage Native SDK 1.4.6.2
  • cocos2d-x 2.2.2 (C++)
  • Xcode 5.0.2

2. まずは Mobage Native SDK の組み込み

何はともあれ Mobage Native SDK を組み込まないと話が先に進みません。

cocos2d-x のテンプレートプロジェクトや、もしくは既存の Xcode プロジェクトに対して、以下の URL を参考にしながら Mobage Native SDK を組み込んでいきます。

https://docs.mobage.com/display/JPSA/Native+Tutorial+Chapter+2+iOS+Setup

上記ドキュメントのプロジェクト作成以外の部分を行っていきます。

3. 作成するクラスのイメージ

本記事で作成するクラスのイメージは下図のような形で C++ から Objective-C の MBGPlatform.h にアクセスするように実装していきます。

4. cocos2d-x から呼び出すための定義部

それでは cocos2d-x 側の実装に入っていきましょう。

CCMobagePlatform.h という以下のようなファイルを用意します。このヘッダファイルを通して Mobage Native SDK にアクセスすることができるようになります。今回はログインのみの実装になりますので、少し簡素なものとなります。

CCMobagePlatform.h

#ifndef __cocos2dMobage__CCMobagePlatform__
#define __cocos2dMobage__CCMobagePlatform__

class CCMobagePlatformDelegate
{
public:
    virtual void onLoginComplete(const char* userId) = 0;
    virtual void onLoginRequired() = 0;
    virtual void onLoginError(int code, const char* description) = 0;
    virtual void onLoginCancel() = 0;
    virtual void onSplashComplete() = 0;
};

namespace CCMobage {
    enum REGION {
        REGION_US, //unsupported for now
        REGION_JP,
        REGION_CN, //unsupported for now
        REGION_KR
    };

    enum SERVER_TYPE {
        SANDBOX,
        PRODUCTION
    };

    class CCMobagePlatform
    {
    private:
        CCMobagePlatformDelegate* m_mobagePlatformDelegate;
                
    public:
        CCMobagePlatform();
        ~CCMobagePlatform();
        
        static CCMobagePlatform* sharedMobagePlatform();
        static void end();
        
        void initialize(REGION region,
                        SERVER_TYPE server,
                        const char* strConsumerKey,
                        const char* strConsumerSecret,
                        const char* strAppId);
        
        CCMobagePlatformDelegate* getDelegate(){ return m_mobagePlatformDelegate; }
        void setDelegate(CCMobagePlatformDelegate* pDelegate);
        void hideSplashScreen();
        void checkLoginStatus();
        void showLoginDialog();        
    };
}

#endif /* defined(__cocos2dMobage__CCMobagePlatform__) */

この内容は、ほとんど Mobage Native SDK for iOS に存在する MBGPlatform.h と内容的には同じものになります。言い換えると、CCMobagePlatform.h は MBGPlatform.h を C++ で書き換えているだけと言えます。

上から内容を解説していきます。

・CCMobagePlatformDelegate クラス

CCMobagePlatformDelegate は MBGPlatformDelegate の代替として準備します。C++ 側で Mobage ログインリスナーへの通知を受け取るための delegate のようなクラスとして定義しています。このクラスのメソッドは全て仮想関数として定義されていますので、使用する場合は多重継承して使用することになります。

・REGION、SERVER_TYPE

この 3 つの enum は MBGPlatform.h の MBG_REGION、MBG_SERVER_TYPE を C++ 側からもアクセスできるように置き換えただけのものになります。

・CCMobagePlatform クラス

CCMobagePlatform クラス内の解説に移りますが、基本的なメンバメソッド、initialize、hideSplashScreen、checkLoginStatus、showLoginDialog に関しては、MBGPlatform の同名のメソッドを C++ で書き換えたものになります。

メンバ変数の m_mobagePlatformDelegate や setDelegate メソッドについては、CCMobagePlatformDelegate クラスのインスタンスを保持しておく目的で定義されています。

static CCMobagePlatform* sharedMobagePlatform() メソッドは、このクラスをシングルトンクラスとしてアクセスできるようにするために用意しています。実行すると内部に静的なインスタンスが作成されます。

5. cocos2d-x から呼び出すための実装部

次に実装部へと移ります。実装部ではまだ Mobage Native SDK 本体へはアクセスしません。C++ と Objective-C の橋渡し役になります。C++ の実装なのですが、.cpp ファイルではなく、.mm ファイルとして行ないます。

以下のように実装を行っていきます。

CCMobagePlatform.mm

#include "CCMobagePlatform.h"
#include "CCMobagePlatformIOS.h"

namespace CCMobage {
    
    static CCMobagePlatform* sharedMobagePlatformObject = nil;
    
    CCMobagePlatform::CCMobagePlatform()
    {
        m_mobagePlatformDelegate = NULL;
    }
    
    CCMobagePlatform::~CCMobagePlatform()
    {
    }
    
    CCMobagePlatform* CCMobagePlatform::sharedMobagePlatform()
    {
        if (! sharedMobagePlatformObject)
        {
            sharedMobagePlatformObject = new CCMobagePlatform();
        }
        return sharedMobagePlatformObject;
    }
    
    void CCMobagePlatform::end()
    {
        if (sharedMobagePlatformObject)
        {
            delete sharedMobagePlatformObject;
            sharedMobagePlatformObject = NULL;
        }
        [CCMobagePlatformIOS sharedMobagePlatformEnd];
    }
    
    void CCMobagePlatform::initialize(REGION region,
                                      SERVER_TYPE server,
                                      const char* strConsumerKey,
                                      const char* strConsumerSecret,
                                      const char* strAppId)
    {    
        NSString* consumerKey = [NSString stringWithCString:strConsumerKey encoding:NSUTF8StringEncoding];
        NSString* consumerSecret = [NSString stringWithCString:strConsumerSecret encoding:NSUTF8StringEncoding];
        NSString* appId = [NSString stringWithCString:strAppId encoding:NSUTF8StringEncoding];
        [[CCMobagePlatformIOS sharedMobagePlatform] initialize: (NSInteger)region 
                                                 serverType: (NSInteger)server
                                                consumerKey: consumerKey
                                             consumerSecret: consumerSecret
                                                      appId: appId];
        
        return;
    }
    
    void CCMobagePlatform::setDelegate(CCMobagePlatformDelegate* pDelegate)
    {
        [[CCMobagePlatformIOS sharedMobagePlatform] setDelegate:pDelegate];
    }
    
    void CCMobagePlatform::hideSplashScreen()
    {
        [[CCMobagePlatformIOS sharedMobagePlatform] hideSplashScreen];
    }
    
    void CCMobagePlatform::checkLoginStatus()
    {
        [[CCMobagePlatformIOS sharedMobagePlatform] checkLoginStatus];
    }
    
    void CCMobagePlatform::showLoginDialog()
    {
        [[CCMobagePlatformIOS sharedMobagePlatform] showLoginDialog];
    }
        
} // endof namespace CCMobage {

CCMobagePlatform 実装部で行うことは、

  • CCMobagePlatformIOS.h 内の CCMobagePlatformIOS クラスへの値の受け渡し(データ型キャスト含む)
  • シングルトンのオブジェクトの管理

のみとなります。

6. Objective-C 側の定義部

さて、あとは Objective-C 側の実装を残すのみとなりました。定義部の実装をしていきましょう。CCMobagePlatformIOS.h として、以下のように Objective-C で実装していきます。

CCMobagePlatformIOS.h

#import "MBGPlatform.h"

class CCMobagePlatformDelegate;

@interface CCMobagePlatformIOS : NSObject
{
    CCMobagePlatformDelegate* m_mobagePlatformDelegate;
}
+ (CCMobagePlatformIOS*) sharedMobagePlatform;
+ (void) sharedMobagePlatformEnd;

- (void) initialize:(NSInteger)region
         serverType:(NSInteger)server
        consumerKey:(NSString *)key
     consumerSecret:(NSString *)secret
              appId:(NSString *)appId;

- (void) setDelegate:(CCMobagePlatformDelegate*) pDelegate;

- (void) hideSplashScreen;

- (void) checkLoginStatus;

- (void) showLoginDialog;

@end

まず、MBGPlatform.h を import します。

クラス定義として、CCMobagePlatformDelegate を定義しておきます。これは実装部で include される CCMobagePlatform.h に記載されている CCMobagePlatformDelegate クラスを使用するためです。(ヘッダ部で CCMobagePlatform.h を include すると 2 重 include になってしまいます)

CCMobagePlatformIOS は NSObject の継承クラスですが、プロトコルに MBGPlatformDelegate を追加し、プロトコルの適合を行ない、MBGPlatformDelegate に通知されたイベントを受け取れるようにします。プロトコルの追加部分に関しては、以下のドキュメントをご確認下さい。

https://docs.mobage.com/display/JPSA/Native+Tutorial+Chapter+2+iOS+Develop

あとは CCMobagePlatform.h の CCMobagePlatformIOS クラスはシングルトンパターンなクラスとして実装します。また、CCMobagePlatformDelegate オブジェクトを受け取る為のオブジェクトと setDelegate メソッドを用意しておきます。

7. Objective-C 側の実装部

さあ最後です。あとは以下の実装を行えば、cocos2d-x 側から C++ のコードを通して Mobage Platform へログインできることとなります。

CCMobagePlatformIOS.mm

#include "CCMobagePlatformIOS.h"
#include "CCMobagePlatform.h"

@implementation CCMobagePlatformIOS

CCMobagePlatformIOS *sharedMobagePlatformObject = nil;

+ (CCMobagePlatformIOS*) sharedMobagePlatform
{
    @synchronized(self)     {
        if (!sharedMobagePlatformObject)
            sharedMobagePlatformObject = [[CCMobagePlatformIOS alloc] init];
    }
    return sharedMobagePlatformObject;
}

+(void) sharedMobagePlatformEnd 
{
    [sharedMobagePlatformObject release];
    sharedMobagePlatformObject = nil;
}  

- (void) initialize:(NSInteger)region
         serverType:(NSInteger)server
        consumerKey:(NSString *)key
     consumerSecret:(NSString *)secret
              appId:(NSString *)appId
{
    [MBGPlatform initialize:region
                 serverType:server 
                consumerKey:key
             consumerSecret:secret
                      appId:appId];
    [[MBGPlatform sharedPlatform] setDelegate:self];
}

- (void) setDelegate:(CCMobagePlatformDelegate*) pDelegate
{
    m_mobagePlatformDelegate = pDelegate;
}

- (void) hideSplashScreen
{
    [[MBGPlatform sharedPlatform] hideSplashScreen];
}

- (void) checkLoginStatus
{
    [[MBGPlatform sharedPlatform] checkLoginStatus];
}

- (void) showLoginDialog
{
    [[MBGPlatform sharedPlatform] showLoginDialog];
}

// MBGPlatformDelegate

- (void) onLoginComplete:(NSString *)userId
{
    if(m_mobagePlatformDelegate)
    {
        const char* strUserId;
        strUserId = [userId UTF8String];
        m_mobagePlatformDelegate->onLoginComplete(strUserId);
    }
}

- (void) onLoginRequired
{
    if(m_mobagePlatformDelegate)
    {
        m_mobagePlatformDelegate->onLoginRequired();
    }
}

- (void) onLoginError:(MBGError *)error
{
    const char* strErrorDescription;- (void) onLoginRequired
{
    if(m_mobagePlatformDelegate)
    {
        m_mobagePlatformDelegate->onLoginRequired();
    }
}

- (void) onLoginError:(MBGError *)error
{
    const char* strErrorDescription;

-(void) onLoginCancel
{
    if(m_mobagePlatformDelegate)
    {
        m_mobagePlatformDelegate->onLoginCancel();
    }
}

@end

上記のコードのポイントをまとめると、以下のようになります。

  • sharedInstance の管理
  • Mobage Native SDK に含まれるメソッドの呼び出し
  • delegate として(プロトコルを設定した)自分自身を設定
  • setDelegate で渡ってきた CCMobagePlatformDelegate オブジェクトをメンバ変数に格納する。
  • MBGPlatformDelegate で実装されたメソッド(onLoginRequired, onLoginComplete, onLoginCancel, onLoginError, onSplashComplete)が呼び出されたら、CCMobagePlatformDelegate オブジェクトの同名のメソッドを呼び出す。

実装自体は、Objective-C 上で MBGPlatform クラスへのデータの受け渡し、MBGPlatformDelegate に含まれるメソッドが呼び出された場合に CCMobagePlatformDelegate のメソッドを呼び出す実装がされています。

これで実装完了です!

あとは、作成した CCMobagePlatform.h, CCMobagePlatform.mm, CCMobagePlatformIOS.h, CCMobagePlatformIOS.mm を Xcode プロジェクトのターゲットに追加します。

8. いよいよ呼び出し

Mobage Native SDK for iOS のログイン周りの実装は、AppDelegate.h や AppDelegate.m へ組み込むことを想定しています。(Objective-C の AppDelegate クラス)

また、cocos2d-x の場合、アプリケーションの起点となるクラスは AppDelegate (C++) となり、AppDelegate.h ファイルと AppDelegate.cpp ファイルに実装されています。

本章は、前章までに作成した CCMobagePlatform クラスを cocos2d-x の AppDelegate クラスへ組み込む方法を解説します。

言語の違いはあれど、ほぼ以下のドキュメントと実装は同じになります。

https://docs.mobage.com/display/JPSA/Native+Tutorial+Chapter+2+iOS+Develop

以下のように実装していきます。

AppDelegate.h

#ifndef  _APP_DELEGATE_H_
#define  _APP_DELEGATE_H_

#include "cocos2d.h"
#include "CCMobagePlatform.h"

typedef enum{
    LOGIN_IDLE,
    LOGIN_REQUIRED,
    LOGIN_COMPLETE,
    LOGIN_ERROR
} LoginStatus;

class  AppDelegate : private cocos2d::CCApplication, CCMobagePlatformDelegate
{
public:
    AppDelegate();
    virtual ~AppDelegate();

    virtual bool applicationDidFinishLaunching();
    virtual void applicationDidEnterBackground();
    virtual void applicationWillEnterForeground();
    
    void checkProgress();
    LoginStatus loginStat;
    bool splashComplete;

    virtual void onLoginComplete(const char* userId);
    virtual void onLoginRequired();
    virtual void onLoginError(int code, const char* description);
    virtual void onLoginCancel();
    virtual void onSplashComplete();    
};

#endif // _APP_DELEGATE_H_

まずは、CCMobagePlatform.h の include を行ない、CCMobagePlatformDelegate の多重継承を行ないます。CCMobagePlatformDelegate に含まれるメソッドの宣言も追加しておきます。またログイン状態の管理用のメソッドして checkProgress メソッドも定義しておきます。

さあいよいよ実際のゲーム側の実装に移ります。
AppDelegate.cpp の AppDelegate::AppDelegate::applicationDidFinishLaunching() に以下の実装を追記します。

    const char* consumerKey = "sdk_app_id:120XXXXX";
    const char* consumerSecret = "c0c74ec60ee745b6733fefefefefefef";
    const char* appId = "120XXXXX";
    CCMobage::CCMobagePlatform::sharedMobagePlatform()->initialize(CCMobage::REGION_JP, CCMobage::SANDBOX,
                                                                   consumerKey, consumerSecret, appId);

    CCMobage::CCMobagePlatform::sharedMobagePlatform()->setDelegate(this);
    CCMobage::CCMobagePlatform::sharedMobagePlatform()->checkLoginStatus();

CCMobagePlatform は全てシングルトンパターンで実装されていますので、CCMobage::CCMobagePlatform::sharedMobagePlatform()->メソッド名のように、sharedMobagePlatform() メソッドで返却されるポインタを使用して、メソッドにアクセスします。

  • initialize メソッドは、MBGPlatform initialize の実装とほぼ変わりません。
  • setDelegate メソッドで this を渡して、onLoginRequired 等の delegate メソッドに通知が来るように設定を行ないます。
  • checkLoginStatus メソッドを呼び出すと、delegate メソッドに通知が行われます。

注意点として、applicationDidFinishLaunching 内で CCDirector でのシーンの遷移を行っていて、遷移先で CCMobagePlatform のメソッドを呼び出すような場合に、まだ Mobage Initialize やログインが完了していないケースがあります。onLoginComplete 以降でシーン遷移させるか、タイトルシーンでは Mobage のメソッドを一切呼ばないようにする等の処置が必要になります。(onLoginComplete 以降でシーン遷移させる場合は、シーンの状態管理も必要になります)

あとは、delegate メソッドとして以下のメソッドを実装しておきます。

void AppDelegate::checkProgress()
{
    if(loginStat == LOGIN_REQUIRED && splashComplete)
    {
        CCMobage::CCMobagePlatform::sharedMobagePlatform()->showLoginDialog();
    }
    if(loginStat == LOGIN_COMPLETE && splashComplete) 
    {
        // Game Start
    }
}

void AppDelegate::onLoginComplete(const char* userId)
{
    CCLog("userId:%s", userId); 
    loginStat = LOGIN_COMPLETE;
    checkProgress();
}
void AppDelegate::onLoginRequired()
{
    loginStat = LOGIN_REQUIRED;
    checkProgress();
}
void AppDelegate::onLoginError(int code, const char* description)
{
    // Error handling
}
void AppDelegate::onLoginCancel()
{
}
void AppDelegate::onSplashComplete()
{
    CCMobage::CCMobagePlatform::sharedMobagePlatform()->hideSplashScreen();
    splashComplete = true;
    checkProgress();
}

これも基本は、以下のドキュメントで記載されている内容を C++ に置き換えただけになります。

https://docs.mobage.com/display/JPSA/Native+Tutorial+Chapter+2+iOS+Develop

後は実行してみましょう!シミュレーターで以下のようにログイン画面が表示されれば成功です。

iOSシミュレータのスクリーンショット 2014.02.12 13.51.55.png

また、「モバゲー ID ではじめる」ボタンをタップし、ログインできるか確認します。ログイン画面がアプリ上で表示されますので、Mobage のテストアカウントの ID / パスワードを入力してログインします。

iOSシミュレータのスクリーンショット 2014.02.12 13.54.15.png

ログ上に userId:XXXXX のような表示が出ていればログイン完了です!以下のようにゲーム画面に遷移します。

iOSシミュレータのスクリーンショット 2014.02.12 13.52.22.png

次回は...

今回説明できなかった showLogoutDialog のコールバックをどのように受けとればいいかなどは次回以降で説明させていただきます。次回の記事は 「Mobage Native SDK を cocos2d-x で使ってみた(Android 編)」を書かせて頂く予定です。