Unityのjslib経由で別ファイルjsを呼び出す

## Unityからhoge.jsのhogeMethodを呼び出したい！

[PartyParrotVRM](https://mizotake.github.io/PartyParrotVRM/page/)というサイトを作っている際にjsの処理をライブラリ使ったりして行いたいと思いました。

その際に使った方法をメモ書きしておきます。

### C#とjslibの連携

C#からの呼び出し処理

```cs:CallbackParameter.cs
    [Serializable]
    public class CallbackParameter
    {
        public string callbackGameObjectName;
        public string callbackFunctionName;
    }
```

```cs
        void Start()
        {
            var executer = new NativeExecuter();
            var callbackParameter = new CallbackParameter
            {
                callbackGameObjectName = gameObject.name,
                callbackFunctionName = "Callback"
            };
            var parameterJson = JsonUtility.ToJson(callbackParameter);
            executer.Execute("hogeMethod", parameterJson);
        }
        public void Callback()
        {
            Debug.Log("callback from js");
        }
```

C#からは呼び出すjsのメソッド名とjsonを渡すようにしています。jsonであればjsで簡単にparseができるためです。今回はCallbackを受けるGameObjectのメソッド名を渡しています。

次にjslibを呼びだすcsです

```cs:NativeExecuter.cs

using System.Runtime.InteropServices;
using UnityEngine;

namespace PartyParrotVRM
{
    public class NativeExecuter
    {

#if UNITY_WEBGL && !UNITY_EDITOR
        [DllImport("__Internal")]
        private static extern void execute(string methodName, string parameter);
#endif

        public void Execute(string methodName, string parameter = "{}")
        {
#if UNITY_WEBGL && !UNITY_EDITOR
            execute(methodName, parameter);
#else
            Debug.Log($"call native method: {methodName}, parameter : {parameter}");
#endif
        }
    }
}
```

上記のように書けば共通処理としてネイティブプラグインを呼び出すことができます。またEditor実行時に確認できるようにDebug.Logを仕込んでおくと便利です。

```js
var jsBridge = {
    execute: function(methodName, parameter) {
        // jsの文字列に変換する
        methodName = Pointer_stringify(methodName)
        parameter = Pointer_stringify(parameter)

        // 実行するメソッド名とパラメータをまとめる
        var jsonObj = {}
        jsonObj.methodName = methodName
        jsonObj.parameter = parameter

        var argsmentString = JSON.stringify(jsonObj)
        // カスタムイベントを作成して発行する
        var event = new CustomEvent('unityMessage', { detail: argsmentString })
        window.dispatchEvent(event)
    }
}

mergeInto(LibraryManager.library, jsBridge);
```

UnityのPlugins配下に置いておく.jslibになります。C#から受け取ったものはjsonにまとめてカスタムイベントに入れて発行します。jslibを調べるとよくpostMessageが使われているのですが何故CustomEventなのかと言うと、jsのライブラリによってはpostMessageを使っているものもあり予期せぬ挙動が起こるためにCustomEventにして回避しています。

今回、呼び出す.jsを`hoge.js`、呼び出すメソッド名を`hogeMethod`としておきます。(hoge.jsはindex.htmlに追加しているものとします)

```js:hoge.js

function hogeMethod(parameter) {
  console.log(parameter)
  // C#から指定されていたGameObject名とメソッド名を使ってコールバック処理を行う
  unityInstance.SendMessage(parameter.callbackGameObjectName, parameter.callbackFunctionName)
}

function recieveMessage(event) {
  var data = JSON.parse(event.detail)
  var methodName = data.methodName
  var parameter = data.parameter
  try {
    parameter = JSON.parse(parameter)
  } catch (e) {
    parameter = null
  }
  // C#から指定されているメソッドを呼び出しparameterを渡す
  eval(`${methodName}(parameter)`)
}

// unityMessageというCustomEventを受け取る
window.addEventListener('unityMessage', recieveMessage, false)
```

以上で全体の処理です。これでC#から`hoge.js`から`hogeMethod`を呼び出すことができます。

### まとめ

現時点ではこれが汎用的でC#から操作しやすいネイティブプラグインとなっていると思います。jsonを使えば言語ごとの型変換や引数の数に対応しなくてよくなるので書きやすいです。これはjsだとparseが簡単だというところもありますが、他のiosやwindowsなどのネイティブプラグインでもjsonでパースさせて解釈させる部分を作ればかなり簡単に受け渡しができるんじゃないかと思います。

hoge.jsなどの自前scriptなどは[WebGLTemplete](https://docs.unity3d.com/ja/current/Manual/webgl-templates.html)を使うことで簡単にビルドに組み込めるので必要があれば使うことをお勧めします。