Unity Native Pluginの作り方（共通編）

#概要
iOSやAndroid向けに開発したSDKをUnityアプリに対応させる場合、プラグインを作ってSDKとUnityアプリの橋渡しをしてやる必要があります。本記事ではUnity側からSDKにアクセスするためのコードの一例を紹介します。具体的なプラグインの実装部分については、それぞれ[iOS編](https://qiita.com/maebaru/items/3ebc9b625ad0b8fd38c1)、[Android編](https://qiita.com/maebaru/items/976a0a0d6a4fab0ab069)を参考にして下さい。 

#サンプルコード
##ISample.cs
Unity側からSDKのコードにアクセスするためのメソッドを宣言するインタフェースです。このインタフェースの実装についてはそれぞれ[iOS編](https://qiita.com/maebaru/items/3ebc9b625ad0b8fd38c1)、[Android編](https://qiita.com/maebaru/items/976a0a0d6a4fab0ab069)を参考にして下さい

```c#:ISample.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum SampleStatus {
    Unavailable = 0,
    Available = 1
}

public interface ISample {
    void Start(string sampleCode);
    void DoSomething();
    SampleStatus GetStatus();
    int GetScore();
    bool IsAvailable();
    string GetSomeText();
}
```

##Sample.cs
上記インタフェースを実装したオブジェクトをsampleNativeとして保持し、そのメソッドの呼び出しを代理で行うWrapperクラスです。

```c#:Sample.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using MiniJSON;

public class Sample : MonoBehaviour {
    private ISample sampleNative;
    public string iosSampleCode;
    public string androidSampleCode;
    private static Sample instance;

    public static Sample GetInstance() {
        if (instance == null) {
            Sample existingInstance = GameObject.FindObjectOfType<Sample>();

            if (existingInstance == null) {
                return null;
            }

            existingInstance.SetSampleNative();
            instance = existingInstance;
        }

        return instance;
    }

    private void Awake() {
        if (instance != null) {
            GameObject.Destroy(this);
            GameObject.Destroy(this.gameObject);
            return;
        }

        SetSampleNative();
        GameObject.DontDestroyOnLoad(this.gameObject);

        instance = this;
    }

    private void SetSampleNative() {
        if (sampleNative != null) {
            return;
        }

        #if UNITY_IOS
            sampleNative = new Sample_iOS(this);
        #elif UNITY_ANDROID
            sampleNative = new Sample_Android(this);
        #endif
    }

    public void DoSomething() {
        sampleNative.DoSomething();
    }

    public SampleStatus GetStatus() {
        return sampleNative.GetStatus();
    }

    public int GetScore() {
        return sampleNative.GetScore();
    }

    public bool IsAvailable() {
        return sampleNative.IsAvailable();
    }

    public string GetSomeText() {
        return sampleNative.GetSomeText();
    }

    public ISample SampleNative {
        get {
            return sampleNative;
        }
    }
}
```

##SampleEventListener.cs
SDKからのコールバックをUnity側で受けるためのListenerクラスです。

```c#:SampleEventListener.cs
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using MiniJSON;

public class SampleEventListener : MonoBehaviour {
    public static event Action<SampleStatus> NotifyStatusChanged;
    private ISample nativeParent;

    public void SetNativeParent(ISample nativeParent) {
        this.nativeParent = nativeParent;
    }

    public void _Sample_didStatusChange(string message) {
        SampleStatus status = (SampleStatus)int.Parse(message);

        if (NotifyStatusChanged != null) {
            NotifyStatusChanged(status);
        }
    }
}
```

## 使用例
Main.csを任意のSceneにアタッチすることでSampleオブジェクトの初期化が行われ、以降はSampleオブジェクト経由でSDKの機能にアクセスすることが可能になります。

```c#:Main.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour {
    void Start () {
        Sample.GetInstance();
    }

    public void OnClickDoSomething() {
        Sample.GetInstance().DoSomething();
    }

    public void OnClickGetStatus() {
        SampleStatus status = Sample.GetInstance().GetStatus();
        ToastUtil.Toast(this, "Status: " + status);
    }

    public void OnClickGetSomeText() {
        string text = Sample.GetInstance().GetSomeText();
        ToastUtil.Toast(this, "text: " + text);
    }
    
    void OnEnable() {
        SampleEventListener.NotifyStatusChanged += NotifyStatusChanged;
    }

    void OnDisable() {
        SampleEventListener.NotifyStatusChanged -= NotifyStatusChanged;
    }

    private void NotifyStatusChanged(SampleStatus status) {
        Debug.Log("# Status changed: " + status);
    }
}
```

#参考
- [Unity Tutorial: Writing Plugin](https://unity3d.com/jp/learn/tutorials/topics/scripting/writing-plugins?playlist=17117)
- [Unity Document: Plugins](https://docs.unity3d.com/ja/540/Manual/Plugins.html)
- [Example of Unity plugin](https://github.com/sessionm/sessionm-unity)