LoginSignup
37
36

More than 5 years have passed since last update.

超〜簡単にcocos2d-xでネイティブ連携を行う方法

Last updated at Posted at 2014-02-24

cocos2d-xを使っていると指定URLをWebブラウザで開きたいときがあると思います。
そんなときは、iOS/Androidのネイティブ機能を使いますが、NativeBridgeを使って
下記の様にしてみましょう。

ポイントは、NativeBridgeというクラス(機能毎にクラス分けしておくと後で
必要な機能のみを別アプリに組み込む事も可能ですね)を作成してiOS/Androidの
ネイティブ実装を隠蔽し、cocos2d-x側ではどちらを呼び出すのか意識させない事です。
注意点として、BrowserNativeBridge.cppXcodeプロジェクトに含めないで下さい。

以下の例ではブラウザー機能の実装のため「BrowserNativeBridge」クラスを
用意しています。

BrowserNativeBridge

BrowserNativeBridge.hを用意する

//  BrowserNativeBridge.h
//  testApp
//
//  Created by takataka5845 on 2014/02/24.
//
//

#ifndef __testApp__BrowserNativeBridge__
#define __testApp__BrowserNativeBridge__

class BrowserNativeBridge {

public :

    static bool openURL(const char *url);

};

#endif /* defined(__testApp__BrowserNativeBridge__) */

iOS用にBrowserNativeBridge.mmを用意する

//  BrowserNativeBridge.mm
//  testApp
//
//  Created by takataka5845 on 2014/02/24.
//
//

#import "BrowserNativeBridge.h"

// NativeBridgeを使う場合は下記の1行は必須
#include "cocoa/CCString.h"

using namespace cocos2d;

bool BrowserNativeBridge::openURL(const char *pURL) {

    NSString *urlText = [NSString stringWithCString:pURL
                                          encoding:NSUTF8StringEncoding];

    NSLog(@"input URL[%@]", urlText);

    BOOL result = YES;

    NSURL *url = [NSURL URLWithString:urlText];

    // 別スレッドで使える様に「__block修飾子」を付けてローカル変数を宣言する
    __block BOOL isOpneApp = false;

    // GCDで実行する
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{

        // 以下は別スレッドで処理される //////////////////////////////////////////
        NSLog(@"Start canOpenURL");
        // そのURLが使えるかチェック
        if ([[UIApplication sharedApplication] canOpenURL:url]) {

            NSLog(@"End canOpenURL is Done");
            NSLog(@"Start openURL");

            // 実際にそのURLを使う
            BOOL ret = [[UIApplication sharedApplication] openURL:url];

            // URLが使えなかった
            if (ret == NO) {
                NSLog(@"End openURL is Error");
                NSLog(@"URL Opne Error!! [%@]", url);
            }
            // URLが使えた
            else {
                NSLog(@"End openURL is Done");
                isOpneApp = true;
            }
        }
        // 使えなかった場合
        else {
            NSLog(@"End canOpenURL is Error");
            NSLog(@"Can not open the URL using the safari.app... URL[%@]", url);
        }
        /////////////////////////////////////////////////////////////////////

        // 以下はメインスレッドで処理される ///////////////////////////////////////
        dispatch_sync(dispatch_get_main_queue(), ^{

            //
            if (isOpneApp == false) {
                UIAlertView *alert = [[[UIAlertView alloc]initWithTitle:@"Error"
                                                                message:@"「Mobile Safari」アプリが\n起動できませんでした"
                                                               delegate:nil
                                                      cancelButtonTitle:nil
                                                      otherButtonTitles:@"OK", nil]autorelease];
                [alert show];
            }

        });
        /////////////////////////////////////////////////////////////////////

    });

    // C++のbool型とObjective-CのBOOL型は変換が必要だよ!!
    if (result == YES) {
        return true;
    }
    return false;
}

Android用にBrowserNativeBridge.cppを用意する

//  BrowserNativeBridge.cpp
//  testApp
//
//  Created by takataka5845 on 2014/02/24.
//
//

#include "BrowserNativeBridge.h"
#include <android/log.h>
#include <jni.h>
#include "cocoa/CCString.h"
#include "platform/android/jni/JniHelper.h"
#include "cocos2d.h"

// Eclipse側のパッケージ名とjavaファイル名を指定
#define kCLASS_NAME "jp/mycompany/test/testApp"

// 呼び出すJavaメソッド名
static const char* const kMETHOD_NAME01 = "onOpenURL";

using namespace std;
using namespace cocos2d;

// 指定URLをWebブラウザで開く
bool BrowserNativeBridge::openURL(const char *url)
{
    JavaVM* jvm = JniHelper::getJavaVM();
    int status;
    JNIEnv *env;
    jmethodID mid;

    bool isAttached = false;

    CCLOG("start Static InterfaceJNI::openURL():[%s]", url);

    // Get Status(JNIのバージョンチェック)
    status = jvm->GetEnv((void **) &env, JNI_VERSION_1_6);
    if(status < 0)
    {
        // JNI インタフェースポインタ (JNIEnv) は、現在のスレッドでのみ有効です
        // 別のスレッドが Java VM にアクセスする必要がある場合、これは最初に AttachCurrentThread() を呼び出し、
        // それ自体を VM に接続し JNI インタフェースポインタを取得する必要があります。
        CCLOG("callback_handler: failed to get JNI environment, assuming native thread...");
        status = jvm->AttachCurrentThread(&env, NULL);
        CCLOG("Status 2: %d", status);
        if(status < 0)
        {
            CCLOG("callback_handler: failed to attach current thread");
            return false;
        }
        isAttached = true;
        CCLOG("Status isAttached: %d", isAttached);
    }
    //-----------------------------------------------------------

    CCLOG("Status: %d", status);

    // Javaクラスを探す
    jclass mClass = env->FindClass(kCLASS_NAME);

    CCLOG("jClass ");

    // メソッドを探す
    mid = env->GetStaticMethodID(mClass, kMETHOD_NAME01, "(Ljava/lang/String;)V");

    // 引数をJavaのString型に変換する
    jstring stringArg = env->NewStringUTF(url);

    CCLOG("mID: %d", mid);

    // メソッドを呼び出す
    if (mid!=0) {
        env->CallStaticVoidMethod(mClass, mid, stringArg);
        CCLOG("Call Method Finish");
    }
    else {
        CCLOG("failed:Method could not be found... ClassName[%s] Method[%s]", kCLASS_NAME, kMETHOD_NAME01);
    }

    if(isAttached) {
        // デタッチする(後処理)
        jvm->DetachCurrentThread();
    }

    return true;
}

Cocos2dxActivityを継承したクラスに以下を追加する(Java)

import android.net.Uri;
import android.util.Log;

public class testApp extends Cocos2dxActivity{

    private static Cocos2dxActivity myActivity;

    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        // Activityを保持する
        myActivity = this;
    }

    // 指定URLをWebブラウザで開く
    public static void onOpenURL(String msg)
    {
        Log.i ("INFO", "Start testApp:onOpenURL() URL[" + msg + "]");
        Uri uri = Uri.parse(msg);
        Intent i = new Intent(Intent.ACTION_VIEW,uri);
        myActivity.startActivity(i);
    }
}

cocos2d-x側にMenu等でコールバックを実装する

bool HelloWorld::init() {

    if ( !CCLayer::init() )
    {
        return false;
    }

    CCMenuItemImage *pMenuItem = CCMenuItemImage::create(
                                        "Normal.png",
                                        "Selected.png",
                                        this,
                                        menu_selector(HelloWorld::menuCallback) );

    CCSize size = CCDirector::sharedDirector()->getWinSize();
    pMenuItem->setPosition( ccp(size.width/2, size.height/2) );

    CCMenu* pMenu = CCMenu::create(pMenuItem,
                                   NULL);
    pMenu->setPosition( CCPointZero );
    this->addChild(pMenu, 1);

    return true;
}

void HelloWorld::menuCallback(CCObject* pSender)
{
    CCLog("HelloWorld::menuCloseCallback2() start!!");

    bool bResult = BrowserNativeBridge::openURL("http://qiita.com/takataka5845");
    if (bResult == false) {
        CCLOG("http://qiita.com/takataka5845 is OPEN Error...");
    }
    else {
        CCLOG("http://qiita.com/takataka5845 is OPEN Done!!");
    }
}

まとめ

上記の様に、ネイティブでの実装を隠蔽することによってcocos2d-x側の実装が
シンプルになります。
しかし、インターフェースをそろえる必要があったりしますが、ここはみなさんの
腕のみせどころです(笑

37
36
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
36