【Unity】WebGL開発時の注意事項

# はじめに
WebGL対応アプリケーションを開発した際にハマったポイントが多数あったので、ハマりポイントとその対策の記録を残します。
 UnityやOSのバージョンによって以下記載事項に従っても上手く動作しない場合があります。
他の方法でも実装・対策できるよ！最新版では動作します！などの情報をお持ちの方がいらっしゃいましたらコメント欄に書き込み頂けると幸いです。

## 現在発生中の不具合

* Developビルド後にProfilerがつながらない。
Unity2021.3.8で解消されました。
<details>
``Warning spam: Connection X is no longer valid. Calling auto disconnect``が発生しProfilerに接続できない不具合が発生しているようです。
UnityEditorや他プラットフォームでビルドする等の方法はあるものの、Runtimeの違いによる差異が大きくなるケースが殆どなのでかなり不便な状態になっています。
良い代替手段があればコメントの方で教えて頂きたいです。
※Unity2021,2022では動作せず、Unity2020,2019最新版では動作するようです。
※MemoryProfilerはいずれのバージョンも動作しないようです。

https://issuetracker.unity3d.com/issues/backport-webgl-profiler-does-not-autoconnect-on-webgl-builds
</details>

## 各ブラウザ＆OS毎のメモリ割り当て上限値

以下の記事にて調査記録を掲載

https://qiita.com/kazuki_kuriyama/items/39a508f7c9ea5d08082b

2025/02/04 追記
iOS18.2にてメモリが一定以上に達するとクラッシュする不具合が解消されたようです。

https://discussions.unity.com/t/webgl-memory-increment-issue-and-crash-on-ios/894771/31


## ローカルでアプリを動作させたい
以下のツール等を使うことによって簡単にローカルでindex.htmlファイルをホストすることができるようになるため、ツールを活用することでビルド後の動作検証等が楽になります。

https://simplewebserver.org/


~~Chromeの場合、Web Server for Chromeを利用することでローカルでWebGLアプリを簡単に動作させることができます。~~ 2023/12/10現在Chrome拡張ツールは入手不可能になっているようです。

https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb?hl=en

## Androidでテクスチャが表示されない
6000.0.20f1時点ではForward+に設定するとAndroidでURP標準シェーダーが表示されないようです。
Forwardに設定すると表示されるようになるようです。
また、前述の6000.0.20f1ではForward+が標準設定になっていますので、デフォルト値から変更する必要があります。

## Unityデフォルトのテキストフォント"Arial"を使用していると日本語フォントが表示されなくなる
標準外の日本語対応フォントをインポートすると日本語表示できるようになる。

## ビルド後にInputFieldで日本語入力ができなくなる
WebGLInputパッケージを使用し、WebGLInputコンポーネントをInputFieldオブジェクトにアタッチすると入力できるようになる。

https://github.com/kou-yeung/WebGLInput

## ブラウザをリサイズするとTextコンポーネントの文字が消える
TextMeshProのTextを使用すると消えなくなる。


## Addressable を利用する際にデフォルトのLZMAが非対応のため展開に失敗する
圧縮形式をLZ4形式に変更すると展開できるようになる。

## ブラウザから別のタブでWebサイトを開く機能が使用できない
従来は標準でAPIが用意されていたがUnity2020から?非サポートになったらしい。
以下のWebサイトに従いJavaScriptを呼び出すことで、別のタブから指定URIのWebサイトを開くことができるようになる。

https://nekojara.city/unity-opennewtab

## 標準のマイク入力APIが使用できない<br>
Unity標準機能ではマイク入力用APIのUnityEngine.Microphoneを使用することができない。<br>
以下のスクリプトをシーンに配置すると利用できるようになる。 

https://github.com/tgraupmann/UnityWebGLMicrophone

## AudioSourceの3Dサウンドを使用できない（VideoPlayerの音声のみ？）
AudioSourceのSpatialBlendによる3Dサウンド設定を利用できないため、AudioListenerとの距離によってボリュームを調整するスクリプトを追加する必要があります。

## AudioSource .GetOutputDataを使用できない
シングルスレッドでしか動作しないため、本機能は利用できないようです。
大体手段として下記のアセットを利用することで同等の操作を行えるようになります。

https://assetstore.unity.com/packages/tools/audio/amplitude-for-webgl-111277

上記アセットの説明文によるとネイティブJavaScriptライブラリを使用することでWebAudioAPIにアクセスしているので、アセットを使わない場合はJS側で実現できるようです。

## PhotonVoice2を使用できない
Photonサービスのボイスチャット機能についてもWebGLは非サポートなっている。
以下のアセットを組み合わせることで使用できるようになる。
■マイクを有効にするUnityアセット

https://assetstore.unity.com/packages/tools/input-management/microphone-pro-webgl-mobiles-desktop-79989


■PUN2を利用してボイスチャットを使えるようにするリポジトリ
※Release1.0.0のバージョンだと繋がらなかったので、アップデートされるまでは最新のコミットを使った方が良いかも。

https://github.com/frostweep/PUN2VoiceWebGLUnity

デモシーンにも不具合がありそうなのでフォークして改修したリポジトリはコチラ

https://github.com/KazukiKuriyama/PUN2VoiceWebGLUnity

公式で利用できないとする理由は以下の通り。
> WebGLは以下の理由によりサポート対象のプラットフォームとはなっていません。
> WebGLはスレッドでうまく作動せず、Photon Voiceのエンコーディングとデコーディングにスレッドを使用しているため。　
UnityのマイクがWebGLでは使用できないため。
WebGLで作動するOpusコーデックライブラリビルドが必要であるため。
WebGL用のPhotonはWebSocketSecureプロトコルにのみ対応しているため。 Photon Voiceは、UDPを使用するほうが効率がいいです。

## マルチスレッド非対応
* WebGLビルドではマルチスレッドは動作しないため、非同期を利用する際は注意が必要です。
* コルーチンはシングルスレッドで動作するため問題なく使えます。
* UniTaskの場合、UniTask.Run()やUniTask.SwitchToThreadPool()などはマルチスレッドで動作するため利用することができません。（エラーも吐かず、何か動きがおかしい！！って状態になるので注意が必要）
* AWS用のSDKはこれが原因なのか動作しない。（動かせた方がいたらコメント頂きたいです。）".jslib"経由でJavaScript用のSDKを使うことは可能だが、C#との受け渡しがint,String,texture2Dしか使えないのでObjectを渡したい場合は、 ~~一旦Stringで受け渡しする必要がある。←C#ではJS側の非同期を待てないっぽいので削除~~ ここから更にC#に対してメッセージを送る必要がある。


## ブラウザ機能の呼び出し(JavaScript呼び出し)が必須の機能がある
* WebGLビルドに対応しているものの、ブラウザを制御するAPIで公開されていないものが多いため、必要な場合はJavaScriptを利用する必要がある。
* JavaScript側でしか実装できない機能の一例
    * 別タブを開く（Unity標準API非対応、window.openを使用する）
    * 現在地の取得（Unity標準API非対応、navigator.geolocation.getCurrentPositionを使用する）
    * AWS接続用SDK（C#用非対応、JS用を使用する）
    * WebSocketによる通信
* ``.jslib``内に関数を定義し、c#側で `` [DllImport("__Internal")] `` のアトリビュートを付与して宣言するとC#から呼び出せるが制約が多い。
* int,stringしか受け渡しができないので、Objectなどは一旦Stringに変換して渡すことになる。
* JSからUnityに返す場合、引数は一つしか持てないので注意

https://docs.unity3d.com/Manual/webgl-templates.html

* もう少し詳しく知りたい方はこちらが非常に参考になります。

https://gist.github.com/yano3nora/ab222f46abfbec85855b550130aa2b85

## VideoPlayerコンポーネントで再生できない
* VideoClipで再生できない
    * VideoClipはWebGLに非対応のため、URL指定で再生する必要があります。

* 再生するプラットフォームによって再生できない
2023/08/27 修正

`動画H.264,音声AACのmp4ファイル` であれば多くのOSで再生することができるようです。
以下の記事に変換方法を記載しています。

https://qiita.com/kazuki_kuriyama/items/04420e3b6b81a675f6a1

* ブラウザのセキュリティの影響で再生できない場合
    ブラウザ上での動画再生にはブラウザのセキュリティの関係上、事前にアプリ起動後に画面をタップする必要があります。
    * 動画が再生される前に事前にどこかをタップ(クリック)する設計にする
    * PlayOnAwakeをfalseに設定し、任意のタイミングでPlay()等で再生開始する。

* 上記の対策を行っても再生されない場合
    * (iOSの場合)OSのアップデート
    * 全トラックをMuteに設定する
    * AudioOutputModeをNoneに設定する

* それでも再生できない場合
    手間ですがVideoコンポーネントを使わずにHTMLのVideoタグからテクスチャを受け取ることで動画を再生させることもできます。

    以下のリポジトリを参考にしてみて下さい。

https://github.com/KazukiKuriyama/unity-webgl-video-ios

    

## ビルド後一部のOS,機種で動作しない
デフォルト設定ではWebGL2で動作するようになっていますが、WebGL2は現時点で対応している環境が限られていますので、WebGL1に変更することによる対応できる機種・OSを増やすことができます。
詳細は元Unityの安原さんの記事を参照のこと。

https://qiita.com/yuji_yasuhara/items/237a571b9ba8be3d0cbd

後日検証したところ同じアプリ起動後でもWebGL2の方がRAMの消費が多かったため、RAMが3GBにも満たないような古い機種だとWebGL1の方が対応できる機種が多くなると思います。

## Decompression Fallbackにチェックをつけても.unitywebファイルが生成されない

`Development Build` にチェックを入れている場合は `.unityweb` は生成されないようです。

## 描画関連
### GPU instancingが動作しない
WebGL1.0は対応していないため、WebGL2.0を使用する必要があります。

>Requirements and compatibility
>This section includes information about the platform, render pipeline, and SRP Batcher compatibility of GPU instancing.


https://docs.unity3d.com/ja/2022.2/Manual/GPUInstancing.html


## ファイル読み書き関連
### StreamingAssetsに配置したファイルはIO.Fileで取得できない
> Android や WebGL など、ストリーミングアセットファイルに直接アクセスできないプラットフォームでストリーミングアセットを読み取るには、UnityWebRequest を使用します。例は、Application.streamingAssetsPath を参照してください。

上記の公式記載の通り直接のアクセスができないため `UnityWebRequest`で取得する必要があります。

```cs
var url = Path.Combine(Application.streamingAssetsPath, targetPath);
            var www = UnityWebRequest.Get(url);
            await www.SendWebRequest();

            if (www.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError(www.error);
            }
            else
            {
                Debug.Log(www.downloadHandler.text);
            }
```


### C#からのアクセス
C#からはブラウザのセキュリティによって他プラットフォームのようにパスを指定したファイルシステムは使用できないため、IndexedDB(ブラウザ内に保存)を利用する必要があります。
その場合、"Application.persistentDataPath"からIndexedDBのパスを取得し、その配下にファイルを書き込む方法があります。
実際の手順はコチラの記事が参考になります。

https://qiita.com/mura-masa/items/8b1e6ca6ff3b0d3e40a6

※"Application.persistentDataPath"直後のスラッシュ"/"以外にスラッシュを入れることができませんので注意が必要です。

## Compute shaderが動作しない
Unityの制限ではなく `OpenGL ES 3.0` 側　の制限で動作しません。

https://docs.unity3d.com/2023.1/Documentation/Manual/class-ComputeShader.html

## JavaScriptからのアクセス
JavaScriptからはファイルシステムにアクセス可能です。
ファイルダイアログを呼び出し、読み書きされている方がいらっしゃいましたので、こちらのソースコードを参考にするとよいと思います。

https://github.com/ruccho/BlobIO

他にもローカルサーバーを立てる方法がありますが、実際にユーザーにサーバーを立てさせるシーンが想像できないので用途は限定されると思います。

https://a1026302.hatenablog.com/entry/2020/09/11/175538

## 別ドメインのリソース(画像・動画など)を取得できない
通常のWebサイトであれば、別ドメインかつCORS許可していないリソースであっても`imgタグ`や`videoタグ`に表示させることは可能ですが、UnityWebGLアプリはCanvasタグ上で動作するためCORSの制約を受け、別ドメインのリソースを表示しようとすると`blocked by CORS policy`エラーが発生します。
これを回避するためには自身が管理するサーバーにリソースを配置、サーバーのCORS許可設定を許可してからアクセスする必要があります。
※scriptタグならCORSの影響を受けないのでjslibで取得することができるかも？※未検証

## アプリを最新のものに差し替えたらエラーがでるようになる
多くの場合、何らかのキャッシュが残っている場合があります。
- ブラウザ側のキャッシュ
    - ブラウザが古いアプリファイルのキャッシュを読まないように `Name Files As Hashes` をONに設定する
    - アプリがキャッシュを保持しないように `DataCaching` をOFFに設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/211bb40f-42db-7549-1ae1-dc0abbc4530b.png)
    - ブラウザのデベロッパーツールを開いた状態で更新ボタンを長押しするとキャッシュクリアメニューが表示されるので `キャッシュの削除とハード読み込み`を実行する
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/8c682609-59bb-2614-7208-af65ca422108.png)
- アプリホスティング環境側(バックエンド)のキャッシュの更新
    - GCS,S3,Blob等のクラウドストレージにホスティングシている場合は各クラウドの設定方法に従いキャッシュを保存しないように設定するか、更新時に都度キャッシュが削除されるようにしておく
    - CloudFrontのようなCDNを利用している場合も同様にアプリ更新時にキャッシュがクリアする

## 〇〇〇のSDKやライブラリがWebGLビルドのみ対応していない
何らかの理由でUnity向けのSDKがWebGLのみ対応していないケースが無数に存在します。
そんな場合はJavaScriptに対応しているかを確認し、WebFront側からSDKを利用するような仕組みで実装する必要があります。
過去にFireBaseに接続するための方法を記録した記事を書いているので、よろしければ参考にしてみてください。

https://techblog.gaudiy.com/entry/2023/09/21/114248

## Next.JSのWebアプリにUnityを組み込みたい
React Unity WebGLを使うのが一般的です。
組み込み後はUnity→jsの方向だとjslibを介してメソッド名を指定してメッセージを送信、js→Unityの方向だとGameObject名＆メソッド名を指定してメッセージを送信することも可能です。

https://react-unity-webgl.dev/

https://qiita.com/kazuki_kuriyama/items/19207dd7d435ad25ed17