» VR開発メモトップへ

Unity WebGLビルドメモ

最終更新日:2023年11月13日

UnityでWebGLビルドを使用するときのTipsをメモしていく場所です。

更新履歴

(2023年11月13日)「Code Optimization」にUnity 2023.1.19と2023.2の設定の追加を反映
(2023年8月14日)リニアカラースペース時の動画再生のパフォーマンス問題に修正が入ったのを追記
(2023年7月3日)「フレームレートを下げるには」の記述を更新
(2023年6月8日)「Code Optimizationの設定をスクリプトから変更するには」「Post ProcessingのAmbient Occlusionを使用するとエラーが出る」を追加
(2023年6月7日)「AssetBundleで使用しているコンポーネントが読み込まれない」にクラスIDの説明を追加


UnityのWebGLビルドについて

Unityのバージョンについて

まず、Unity 2021.3 LTS以降にするのがおすすめです。Unity 2021.2でEmscriptenがバージョン2に更新され、ビルドが速くなり、出力サイズが小さくなっています。また、モバイルブラウザで圧縮テクスチャや圧縮オーディオがサポートされています。

さらにUnity 2022.1ではiPhone/Androidで実行するときの警告表示がなくなりました。スマートフォン向けのプロジェクトでも徐々に使っていけそうです。

Unityが出力するファイルについて

UnityのWebGLビルドは、WebAssemblyの実行ファイル(.wasm)とアセットをパッケージしたデータファイル(.data)、それらをロードするためのHTMLやJavaScriptファイルを出力します。これらのファイル一式を任意のウェブサーバーでホストすることで配信ができます。

WebAssemblyは、C#からIL2CPP(Unity製)で変換されたC++コードをEmscriptenでコンパイルし、UnityのランタイムモジュールのDLLとリンクして生成しているようです(このあたりもう少し追いかけたいです)。

WebAssemblyについて

WebAssemblyはW3Cで仕様の策定が進められているCPUアーキテクチャに依存しない実行形式で、ブラウザのJavaScriptエンジンに搭載されたWebAssembly専用のパスを使用してネイティブコードにコンパイル・実行されます。

ブラウザ JavaScriptエンジン コンパイラの解説
Chrome V8 WebAssembly compilation pipeline
Safari JavaScriptCore JavaScriptCore’s new WebAssembly interpreter
Firefox SpiderMonkey Firefox’s new streaming and tiering compiler

どのブラウザのエンジンも何段かのコンパイラを持ち、.wasmをロードしつつ速やかに(低速で)実行を開始する一方、バックグラウンドでより最適化されたネイティブコードを生成して徐々に入れ替えていくという挙動のようです。このため、ユーザーが.wasmファイルをロードしてコンパイルがある程度進行するまで、フレームレートの低下やヒッチが発生する場合があります。

使用できるレンダリングパイプラインは?

ビルトインまたはURPが使用できます。HDRPはWebGLビルドでは動作しません。URPを使用するとビルトインよりも出力サイズが若干大きくなります。

出力するウェブページについて

Player SettingsのResolution and Presentationで、出力するHTMLのテンプレートを選択できます。Unity公式ではWebGL templatesに説明があります。

Unityの下記フォルダにデフォルトのテンプレート群が含まれていて、テンプレートのフォルダをプロジェクトのAssets/WebGLTemplatesフォルダにコピーして改造することで、アプリケーションにあわせてページをカスタマイズできます。

C:\Program Files\Unity\Hub\Editor\{Unityのバージョン}\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\WebGLTemplates

表示をウィンドウ全体に拡げるには

Unity 2021.2以降のテンプレートを、Unityの画面がPCとスマートフォンでブラウザのウィンドウ全体に表示されるように改造してみます。上記フォルダからDefaultフォルダをWebGLTemplatesフォルダにコピーして、TemplateData/style.cssに以下を追記します。

/* Unityの画面をウィンドウ全体に拡げる */
#unity-container, #unity-canvas {
    width: 100%;
    height: 100%;
}

/* フッターのロゴ等を隠す */
#unity-footer {
    display: none;
}

/* 縦スクロールバーを隠す */
body {
    overflow: hidden;
}

さらにindex.htmlにiPhone/Android対応のための箇所があるのですが、ウィンドウの拡大に支障があるのと、ページがちらついたりと不都合なので消してしまいます。

if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
  ...
}

の条件式をelse節を含めてすべて消して、headタグの中に

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

を追加してみてください。

.wasm/.dataファイルの圧縮

Unityが出力する.wasmファイルや.dataファイルはそこそこ巨大になります。ウェブサーバーから配信する際に、GzipやBrotliで圧縮するとネットワーク転送量およびダウンロードにかかる時間が小さくなります。UnityではProject Settings > Playerの設定によって出力ファイルが以下のように変わります。

Decompression Fallbackがオンの場合

*.data.unityweb
*.framework.js.unityweb
*.wasm.unityweb

Decompression FallbackがオフでCompression FormatがGzipの場合

*.data.gz
*.framework.js.gz
*.wasm.gz

Decompression FallbackがオフでCompression FormatがBrotliの場合

*.data.br
*.framework.js.br
*.wasm.br

Compression FormatをGzipにすると、Uncompressedのときに比べて出力サイズが4分の1くらいに小さくなります。Brotliにするとさらに4分の3くらいのサイズになりますが、圧縮にとても時間がかかるため開発中は使用しないほうがいいかもしれません(ビルドの最終段でシングルスレッドのbrotli.exeで長時間止まります)。

注意点として、Gzip・Brotli圧縮したファイルをウェブサーバーで配信するときにはContent-Encodingヘッダを付与する必要があります。Decompression FallbackをオンにするとContent-Encodingヘッダなしでも読み込めるようになりますが、ブラウザで表示するときに展開に時間がかかります。

Content-Encodingヘッダの設定方法は配信手段によって異なるので調べてください。下記はAWS S3に.brファイルのみContent-Encoding: brを付与してアップロードするコマンドの例です。

aws s3 sync . s3://example.com/ --exclude "*.br"
aws s3 sync . s3://example.com/ --exclude "*" --include "*.br" --content-encoding "br"

実行環境を取得するには

SystemInfo.operatingSystemでOS名が取得できます(“Windows 10”、“iPhoneOS 15.5”、“Android 12"等)。

SystemInfo.deviceModelでブラウザのバージョンが取得できます(“Chrome 102.0.0.0”、“Safari 15.5"等)。

SystemInfo.deviceTypeでPC(Desktop)とモバイル(Handheld)の判別ができるはずなのですが、Unity 2022.1現在、iPhone・AndroidやDevice Simulatorで動かしていてもDesktopが返ってくるので注意です(Unity 2021からの不具合?)。

設定をスクリプトで変更するには

エディタ拡張のメニューやCIでビルドする際に、スクリプトから圧縮フォーマットを変更するには以下のようにします。

PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Brotli;
PlayerSettings.WebGL.decompressionFallback = false;

テンプレートは以下の要領で変更できます(「PROJECT:」が必要です)。

PlayerSettings.WebGL.template = "PROJECT:[フォルダ名]";

ビルドを速くするには

ビルド速度に影響する設定を以下に列挙します。

Compression Format

Project Settings > PlayerのCompression FormatをBrotliにすると圧縮に非常に長い時間がかかります。BrotliにするとビルドサイズがGzipの4分の3ほどに縮まりますが、開発中はGzipにしておくのがおすすめです。

スクリプトからはPlayerSettings.WebGL.compressionFormatで変更できます。

IL2CPP Code Generation

Project Settings > PlayerのIL2CPP Code GenerationをFaster runtimeからFaster (smaller) buildsに変更するとIL2CPPのコンパイル速度が倍くらい速くなります。実行時のCPU処理時間が数割ほど増えるようです。

Code Optimization

Unity 2022.2以降ビルドがとても遅くなっていて、「Linking build.js (wasm)」のダイヤログ表示で長時間止まります。これは、Unity 2022.2からEmscriptenが3.1.8に更新され、EmscriptenのLTO(リンク時最適化)に時間がかかっているためです。

対策として、Unity 2022.2.3以降およびUnity 2023.1以降では、Build Settingsの「Development Build」をオンにするか、Code Optimizationの「Shorter Build Time」を選択するとビルド時間が大幅に短くなります。Development Buildが最も速いためUnity公式ではイテレーションのために推奨されていますが、サイズがとても大きくなります。

Unity 2023.1のリリースノートより:

WebGL: Improved WebAssembly build options to avoid long link times. Added a new Release build option that is focused on faster build speeds, but note that Development builds are the fastest for iteration. (UUM-15615)

各設定でのWASMのサイズは以下のようになります(一例)。

  • Shorter Build Time 6433KB
  • Runtime Speed 7259KB
  • Disk Size 5624KB

UnityのBuild Settingsの設定がそれぞれ以下のEmscriptenの最適化オプションに対応しているとのことです。

Build Settingsの設定 Emscriptenの最適化オプション
Development Build -O1
Code Optimization: Shorter Build Time -O2
Code Optimization: Runtime Speed -O3
Code Optimization: Disk Size -Os

Code Optimizationの設定をスクリプトから変更するには

Unity 2023.2時点では専用のAPIが存在しないのですが、以下の要領で変更できます。

enum CodeOptimization
{
    BuildTimes,
    RuntimeSpeed,
    RuntimeSpeedLTO,
    DiskSize,
    DiskSizeLTO,
}

EditorUserBuildSettings.SetPlatformSettings(BuildPipeline.GetBuildTargetName(BuildTarget.WebGL), "CodeOptimization", CodeOptimization.BuildTimes.ToString());

設定を読むには以下の要領です。

EditorUserBuildSettings.GetPlatformSettings(BuildPipeline.GetBuildTargetName(BuildTarget.WebGL), "CodeOptimization");

ビルドサイズを小さくするには

Unityのビルドサイズを小さくするには」を参照してください。

URP、TextMesh Pro、Input System、UI Toolkitあたりは依存モジュールや付随するアセットが多く、使用するとサイズが大きくなりがちなので避けたほうがいいかもしれません。

テクスチャフォーマットについて

Unityのドキュメントの下記ページに、プラットフォームごとの対応テクスチャフォーマットの表(「Supported texture formats, by platform」)があります。

ページのUnity 2021.2の版から、WebGLの列が「WebGL (Desktop Browsers)」と「WebGL (iOS and Android browser)」に分かれています。

困ったことに、PCブラウザとモバイルブラウザで対応しているテクスチャフォーマットが異なります。ブラウザが対応していないフォーマットのテクスチャを読み込むと、「format is not supported, decompressing texture」の警告ログが出てテクスチャがソフトウェアで展開され、アプリの起動に時間がかかり、メモリも大きく消費しますので、どのフォーマットを使用するか検討する必要があります。

Unity 2021.2.13でリニアカラースペースのときに各フォーマットのテクスチャをビルドして読み込むと以下のようになりました。

RGB(A) Compressed ASTC

プラットフォーム 結果
iPhone 12 Pro 警告が出ない
Android (Pixel 4a) 警告が出ない
Windows Chrome 警告が出る
macOS Safari 警告が出ない

RGBA Compressed ETC2 / RGBA Crunched ETC2

プラットフォーム 結果
iPhone 12 Pro 警告が出る
Android (Pixel 4a) 警告が出ない
Windows Chrome 警告が出る
macOS Safari 警告が出る

(ドキュメントの表ではiPhoneのETC2圧縮はyesになっているのですが……)

RGBA Compressed DXT / RGBA Crunched DXT

プラットフォーム 結果
iPhone 12 Pro 警告が出る
Android (Pixel 4a) 警告が出る
Windows Chrome 警告が出ない
macOS Safari 警告が出ない

上記の結果から、以下のようにするのがよさそう(?)です。

  • iPhone・Androidに対応するならBuild SettingsのTexture CompressionをASTCにする。また、個別のインポート設定はRGB(A) Compressed ASTCを使用する。圧縮率最優先ならRGBA Crunched ETC2を使ってもよさそう(?)
  • Windows・Macのみに対応するならBuild SettingsのTexture CompressionをDXTにする。また、個別のインポート設定はRGBA Compressed DXTまたはRGBA Crunched DXTを使用する
  • 非圧縮でなければならないテクスチャはRGBA16bit等を使う(PC、モバイルどちらも対応している)。
  • PCとモバイル両方に最適化するなら実行ファイルを分けてそれぞれテクスチャフォーマットを変えてビルドする

参考として、比較的簡単なプロジェクトでBuild SettingsのTexture CompressionをDXTからASTCに変更した場合に、テクスチャ読み込み時間がiPhone 12 Proで6秒から1秒に、PCで1秒から2秒に変わりました。

なお、Unity公式のロードマップに「Basis Universal Texture Support」があります。Basis Universalはさまざまなテクスチャフォーマットに変換するための中間フォーマットで、将来的にはこれを利用してすべてのブラウザ環境に対応するものと思われます。

デバッグ方法

ブラウザの開発者コンソール

ブラウザの開発者用コンソールにエラーやDebug.Logの出力が表示されます。Chromeでは右上のメニューからその他のツール > デベロッパーツールを開いてConsoleタブを開きます。

iPhoneのSafariは、Macに接続すれば開発者コンソールを開けます。iPhoneの 設定 > Safari > 詳細 > Webインスペクタ(Settings > Safari > Advanced > Web Inspector)をオンにして、macOSのSafariのDevelopメニューから端末名 > Automatically Show Web Inspector for JSContexts、Connect via Network等をオンにしてiPhoneでページを開くとWeb Inspectorが開きます。

(2023年4月)以前はアプリ内のWebViewのページもWeb Inspectorで見ることができたのですが、iOS 16.4以降ではデフォルトで開けなくなりました。アプリ側で以下のように明示的に有効にする必要があります。

webView.isInspectable = true

AndroidのChromeも同じくリモート接続ができます(詳細略)。

謎のエラーダイアログで止まる

ブラウザでの実行時にエラーダイアログが出る場合、Build Settings > Development BuildおよびProject Settings > Debug Symbolsを有効にしてビルドすると、どこでクラッシュしているか分かるようになります。

SRDebugger

実行時にコンソールを開けるSRDebuggerというアセットがありますが、WebGLビルドでも使用できてスマートフォン等でも使えるので便利です(画面の左上隅を3回タップするとコンソールが開けます)。なお、インポートするとビルド後の出力サイズが1MBほど増えます。

パフォーマンス最適化

Unityのプロファイラを使用する

UnityのプロファイラはWebGLターゲットでも使用できます。Build SettingsでDevelopment BuildとAutoconnect ProfilerをオンにしてBuild And Runで実行するとプロファイラが開きます。

  • Timeline表示にすると1フレームのCPUの処理内容が確認できます(0.1ms単位でレポートされるようです)。
  • GPUプロファイリングには対応していません。
  • Renderingの表示列で、ドローコール・SetPassコール、頂点数、テクスチャ容量等を確認できます。

なお、Unity 2022.1では、以下のメッセージがコンソールに大量に出力されてプロファイラが使用できない不具合があります(Unity 2022.1.12で修正されてる?)。

Connection [number] is no longer valid. Calling auto disconnect.

ブラウザのプロファイラを使用する

ブラウザに搭載されている開発者向け機能を使用して、Unityが出力するWebAssemblyのパフォーマンス計測ができます。特に、UnityでBuild SettingsのDevelopment BuildをオンにしてビルドするとC#のメソッド名が見えるようになります。

また、プロファイルを開始してからページを読み込むと起動の待ち時間の詳細を追うことができます。

Chrome

PCでは、デベロッパーツール(右上のメニューボタン > その他のツール > デベロッパーツール)を開いてPerformanceタブを開き、左上の赤い丸をクリックして数秒ほど待って停止すると結果が表示されます。

Safari

macOSのSafariでは、Deveop > Show Web InspectorでTimelinesタブを開いて同じく左上の赤い丸をクリックして止めるとプロファイリングができます。JavaScript & Eventsタブで、各フレームのCPU実行時間を確認できます(「Animation Frame [num] Fired」からUnityが出力したWebAssemblyが呼ばれています)。

iPhone/iPadでもUSB接続したmacOSのSafariのWeb Inspectorで実行時間の詳細を確認できます。

フレームレートを下げるには

QualitySettings.vSyncCountを0にしてApplication.targetFrameRateでフレームレートを設定できます。Chrome(Windows、Android)とSafari(iOS、macOS)で、5~60くらいの範囲で設定したフレームレートで動作することを確認しています(Unity 2022.1.24、2022.3)。

また、フレームレートを下げるとiPhone・Androidで動作が抑制されて端末が冷えることを確認しています。

描画解像度を下げるには

Assets/WebGLTemplatesフォルダに入っているテンプレートのindex.htmlで、configを作成しているところにdevicePixelRatioを設定します(各デバイスのデフォルト値を把握していないので要調査)。

var config = {
  ...
  productName: "{{{ PRODUCT_NAME }}}",
  productVersion: "{{{ PRODUCT_VERSION }}}",
  devicePixelRatio: 1,
};

Power Preferenceについて

Unity 2022.1.19からProject SettingsにPower Preferenceの設定が追加されています。GPUを複数搭載しているノートPCで、Low Powerにすると統合GPUを使用するようブラウザに指示します。Chrome 80(Mac版のみ?)からデフォルトがLow Powerになっていて、High Performanceを明示的に設定しないと(Unity 2022.1.19以降にしないと)専用GPUが使えないとのことです。この設定はあくまでブラウザへのヒントなので、実際の挙動は要調査です。

参考:WebGL Specification - 5.2 WebGLContextAttributes

PlayerPrefsの保存場所について

PlayersPrefsのデータはブラウザのIndexedDBに保存されます。

Chromeだとデベロッパーツールを開いてApplicationタブのIndexedDBのところで確認できます。「Delete database」で初期化できます。

不具合・トラブルのメモ

iOSでフレームレートが出ない

iOS 15.4からWebGLがデフォルトでMetalを使用するようになっています(Settings > Safari > Advanced > Experimental WebKit Fieaturesの「WebGL via Metal」)。

WebKitはWebGLの描画にGoogleのANGLEを使用しているのですが、ANGLEのMetalバックエンドに、大きなUniform Bufferを使っているとパフォーマンスが非常に遅くなる不具合があるようです。ANGLE側では2023年2月に修正されており、iOSにいつ降りてくるかというステータスのようです。

iOS 16で文字表示時に固まる

iOS 16で、ダイナミックフォントを表示するときに秒単位でフリーズする現象が発生しています。フォントアセットのCharacterにDynamicを使用しないようにします。代わりにCharacter Set等を使用します。

iPhoneでクラッシュする

iOS 15.4にて、UnityのWebGLビルドで下記のようなエラーダイアログが表示されて動作が停止する不具合が発生しています。

RuntimeError: call_indirect to a null table entry (evaluating 'dynCall_iiii(index,a1,a2,a3)')...

RuntimeError: Out of bounds memory access (evaluating 'asm[name].apply(null, arguments)')<?>.wasm-function[il2cpp::vm::GlobalMetaData::GetContainerDeclaringType...

RuntimeError: Out of bounds memory access (evaluating 'dynCall_ii(index,a1)')<?>.wasm-function[728]@[wasm code]...

iOS Safari側の問題らしく、WebKitのWebAssemblyのロードに関連しているようです。Unityのエンジニアより、IL2CPPの特定の関数についてClangの最適化を無効にする回避策が発見されています。手元ではこの方法で動作するのを確認しました。

Windowsでは、以下のファイルを開き、

C:\Program Files\Unity\Hub\Editor\{Unityのバージョン}\Editor\Data\il2cpp\libil2cpp\metadata\GenericMetadata.cpp

「const Il2CppType* GenericMetadata::InflateIfNeeded」関数を検索して、前後を「#pragma clang optimize off」と「#pragma clang optimize on」で囲って管理者権限で保存します。修正後、確実に再コンパイルするにはプロジェクトのLibrary/Beeフォルダを削除します。

iPhoneでApplication.OpenURLが効かない

JavaScriptでページ移動をすればオーケーです。「UnityからJavaScriptの関数を呼ぶには」を参照してください。

iPhoneでフレームがきちんとクリアされない

MSAAを有効にしていると発生するようです(Unity 2021.2、2022.1で確認)。解像度が十分に高いので、iPhoneではMSAAはオフにしておくのがよさそうです。

モバイルでソフトウェアキーボードが表示されない

Unity 2022.1からInputFieldでソフトウェアキーボードが表示されるようになっています。

なお、iPhoneでソフトウェアキーボードを開いて閉じるとページの下に数ピクセルの白い線が残る現象が起きています。Unity 2022.1.14のリリースノートに下記項目があるのですが、直っていない……。

WebGL: Fixed a bug where the soft keyboard would leave whitespace at the bottom of the page after being dismissed. (UUM-1159)

Standaloneビルドと比べて描画品質が低い

WebGLビルドのときだけ影がギザギザになったりしている場合は、Project Settings > Qualityをチェックしてみてください。WebGLビルドはデフォルトで低めの描画品質に設定されています(Unityに慣れていないと盲点だと思います)。

Post ProcessingのAmbient Occlusionを使用するとエラーが出る

コンソールに以下のエラーが出る場合があります。

Kernel 'MultiScaleVODownsample1' not found
ArgumentException: Kernel 'MultiScaleVODownsample1' not found.

Ambient OcclusionのMulti Scale Volumetric Obscuranceモードはコンピュートシェーダーを使用しているため、WebGLターゲットでは動作しません。Post-process VolumeのAmbient OcclusionのModeをScalable Ambient Obscuranceに変更してください。

コンソールにエラーが出続ける場合、CameraのPost-process Layerをいったん無効にして再度有効にしてみてください。

Textコンポーネントで日本語フォントが表示されない

Textコンポーネントで、デフォルトのArialでは日本語が表示されません。プロジェクトに(権利的に使用可能な)日本語フォントファイルをインポートして指定してください。

なおWebGLビルドでは、Font.GetOSInstalledFontNamesはPCでもモバイルでも使用できない(空配列が返ってくる)ようです。

マルチタッチでOnPointerUp/OnEndDragが送られてこない

Unity 2021.3で、マルチタッチ操作をしているときに指を離してもIPointerUpHandlerとIEndDragHandlerのイベントが発生せずボタン類が押しっぱなしになる現象が起きました。Unity 2022.1だと大丈夫のようです。

オーディオが停止しない

AudioClipのLoad TypeをDecompress On Load以外にしているとAudioSource.Stopで止まらない現象が起きました。

Standaloneビルドのときとライトの当たり方が違う

カメラが移動したときにライティングがパカパカ変わることがありました。Directional Lightが複数あったのが原因でした。

UniTaskで非同期メソッドをキャンセルするとクラッシュする

asyncメソッドをキャンセルしてOperationCanceledExceptionが発生したとき、Project Settings > PlayerのEnable ExceptionesがNoneになっているとブラウザのアラートダイアログが表示されます。Explicity Thrown Exceptions Only以上にしてください。

WebGLビルドでアンチエイリアスが効かない

Unity 2021.2でMSAAが有効にならない不具合がありました。Unity 2021.2.15で修正されたようです。

AssetBundleについて

AssetBundleはWebGLビルドでも使用できます。大きいゲームやアプリケーションでは、最初のシーンを軽量にして、AssetBundleで逐次追加のシーンを読み込むようにすると起動が速くなります。

AssetBundleのロード時に固まる

Unity 2021.3時点ではWebGLビルドはマルチスレッドに対応しておらず、AssetBundleのダウンロード完了時にデータを一気に展開しメインスレッドをブロックします。このためリアルタイムゲーム等のインゲーム中にバックグラウンドでAssetBundleの読み込みを行うのは難しいです。

WebGL does not support threading, but http downloads only become available when they have finished downloading. Because of this, Unity WebGL builds need to decompress AssetBundle data on the main thread when the download is done, blocking the main thread.

なお、PlayerSettings.WebGL.threadsSupportというexperimentalなAPIがありますが、これを有効にしてしまうと以下のエラーでビルドが通らなくなります

Internal build system error. BuildProgram exited with code -2147024809.

修復するには、ProjectSettings.assetのwebGLThreadsSupportを0に戻します。

マテリアルが正常にロードされない

エディタがWebGLプラットフォームのときには、WebGLプラットフォームではなくStandaloneプラットフォームでビルドしたAssetBundleをロードする必要があります。

Addressable Asset Systemは使用できる?

AddressablesはWebGLプラットフォームでも使用できるようです。wasmのサイズがいくらか大きくなります(TODO:ある程度数字を出す)。

前述の問題で、WebGLのバンドルをリモートから読み込むとマテリアルが剥がれます。Use Asset Databaseを使うか、Standloneプラットフォームでバンドルをビルドして再生します。

異なるドメインからAssetBundleをロードするには

ブラウザのセキュリティにより、異なるドメインのウェブサーバーからAssetBundleをロードする場合、ウェブサーバーでCORS(Cross-Origin Resource Sharing)の設定をしてアクセスを許可する必要があります(配信方法によって異なるのでぐぐってください。たとえばAmazon S3だとbucketのPermissionsの下にCORSの設定があります)。

CORS有効のローカルウェブサーバーを立てたい場合、Node.jsのhttp-serverを使うのが簡単です。

$ npm install -g http-server
$ http-server -p 8080 --cors

AssetBundleで使用しているコンポーネントが読み込まれない

AssetBundleをロードした際にブラウザのコンソールに以下のようなエラーが出る場合は、AssetBundleで使用しているコンポーネントのコードがProject SettingsのStrip Engine Codeで削除されてしまっています。

Could not produce class with ID 220.
Could not produce class with ID 205.

IDの番号から該当のクラスを確認して、link.xmlという名前のファイルをAssetsフォルダ内に作成して指定すると保持されるようになります。例えば上記の220番と205番であれば、

<linker>
    <assembly fullname="UnityEngine">
        <type fullname="UnityEngine.LODGroup" preserve="all"/>
        <type fullname="UnityEngine.LightProbeGroup" preserve="all"/>
    </assembly>
</linker>

のようにすると解消されます。Unityの公式ドキュメントではこちらに説明があります。

JavaScriptプラグイン(.jslib)の書き方

JavaScriptのプラグインを作ることができます。UnityのWebGLビルドではできない処理や、手間のかかる処理をブラウザ側で実行し、相互のやり取りができます。

Unity公式では下記ページに最低限の説明があります。

公式の情報が少ないのですが、jslibはEmscriptenの仕組みをほぼそのまま用いているというのが重要ポイントです。

gtk2kさんの記事が非常に詳しくおすすめです。

以下は個人的なメモです。Unity 2021.2以降(Emscripten 2.0.19)を想定しています。

Unityから.jslibの関数を呼ぶには

.jslibを使用する最も基本的な方法は下記の通りです。下記ファイルをPluginsフォルダに配置してBrowser.OpenURLでブラウザのページが遷移します。

(Browser.cs)

using System.Runtime.InteropServices;

public class Browser
{
    [DllImport("__Internal")]
    public static extern void OpenURL(string url);
}

(Browser.jslib)

mergeInto(LibraryManager.library, {
  OpenURL: function(url) {
    window.open(UTF8ToString(url), '_self');
  },
}

.jslib側での文字列の受け取りにUTF8ToString関数を使用しています。

ビルドしたコードにJavaScriptを埋め込むには

拡張子.jspreのJavaScriptファイルをPluginsフォルダに置いておくと、ビルド後のJavaScriptコードに含まれます。

Visual Studio Codeでの.jslib/.jspreの扱いについて

JavaScriptはVisual Studio Codeで書くのがおすすめです。.jslib/.jspreファイルがJavaScriptとして認識されるように設定しておきましょう。.jslib/.jspreファイルを開き、右下に表示されているファイル種別をクリックして、Cofigure Flie Association for ‘.jslib’…でJavaScriptを選択します。

プラグインを書くときに参考になるコード

Unity本体に、UnityのAPIで使用されているjslibや、EmscriptenのJavaScriptコードが含まれていて、jslibを自作するときの参考になります。以下の場所にあります。

C:\Program Files\Unity\Hub\Editor\{version}\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\lib
C:\Program Files\Unity\Hub\Editor\{version}\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\src\library_*.js

.jslibの関数間で変数を共用したい

以下の要領でプライベートアクセスの変数を使用できます。

var LibraryWebGL = {
  $local: {
    name: null,
  }
    
  Initialize: function () {
    local.name = 'korinVR';
  },
};

autoAddDeps(LibraryWebGL, '$local');
mergeInto(LibraryManager.library, LibraryWebGL);

.jslibの関数からページのJavaScriptを呼ぶには

WebGLTemplatesフォルダのテンプレートページで以下のようにグローバルスコープの関数を定義すると、

hello = function() {
  console.log('Hello, Unity!');
}

.jslibの関数から通常通り呼び出すことができます。

mergeInto(LibraryManager.library, {
  Hello: function() {
    hello();
  },
};

またこれの延長で、.jslibはUnityとのやり取りに留めておいて、実際の処理はページのJavaScriptを呼び出すようにすると、ビルドしなくてもJavaScriptを書き換えて動作を変えられるのでイテレーションが速くなって便利です。

ページからUnityの関数を呼ぶには

WebGLTemplatesフォルダのテンプレートページでcreateUnityInstanceの戻り値を保存して、SendMessageで呼び出すことができます。

var unityInstance = null;

createUnityInstance(document.querySelector("#unity-canvas"), {
  ...
}).then((instance) => {
  unityInstance = instance;
});
<button onclick="unityInstance.Module.SendMessage('MessageReceiver', 'Hello');">Hello</button>
using UnityEngine;

public class MessageReceiver : MonoBehaviour
{
    public void Hello()
    {
        Debug.Log("Hello");
    }
}

.jslibの関数からUnityの関数を呼ぶには

簡単な方法

.jslibからSendMessageを呼び出せます。

unityInstance.SendMessage(objectName, methodName, value)

手軽なのですが、特定の名前のゲームオブジェクトをシーンに配置する必要があり、また名前をうっかり変更すると動かなくなるデメリットがあります。

難しい方法

Unity公式のドキュメントでは説明されていませんが、{{{ makeDynCall(‘sig’, ‘ptr’) }}} (arg1, arg2);のような形式でC#のMonoPInvokeCallback属性のメソッドを呼び出せます。呼び出されるメソッドはあらかじめC#からjslibに教えておきます。

TODO:サンプルを追加

「sig」は以下のような形式の文字列です。

'v': void type
'i': 32-bit integer type
'j': 64-bit integer type (currently does not exist in JavaScript)
'f': 32-bit float type
'd': 64-bit float type

.jslibの関数からUnityに配列を渡すには

JavaScriptでEmscriptenのヒープを_mallocで確保してデータをストアし、C#のメソッドを呼んで_freeで解放します。C#側ではMonoPInvokeCallback属性のメソッドでMarshal.Copyして受け取ります。ポインタだけでは配列のサイズが分からないので教える必要があります。

TODO:サンプルを追加

TODO:Emscriptenのヒープ操作について

ページから.jspreの関数を呼ぶには

.jspreで以下のようにモジュールを定義して、

Module.FrameSynthesis = {};
Module.FrameSynthesis.Test = function () {
    console.log('Test');
};

WebGLTemplatesフォルダのテンプレートページでcreateUnityInstanceの戻り値を保存して、下記の要領で呼び出せます。

var unityInstance = null;

createUnityInstance(document.querySelector("#unity-canvas"), {
  ...
}).then((instance) => {
  unityInstance = instance;
});
<button onclick="unityInstance.Module.FrameSynthesis.Test();"></button>

ページから.jslibの関数を呼ぶには

以前はModule.asmLibraryArgで呼ぶことができたのですが、少なくともUnity 2022.1と2022.2ではなくなっている(undefinedになっている)ようです。SendMessageを使ってUnityの関数経由で呼び出すしかないかもしれません。

Pointer_stringifyで警告が出る

Unity 2021.2以降ではPointer_stringifyを使用するとブラウザコンソールに警告が出ます。UTF8ToStringに変更する必要があります。

その他のTips

動画の再生について

iOSでは動画の再生に諸々の制限があり注意です(詳細はそのうち)。

リニアカラースペースでVideo Playerのフレームレートが大幅に低下する問題が起きています。iOSで顕著です(Unity 2022.3.7と2023.1.7で修正が入ったようです)。

設定のBattery > Low Power Modeをオンにしていると動画が再生されないようです。

iOSのロックダウンモードについて

iOS 16からロックダウンモードというセキュリティを極限まで高める設定が登場し、これを有効にするとJITコンパイラ、WebGL、WebAssembly等ブラウザの高度な機能の多くが動作しなくなります(当然ながらUnityのWebGLビルドも動かなくなります)。Appleの公式ページで説明されているように、多くの一般の方は使用しない設定のはずですが、存在は知っておいたほうがよさそうです。

ブラウザのURLが知りたい

Application.absoluteURLで取得できます。ブラウザで実行しているかどうかの判定にも使用できます。

UnityのWebGLビルド関連コンポーネント

Unityの下記フォルダにWebGLビルドに関連するコンポーネントが含まれており、目を通しておくと面白いかもしれません。

C:\Program Files\Unity\Hub\Editor\{Unityのバージョン}\Editor\Data\PlaybackEngines\WebGLSupport
書いた人:こりん(@korinVR
» VR開発メモトップへ