はじめに

久しぶりにUnityのWegGLを触る機会に巡りあわせたので、調査内容をまとめてみました。

Unity公式フォーラムでも言われているように、Unity2020.1からはどうやら大きな変更があるみたいです。

今回の基本編では、主にUnity2019.4までと何が変わったのかを確認していきながら、 ビルド時の圧縮についてや、C#,Js(※1)間の連携をみていきたいと思います。

ある程度Unityに触り慣れている人ならすぐにわかるであろうBuild方法などは、 今回は省略しています。

※1:以下この記事では、Js = JavaScript を指します。

開発環境

  • Unity2020.1.6f1
  • Windows10
  • テストしたサーバー環境
    • Firebase Hosting
    • XAMPP+Apache

※Chromeはローカルでは動きませんでした。ローカルで試したい場合でもサーバーを通す必要があります。

主な変更点

以上の内容を簡略してまとめてみました。詳細はそれぞれのリンクをご覧になってください。

2020/10/12現在、肌感ではありますがまだ公式以外では、Unity2020.1以降の記事は

あまり見受けられないように感じれます。

ほとんどが、Unity2019.4までの内容だと思いますので、バージョンに気を付けて下さい。

モバイルサポートダイアログの画像

なお、Unity2020.1でもモバイルは公式にサポートしていませんが、 こちらのサイトにあるような、上記の警告ダイアログは表示されず、軽いアプリならiPhone、Androidでも正常に 動作しました。

固有のビルドローダ

どうやら汎用的に作られていたのものを最適化して、

読み込みパフォーマンスを上げたみたいです。

ローダーのサイズがUnity2019.3では約155KBだったのが、

Unity2020.1では9KBまで縮小されているとこのことです。

テンプレート変数

以前までの、%VARIABLE%は無くし、 Unity2020.1では下記のようなプリプロセッサ変数が、 .html、.css、.js、.php、で使用出来るようになったみたいです。

Unity側のPlayerSettingsの内容がそのまま使用できるイメージです。

変数名 内容
COMPANY_NAME 会社名
PRODUCT_NAME 製品名
PRODUCT_VERSION バージョン
WIDTH デフォルトのキャンバス幅
HEIGHT デフォルトのキャンバスの高さ
SPLASH_SCREEN_STYLE 選択したスプラッシュスタイルに応じて、「DARK」または「LIGHT」に設定されます

他にもまだたくさんありますので、詳細はマニュアルを見てください。

マクロ

{{{****}}}のように、中括弧3つでくくることによって、上記のような、 プリプロセッサ変数が使えるようになります。

<div id = "unity-build-title"> {{{PRODUCT_NAME}}} </ div>

この場合、Unity側でProductNameを「MyWebGLGame」のように設定していれば、 その文字列が返ってきます。

条件付きディレクティブ

下記のような、条件付きディレクティブを書くことが出来るようになりました。

#if EXPRESSION
  // EXPRESSIONがTrueならここを通る。
#else
  // 違うのであれば、ここを通る。
#endif

カスタムユーザー変数

以前までは、「UNITY_CUSTOM_」と自作のテンプレート変数はマークする必要がありましたが、

Unity2020.1からはすべて自動的に検出するため不要になったみたいです。

ビルド

Unity2020.1インスタンス化関数が次のように変更されました。

Unity2019.4まで

UnityLoader.instantiate(container, url, override)

Unity2020.1から

createUnityInstance(canvas, config, onProgress).then(onSuccess).catch(onError);
  • 引数に<canvas>要素を渡すことが出来るようになり、ページレイアウトを完全に制御できるようになりました。
  • ビルド構成オブジェクトを外部JSONに保存せずに、直接引数に渡すようになりました。これによって読み込みパフォーマンスが向上しました。
  • Promiseオブジェクトを返すようになりました。
    • Jsの知見がある方にとっては常識かもしれないですが、PromiseオブジェクトとはJsの非同期処理を担ってくれるものみたいです。

PlayerSettingsの圧縮方式

圧縮設定の画像

ProjectSettings -> Player -> WebGL -> Publishing Settings で設定出来る内容です。 上記画像の赤線の箇所が圧縮の設定です。

Compression Format

圧縮方式 公式マニュアルの説明
gzip デフォルトのオプションです。GzipファイルはBrotliファイルよりも大きいですが、ビルドが速くHTTPとHTTPSの両方ですべてのブラウザーでネイティブにサポートされています。
Brotli Brotli圧縮は、最高の圧縮率を提供します。Brotliで圧縮されたファイルはgzipよりも小さいですが、圧縮に時間がかかるため、リリースビルドでの反復時間が長くなります。ChromeとFirefoxは、HTTPSを介したBrotli圧縮のみをネイティブにサポートしています。
Disabled 圧縮を無効にします。後処理スクリプトで独自の圧縮を実装する場合は、このオプションを使用します。ホスティングサーバーで静的圧縮を使用する場合にも使用する必要があります。

なおこちらのフォーラムではUnity2020では圧縮するとWebアプリの起動に失敗すると言われており、これと同じ現象が起きました。

無圧縮にすることでアプリの起動は出来ましたが、サーバー側の設定で上手くいくのではないかと思います。

Decompression fallback

Js解凍ツールをビルドに埋め込み、ブラウザが圧縮にした場合はコンテンツ を解凍します。 ビルドファイルの読み込みに時間がかかるみたいです。

ファイルサイズが小さいためかもしれないですが、無圧縮との体感的な違い は感じられなかったです。

圧縮方式をどれにしようと、こちらにチェックをいれておくと、アプリ起動に成功しました。

C#、Jsとの相互連携

相互連携の公式マニュアル

C#からJsを呼ぶ

Unity2019.4とやり方は変わらないです。

mergeInto(LibraryManager.library, {
  Hello: function () {
    window.alert("Hello, world!");
  },
  ~省略~
}

マニュアルにあるような、上記Jsを*.jslibという拡張子で作成します。

jslibの配置画像

それを上記画像のような、Assets/Pluginsフォルダに配置します。

using UnityEngine;
using System.Runtime.InteropServices;

public class NewBehaviourScript : MonoBehaviour {

    [DllImport("__Internal")]
    private static extern void Hello();

    void Start() {
          Hello();
    }
}

次にC#から呼ぶときですが、上記のようにDllImport属性をつけて、 呼び出します。このあたりはC++のDLL呼び出しと同じ感じだと思います。

JsからC#を呼ぶ

こちらはUnity2019.4と2020.1ではunityInstanceの生成方法が違うため、 参照方法に注意が必要です。

unityInstance.SendMessage('MyGameObject', 'MyFunction');
unityInstance.SendMessage('MyGameObject', 'MyFunction', 5);
unityInstance.SendMessage('MyGameObject', 'MyFunction', 'MyString');

まずJs側ですが、呼び出す関数はどちらのバージョンも上記のように同じです。

  • 第一引数:スクリプトをアタッチしているゲームオブジェクトの名前
  • 第二引数:スクリプトのPublic関数の名前
  • 第三引数:なし or 数字 or 文字列
 var script = document.createElement("script");
      script.src = loaderUrl;
      script.onload = () => {
            createUnityInstance(canvas, config, 
            (progress) => {
                            //プログレスバーの表示の更新
                            progressBarFull.style.width = 100 * progress + "%";
                          })
            .then((unityInstance) => {
                            //プログレスバーの表示を消す。
                            loadingBar.style.display = "none";
                            //Unityにコールバックを送る。
                            unityInstance.SendMessage('ReceiveJS', 'SetText', 'CallBack Success.');
                            })
            .catch((message) => {
                              alert(message);
                            });
      };
      document.body.appendChild(script);

なおJsの知見が無い筆者は、createUnityInstance()の成功した時の コールバックで受け取れる、unityInstanceから直接呼んで呼ばれるのを確認しました。

ビルドして生成されたファイルの画像

このcreateUnityInstance()はデフォルトのテンプレート(※2)を設定しているのなら、Build後に生成される上記画像のようなindex.htmlから確認することが出来ます。

Jsに知見にある方なら、このunityInstanceを任意に参照して、 別のJsライブラリと連携といったことも可能だと思います。

※2:Build後に生成されるHTMLテンプレートのことです。初期はデフォルトに設定されています。

/// <summary>
/// Js側から呼ばれるときのテスト
/// </summary>
public class ReceiveJsSample : MonoBehaviour
{
        [SerializeField]
        private Text _label;

        /// <summary>
        /// テキスト設定
        /// </summary>
        /// <param name="text"></param>
        public void SetText(string text)
        {
            _label.text = text;
        }
}

C#側はPublic関数を用意してゲームオブジェクトにアタッチするだけで、簡単です。

まとめ

以前はUnityのWebGLに対してあまり動作しなくて、ネガティブなイメージを持っていましたが、今回久しぶりにUnityのWebGLを触ってみて、思っていたより使いやすくなっているというイメージを抱きました。

さらに最適化も各所に施されていうということで、これからWegGLを触るのであれば Unity2020.1以降が良いのではないかと思います。

次の応用編では、Unityのロゴを消すちょっとしたHTMLの改造と、Unityエンジニア ならよく見かけるであろういつもの"あれ"を表示してみたいと思います。



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる