4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unity WebGL における Javascript 非同期処理の実装パターン

Last updated at Posted at 2024-12-21

WebGL ビルドで JavaScript と連携する際、非同期処理はパフォーマンスとユーザー体験の向上に不可欠です。しかし、従来の UnityInstance.SendMessage を用いた方法では、コールバック処理が煩雑になり、コードの見通しが悪くなるという問題がありました。このパターンでは、async/await と UniTask を活用することで、非同期処理を同期的に記述でき、コードの可読性と保守性を大幅に向上させています。

実装

1. Javascript関数呼び出しクラス

MyJsFunc.cs
public class MyJsFunc
{
    [DllImport("__Internal")]
    private static extern void _myJsFunc(string args, string callbackObject, string callbackMethod);

    private GameObject _gameObject;
    private CallbackAwaiter _awaiter;

    public MyJsFunc()
    {
        // unityInstance.SendMessage() で戻り値を受け取る
        // ユニークな名前の空 GameObject を作成する
        _gameObject = new GameObject(Guid.NewGuid().ToString());
        _awaiter = _gameObject.AddComponent<CallbackAwaiter>();
    }

    public async UniTask<string> Invoke(string args)
    {
        try
        {
            _myJsFunc(args, _gameObject.name, "Callback");
            return await _awaiter.Task;
        }
        finally
        {
            // 処理が終わったら GameObject を片付ける
            Destroy(_gameObject);
        }
    }
}

2. コールバック待機コンポーネント

CallbackAwaiter.cs
public class CallbackAwaiter : MonoBehaviour
{
    private UniTaskCompletionSource<string> _utcs = new();
    public UniTask<string> Task => _utcs.Task;

    void Awake()
    {
        // シーン遷移後もコールバックを受け取れるようにする
        DontDestroyOnLoad(gameObject);
    }

    // Javascript 側から UnityInstance.SendMessage で呼び出されるメソッド
    public void Callback(string str)
    {
        _utcs.TrySetResult(str);
    }
}

3. C# から Javascript 関数を呼び出すためのインターフェース

myfunc.jslib
mergeInto(LibraryManager.library, {
  _myJsFunc: function (args, callbackObject, callbackMethod) {
    args = UTF8ToString(args)
    callbackObject = UTF8ToString(callbackObject)
    callbackMethod = UTF8ToString(callbackMethod)
    // jslib は薄い Wrapper として利用している
    window.unityBridge.doSomething(args, callbackObject, callbackMethod)
  }
})

4. 非同期処理の記述

myfunc.ts
window.unityBridge = window.unityBridge || {}

window.unityBridge.doSomething = async (args: string, callbackObject: string, callbackMethod: string) => {
  // ...
  // 非同期で行う何らかの処理
  const result = await xxx()

  // 処理結果をコールバック
  window.unityInstance.SendMessage(callbackObject, callbackMethod, result)
}

5. ユーティリティクラス

Javascript.cs
public static class Javascript
{
    public static UniTask<string> DoSomething(string args)
    {
        return new MyJsFunc().Invoke(args);
    }
}

JavaScript関数の呼び出しを簡略化するためのユーティリティクラス。

Javascript の処理の呼び出し

var result = await Javascript.DoSomething(args);

Javascript 側の非同期処理を async/await パターンで呼び出せるようになりました。

補足

実際にはエラーハンドリングをちゃんと行ったり、CallbackAwaiter.cs にキャンセルトークン持たせたり、コールバックが返ってこない場合のタイムアウト処理を持たせたり、引数や戻り値に複雑なデータ型をやり取りするために JSON を使用したり、MyJsFunc クラスでは複数の Javascript の関数を呼び出せるように Template Method パターンで実装を分割したりしているが、核心部分はこんな感じ。

4
3
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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?