Unityのプラグインを作るAndroid/iOS

# Unityのプラグインを作る

## Android編

.aarの作成方法は↓の記事が素晴らしい。
このままAndroidStudioをライブラリをビルドして.aarを作れば良い。

[突然仕事でUnity向けのSDKやPluginを作ることになった人向け資料<Android実践編>](http://qiita.com/keidroid/items/455c61de9355eff2a907)

### 組み込み方法

作った.aarを Assets/Plugins/Android　に置けば使える。

### スタティックメソッドの呼び出し方

AndroidJavaClass + CallStatic<戻り値の型>()

#### Unity(C#)

```C#:Scene.cs
AndroidJavaClass cls = new AndroidJavaClass("com.package.PluginTest");
string result = cls.CallStatic<string>("execStaticMethod", "2", 2);

```

#### Android(Java)

```Java:PluginTest.java

public class PluginTest
{
    // スタティック関数
    public static String execStaticMethod(String str, int multiply)
    {
        Integer i = Integer.parseInt(str);
        String result = String.format("%d", i * multiply);
        return result;
    }
}

```



### インスタンスメソッドの呼び出し方

AndroidJavaObject + Call<戻り値の型>()

#### Unity(C#)

```C#:Scene.cs
AndroidJavaObject cls = new AndroidJavaObject("com.package.PluginTest"/*, "コンストラクタに引数も渡せる" */);
string result = cls.Call<string>("execMemberMethod", text, 2);

```

#### Android(Java)

```Java:PluginTest.java

public class PluginTest
{
    // メンバー関数
    public String execMemberMethod(String str, int multiply)
    {
        Integer i = Integer.parseInt(str);
        String result = String.format("%d", i * multiply);
        return result;
    }
}

```


### Delegate登録してコールバックを受け取る

com.unity3d.player.UnityPlayer.UnitySendMessage

#### Unity(C#)

```C#:Scene.cs
AndroidJavaClass cls = new AndroidJavaClass("com.package.PluginTest");
cls.CallStatic("execDelegateMethod", text, 2, "Canvas", "Delegate");

```

```C#:Scene.cs
	public void Delegate(string message)
	{
		Debug.Log(message);
	}
```

#### Android(Java)

```Java:PluginTest.java

public class PluginTest
{
    // デリゲート関数
    public static void execDelegateMethod(final String str, final int multiply, final String gameobject, final String method)
    {
        new Thread(new Runnable() {
            public void run() {
                try {
                    Thread.sleep(3000);
                    String result = execStaticMethod(str, multiply);
                    // 文字列一つしか送れないから、複数の場合はJSONとかに
                    com.unity3d.player.UnityPlayer.UnitySendMessage(gameobject, method, result);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

```

## iOS編

iOSは
[Unity & iOSプラグイン連携](http://qiita.com/shell/items/7a1e585f030f3f6aa601)
にもあるように、
C# → C → Objective-C(++) → Swift
こういうブリッジになる。

↓ここが一番詳しく書いている
[[Unity] C#とObjective-Cの連携まとめ]
(http://qiita.com/tkyaji/items/74d485a021c75ed10bca)

### 組み込み方法

ソース・ファイルをAssets/Plugins/iOSに置けば、iOS Build時のXCodeプロジェクトに組み込まれて使える。
拡張子は　.mm がオススメ

### C言語でオブジェクト指向
C言語はオブジェクト指向言語ではないので、オブジェクト指向的な実装をするためには、

```ObjC:Object.mm
Obj* Obj_new
{
  return [Obj alloc];
}
void Obj_doSomething(Obj* obj, int value)
{
  [obj doSomething:value];
}
```
こういう実装にしておけば、こう書ける↓

```C#:Object.cs
IntPtr obj = Obj_new();
Obj_doSomething(obj, 2);
```

### 型対応表
|C#|C|
|:-:|:-:|
|int|int| 
|float|float|
|string|char*|
|System.IntPtr|void*|

配列もいけるっぽい↓
[[Unity] C#とObjective-Cの連携まとめ]
(http://qiita.com/tkyaji/items/74d485a021c75ed10bca)


#### 注意
返り値を文字列にするためには、その文字列はmalloc()で確保しておかないといけない。ARCとかauto releaseとかそういう問題ではなく、返り値がstring型だったらUnityがchar* をfree()している。

だから、

```
return "abc";
```
とかすると、　"abc"をfree()するので落ちる。
こういう関数を用意しておいて、

```
const char* mallocString(const char* str)
{
  char* ret = (char*)malloc(strlen(str)*4);
  strcpy(ret, str);
  return ret;
}
```

```
return mallocString("abc");
```
こうするのが正解。(これでもUnityがfree()しているからメモリーリークは起きていないはず)

[参考：How to return string from native iOS plugin to unity?](https://stackoverflow.com/questions/37047781/how-to-return-string-from-native-ios-plugin-to-unity)



### インターフェースを作る

.hに定義しそうなもんだが、.mmに直書きでいける。

#### iOS(Object-C++)

```ObjC:Interface.mm
#import <Foundation/Foundation.h>
#import "PluginTest.h"

#ifdef __cplusplus
extern "C" {
#endif
  
  // スタティック関数
  const char* PluginTest_execStaticMethod(const char* str, int mupltiply)
  {
    return [PluginTest execStaticMethod:str multiply:mupltiply];
  }

  // インスタンス生成
  PluginTest* PluginTest_new()
  {
    return [PluginTest alloc];
  }
  
  // メンバ関数
  const char* PluginTest_execMemberMethod(PluginTest* instance, const char* str, int mupltiply)
  {
    return [instance execMemberMethod:str multiply:mupltiply];
  }
  
  // デリゲート
  void PluginTest_execDelegateMethod(const char* str, int multiply, const char* gameobject, const char* method) {
    [PluginTest execDelegateMethod:str multiply:multiply gameobject:gameobject method:method];
  }
  
  
#ifdef __cplusplus
}
#endif

```

### スタティックメソッドの呼び出し方

#### Unity(C#)

```C#:PluginTest.cs
// iOS側で用意したインターフェースの定義
[DllImport("__Internal")]
static extern string PluginTest_execStaticMethod(string text, int multiple);
```
```C#:MainScene.cs
// 呼ぶとき
PluginTest.PluginTest_execStaticMethod(text, multiple);
```

#### iOS(Object-C++)

```Obj-C:PluginTest.mm
// + が Static関数
+(const char*)execStaticMethod:(const char*)str multiply:(int)multiply
{
  int num = atoi(str);
  int result = num * multiply;
  return mallocString([[NSString stringWithFormat:@"%d", result]UTF8String]);
}
```

### インスタンスメソッドの呼び出し方

#### Unity(C#)

```C#:PluginTest.cs
[DllImport("__Internal")]
static extern IntPtr PluginTest_new();

[DllImport("__Internal")]
static extern string PluginTest_execMemberMethod(IntPtr p, string text, int multiple);
```
```C#:MainScene.cs
// 呼ぶとき
var p = PluginTest.PluginTest_new();
PluginTest.PluginTest_execMemberMethod(p, text, multiple);
```

#### iOS(Object-C++)

```Obj-C:PluginTest.mm
// + が Static関数
-(const char*)execMemberMethod:(const char*)str multiply:(int)multiply
{
  int num = atoi(str);
  int result = num * multiply;
  return mallocString([[NSString stringWithFormat:@"%d", result]UTF8String]);
}
```



### Delegate登録してコールバックを受け取る
UnitySendMessageで、ゲームオブエジェクト名・メソッド名を指定して、C#にメッセージを送る。

#### Unity(C#)

```C#:PluginTest.cs
[DllImport("__Internal")]
static extern void PluginTest_execDelegateMethod(string text, int multiple, string gameobject, string method);
```
```C#:MainScene.cs
// 呼ぶとき
PluginTest.PluginTest_execDelegateMethod(p, text, multiple, "Canvas", "Delegate");
```


#### iOS(Object-C)

```C#:PluginTest.mm
// Delegateでコールバック
+(void)execDelegateMethod:(const char*)str multiply:(int)multiply gameobject:(const char*)gameobject method:(const char*)method
{
  const char* result = [PluginTest execStaticMethod:str multiply:multiply];
  const char* go = mallocString(gameobject);
  const char* mt = mallocString(method);
  
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [NSThread sleepForTimeInterval:3.0];
    UnitySendMessage(go, mt, result);
  });
}
```