Skip to content

Instantly share code, notes, and snippets.

@yano3nora
Last active September 23, 2023 16:35
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yano3nora/ab222f46abfbec85855b550130aa2b85 to your computer and use it in GitHub Desktop.
Save yano3nora/ab222f46abfbec85855b550130aa2b85 to your computer and use it in GitHub Desktop.
[unity: Unity WebGL build + Firebase] Unity interaction with JavaScript. #unity #firebase #js

Premise

  • firebase の unity 向け sdk は ios / android 用で webgl 向けはない
  • firebase の js 向け sdk を利用して csharp 側から js を実行するしかない
  • 状態の push は csharp から js function を call して firebase に push する
  • 状態の subscribe は js から csharp function を call して state を変更する

Interacting with JavaScript

WebGL: Interacting with browser scripting
Unity(WebGL)でC#の関数からブラウザー側のJavaScript関数を呼び出すまたはその逆(JS⇒C#)に関する知見(プラグイン形式[.jslib])

  • 大前提 webgl ビルド後じゃないと js 動かせない
    • unity editor にブラウザエミュレートはなく、今んとこ入る予定もなさそう
    • ビルド通すために「unity editor platform なら動かさない」分岐が必要
    • 開発ビルド > スクリプトのみビルド (patch build) で頑張るのが一番早そう
  • 外部依存は webgl build 用の template の index.html で cdn から取るなりする
    • jslib から window 参照できるのでそこに入れる感じ
  • jspre ファイルなら es6 で書ける
  • js => csharp に非同期処理の結果を返すようなケースは SendMessage しないと無理そう

js 側がでかいなら、iOS / Android の SDK で emulator につないで開発 ... とかが必要そう。

UnityのWebGLでJavaScriptを使いやすくする

↑ 検証したい

Arguments

基本的な数値型は、変換を必要とせずに、関数パラメーターで JavaScript に渡すことができます。他のデータ型は、emscripten ヒープ内のポインターとして渡されます (実際には JavaScript の大きな配列です)。文字列の場合は、Pointer_stringify ヘルパー関数を使用して JavaScript 文字列に変換できます。文字列値を返すには、メモリを割り当てるために _malloc を呼び出し、JavaScript 文字列を書き込むために stringToUTF8 ヘルパー関数を呼び出す必要があります。文字列が戻り値の場合は、il2cpp ランタイムがメモリ解放の処理をします。プリミティブ型の配列の場合、emscripten はメモリを表すさまざまなサイズの整数、符号なし整数、浮動小数点のヒープに対して、異なる ArrayBufferViews を提供します。 HEAP8、HEAPU8、HEAP16、HEAPU16、HEAP32、HEAPU32、HEAPF32、HEAPF64 などです。 WebGL のテクスチャにアクセスするために、emscripten は GL.textures 配列を提供し Unity から WebGL テクスチャオブジェクトへネイティブのテクスチャ ID をマッピングします。WebGL 関数は、emscripten の WebGL コンテキストGLctx で呼び出すことができます。

  • 数値はそのまま引数として渡せる、数値以外は変換をかます必要がある
  • csharp => js に文字列を渡すなら js 側で Pointer_stringify() で変換
  • js => csharp に文字列を渡すなら _malloc() で文字列の長さ分メモリバッファを確保してから stringToUTF8() で変換してバッファを返却
  • 非同期で値を返す、みたいなことは仕様上できないっぽいので SendMessage で unity のメソッドを読んで、そこに引数渡すのがいいみたい

ざっと見て Newtonsoft.Json なんかで Json のやりとりに徹底したほうが楽そう。


Example

<!-- webgl build template index.html -->
<head>
  <!-- firebase -->
  <script src="https://www.gstatic.com/firebasejs/8.2.10/firebase-app.js"></script>
  <script src="https://www.gstatic.com/firebasejs/8.2.10/firebase-firestore.js"></script>
  <script src="https://www.gstatic.com/firebasejs/8.3.0/firebase-database.js"></script>
  <script>
    // initialize
    var firebase = window.firebase;
    
    firebase.initializeApp({
      apiKey: 'xxx',
      authDomain: 'xxx',
      databaseURL: 'xxx',
      projectId: 'xxx',
    });

    // emulator setting
    if (window.location.hostname === 'localhost') {
      firebase.database().useEmulator('localhost', 9000);
    }
  </script>
  <body>
    <canvas id="unity-canvas" style="background: #333"></canvas>
    <script>
      var unityInstance = null;

      createUnityInstance(
        document.querySelector("#unity-canvas"),
        {
          dataUrl: "Build/{{{ DATA_FILENAME }}}",
          frameworkUrl: "Build/{{{ FRAMEWORK_FILENAME }}}",
          codeUrl: "Build/{{{ CODE_FILENAME }}}",
          streamingAssetsUrl: "StreamingAssets",
          companyName: "{{{ COMPANY_NAME }}}",
          productName: "{{{ PRODUCT_NAME }}}",
          productVersion: "{{{ PRODUCT_VERSION }}}",
        }
      ).then(instance => {
        unityInstance = instance;
      });
    </script>
  </body>
</head>
// Assets/Plugins/Firebase.jslib

mergeInto(LibraryManager.library, {
  fetchState: function () {
    firebase.database()
      .ref('game-state')
      .get()
      .then(function (snapshot) {
        const json = (function () {
          return snapshot.exists()
            ? JSON.stringify(snapshot.val())
            : '';
        })();
      
        unityInstance.SendMessage('Firebase', 'SyncState', json);
      });
  },

  // 引数に default 値入れると build コケるので注意
  pushState: function (json) {
    firebase.database()
      .ref('game-state')
      .set(JSON.parse(Pointer_stringify(json)));
  },
});
using Newtonsoft.Json;
using UnityEngine;
using System.Runtime.InteropServices;

public class Firebase: MonoBehaviour {
    [DllImport("__Internal")]
    private static extern void fetchState();
    [DllImport("__Internal")]
    private static extern void pushState(string json);

    public void Start() {
        if (Application.platform != RuntimePlatform.WebGLPlayer)
        {
          return;
        }

        fetchState();
    }
  
    // static method だと sendmessage できないので注意
    public void SyncState(string json)
    {
        object state = JsonConvert.DeserializeObject(json);

        Debug.Log(JsonConvert.SerializeObject(state));
      
        // あとは csharp 側の変数に代入するなりなんなり
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment