Cocos2d-xコピペで始めるネイティブ連携


# 目的
ネイティブ連携の初動で毎回はまるのでシンプルな構造をテンプレート化しておく。備忘録。

# 初期設定
XcodeとEclipese(Android)でコード連携する際、XcodeのデフォルトだとClasses以下のパスは引けないので「Search Paths」に「Classes」を設定しておく
![SearchPath.png](https://qiita-image-store.s3.amazonaws.com/0/35973/8674faa8-2bd1-1690-cf0a-7a6e413c5349.png "SearchPath.png")

# Tree構造
* Xcode上での配置
* platform/Android以下は「Target Membership」のチェックをはずす
![ツリー構造.png](https://qiita-image-store.s3.amazonaws.com/0/35973/0b999585-8e6f-a4e8-4dbb-a7890cbfd1e7.png "ツリー構造.png")

* AndroidのJavaファイルはsrc/org/cocos2dx/libに設置
![ツリー構造Java.png](https://qiita-image-store.s3.amazonaws.com/0/35973/708e5bce-7207-4473-a904-5a0c6f4148fb.png "ツリー構造Java.png")

# 利用方法
* Delegateを使うのでclassにDelegateを設定

```cpp:HelloWorld.h
class HelloWorld : public cocos2d::Layer,public native_plugin::NativeDelegate
{
    // CallBack
    void method1(const char* str);
    void method2();
}
```

```cpp:HelloWorld.cpp
#include "HelloWorldScene.h"
#include "platform/NativeTest.h"
bool HelloWorld::init()
{
  … 省略
  auto* native = new native_plugin::NativeTest(this);
  native->test("hoge");
  … 省略
}
void HelloWorld::method1(const char *str){
    log("HelloWorld %s",str);
}

void HelloWorld::method2(){
    log("method2");
}
```

# 以下実装

```cpp:NativeTest.h
namespace native_plugin {

    class NativeTest;
    class NativeDelegate
    {
    public:
        virtual void method1(const char * str){}
        virtual void method2(){}
    };
    class NativeTest
    {
    public:
        NativeTest(NativeDelegate* delegate);
        ~NativeTest();
        void test(const char* str);
    };
}
```
## 以下iOS
```cpp:NativeTest.mm
#import "NativeTest.h"

@interface NativeManager : NSObject
{
    void *object;
}
- (void)test:(NSString*)str;

@end

@implementation NativeManager

- (void)test:(NSString*)str
{
    NSLog(@"[NativeTest] %@", str);
    
    native_plugin::NativeDelegate *pDelegate = (native_plugin::NativeDelegate*)object;
    if(pDelegate != NULL){
        pDelegate->method1("method1");
        pDelegate->method2();
    }
}

-(id)initWithDelegate:(void *)delegate
{
    self = [super init];
    if (self) {
	    object = delegate;
    }
    return self;
}
@end

namespace native_plugin {
    
    NativeManager * _native = NULL;
    class NativeTest;
    
    NativeTest::NativeTest(NativeDelegate* delegate){
        _native = [[NativeManager alloc] initWithDelegate:(void *)delegate];
    }
    NativeTest::~NativeTest()
    {
        [_native release];
    }
    void NativeTest::test(const char* str){
        [_native test:[NSString stringWithFormat:@"%s", str]];
    }
}
```
## 以下Android
```cpp:NativeTest.cpp
#include "platform/NativeTest.h"
#include "NativeTestJni.h"


namespace native_plugin {
    
    NativeTest::NativeTest(NativeDelegate* delegate)
    {
        setDelegateJni(delegate);
    }
    NativeTest::~NativeTest()
    {
        stopJni();
    }
    void NativeTest::test(const char *str)
    {
    	testJni(str);
    }
}
```

```cpp:NativeTestJni.h
#include <jni.h>

extern "C"
{
	void setDelegateJni(void *delegate);
	void testJni(const char *peerID);
	void stopJni();
}
```
```cpp:NativeTestJni.cpp
#include "NativeTestJni.h"
#include "platform/NativeTest.h"
#include "jni/JniHelper.h"
#include <jni.h>

#define  CLASS_NAME "org/cocos2dx/lib/NativeTest"

using namespace cocos2d;
using namespace std;

void *_delegate;

extern "C"
{
	void setDelegateJni(void *delegate)
	{
		_delegate = delegate;
	}

	void stopJni()
	{
	}
	void testJni(const char* str){
		JniMethodInfo t;
		if (JniHelper::getStaticMethodInfo(t, CLASS_NAME, "method1", "(Ljava/lang/String;)V")) {
			jstring arg1 = t.env->NewStringUTF(str);
			t.env->CallStaticVoidMethod(t.classID, t.methodID, arg1);
			t.env->DeleteLocalRef(arg1);
		}
	}

	JNIEXPORT void JNICALL Java_org_cocos2dx_lib_NativeTest_onMethod1(JNIEnv *env, jobject thiz, jstring str) {
		if(_delegate) {
			const char *arg1 = env->GetStringUTFChars(str, NULL);

			native_plugin::NativeDelegate *nativeDelegate = (native_plugin::NativeDelegate*)_delegate;
			nativeDelegate->method1(arg1);
		}
	}
}
```

```java:NativeTest.java
package org.cocos2dx.lib;

import org.cocos2dx.lib.Cocos2dxActivity;
import android.os.Bundle; 
import android.util.Log;

public class NativeTest extends Cocos2dxActivity {
	
	private static final String TAG = "NativeTest";
	private static final boolean D = true;
	public static native void onMethod1(String str);

	protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(D) Log.e(TAG, "onCreate"); 
    }
	
	public static void method1(String str) {
        if (D) Log.d(TAG, "start()");
	    if (D) Log.d(TAG, " > " + str);
		NativeTest.onMethod1("method1");
	}
}
```