はじめに
WebGL対応アプリケーションを開発した際にハマったポイントが多数あったので、ハマりポイントとその対策の記録を残します。
UnityやOSのバージョンによって以下記載事項に従っても上手く動作しない場合があります。
他の方法でも実装・対策できるよ!最新版では動作します!などの情報をお持ちの方がいらっしゃいましたらコメント欄に書き込み頂けると幸いです。
現在発生中の不具合
- Developビルド後にProfilerがつながらない。
Unity2021.3.8で解消されました。
https://issuetracker.unity3d.com/issues/backport-webgl-profiler-does-not-autoconnect-on-webgl-builds
各ブラウザ&OS毎のメモリ割り当て上限値
以下の記事にて調査記録を掲載
ローカルでアプリを動作させたい
以下のツール等を使うことによって簡単にローカルでindex.htmlファイルをホストすることができるようになるため、ツールを活用することでビルド後の動作検証等が楽になります。
Chromeの場合、Web Server for Chromeを利用することでローカルでWebGLアプリを簡単に動作させることができます。 2023/12/10現在Chrome拡張ツールは入手不可能になっているようです。
Androidでテクスチャが表示されない
6000.0.20f1時点ではForward+に設定するとAndroidでURP標準シェーダーが表示されないようです。
Forwardに設定すると表示されるようになるようです。
また、前述の6000.0.20f1ではForward+が標準設定になっていますので、デフォルト値から変更する必要があります。
Unityデフォルトのテキストフォント"Arial"を使用していると日本語フォントが表示されなくなる
標準外の日本語対応フォントをインポートすると日本語表示できるようになる。
ビルド後にInputFieldで日本語入力ができなくなる
WebGLInputパッケージを使用し、WebGLInputコンポーネントをInputFieldオブジェクトにアタッチすると入力できるようになる。
ブラウザをリサイズするとTextコンポーネントの文字が消える
TextMeshProのTextを使用すると消えなくなる。
Addressable を利用する際にデフォルトのLZMAが非対応のため展開に失敗する
圧縮形式をLZ4形式に変更すると展開できるようになる。
ブラウザから別のタブでWebサイトを開く機能が使用できない
従来は標準でAPIが用意されていたがUnity2020から?非サポートになったらしい。
以下のWebサイトに従いJavaScriptを呼び出すことで、別のタブから指定URIのWebサイトを開くことができるようになる。
標準のマイク入力APIが使用できない
Unity標準機能ではマイク入力用APIのUnityEngine.Microphoneを使用することができない。
以下のスクリプトをシーンに配置すると利用できるようになる。
AudioSourceの3Dサウンドを使用できない(VideoPlayerの音声のみ?)
AudioSourceのSpatialBlendによる3Dサウンド設定を利用できないため、AudioListenerとの距離によってボリュームを調整するスクリプトを追加する必要があります。
AudioSource .GetOutputDataを使用できない
シングルスレッドでしか動作しないため、本機能は利用できないようです。
大体手段として下記のアセットを利用することで同等の操作を行えるようになります。
上記アセットの説明文によるとネイティブJavaScriptライブラリを使用することでWebAudioAPIにアクセスしているので、アセットを使わない場合はJS側で実現できるようです。
PhotonVoice2を使用できない
Photonサービスのボイスチャット機能についてもWebGLは非サポートなっている。
以下のアセットを組み合わせることで使用できるようになる。
■マイクを有効にするUnityアセット
■PUN2を利用してボイスチャットを使えるようにするリポジトリ
※Release1.0.0のバージョンだと繋がらなかったので、アップデートされるまでは最新のコミットを使った方が良いかも。
デモシーンにも不具合がありそうなのでフォークして改修したリポジトリはコチラ
公式で利用できないとする理由は以下の通り。
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に返す場合、引数は一つしか持てないので注意
- もう少し詳しく知りたい方はこちらが非常に参考になります。
VideoPlayerコンポーネントで再生できない
-
VideoClipで再生できない
- VideoClipはWebGLに非対応のため、URL指定で再生する必要があります。
-
再生するプラットフォームによって再生できない
2023/08/27 修正
動画H.264,音声AACのmp4ファイル
であれば多くのOSで再生することができるようです。
以下の記事に変換方法を記載しています。
-
ブラウザのセキュリティの影響で再生できない場合
ブラウザ上での動画再生にはブラウザのセキュリティの関係上、事前にアプリ起動後に画面をタップする必要があります。- 動画が再生される前に事前にどこかをタップ(クリック)する設計にする
- PlayOnAwakeをfalseに設定し、任意のタイミングでPlay()等で再生開始する。
-
上記の対策を行っても再生されない場合
- (iOSの場合)OSのアップデート
- 全トラックをMuteに設定する
- AudioOutputModeをNoneに設定する
-
それでも再生できない場合
手間ですがVideoコンポーネントを使わずにHTMLのVideoタグからテクスチャを受け取ることで動画を再生させることもできます。以下のリポジトリを参考にしてみて下さい。
ビルド後一部のOS,機種で動作しない
デフォルト設定ではWebGL2で動作するようになっていますが、WebGL2は現時点で対応している環境が限られていますので、WebGL1に変更することによる対応できる機種・OSを増やすことができます。
詳細は元Unityの安原さんの記事を参照のこと。
後日検証したところ同じアプリ起動後でも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.
ファイル読み書き関連
StreamingAssetsに配置したファイルはIO.Fileで取得できない
Android や WebGL など、ストリーミングアセットファイルに直接アクセスできないプラットフォームでストリーミングアセットを読み取るには、UnityWebRequest を使用します。例は、Application.streamingAssetsPath を参照してください。
上記の公式記載の通り直接のアクセスができないため UnityWebRequest
で取得する必要があります。
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のパスを取得し、その配下にファイルを書き込む方法があります。
実際の手順はコチラの記事が参考になります。
※"Application.persistentDataPath"直後のスラッシュ"/"以外にスラッシュを入れることができませんので注意が必要です。
Compute shaderが動作しない
Unityの制限ではなく OpenGL ES 3.0
側 の制限で動作しません。
JavaScriptからのアクセス
JavaScriptからはファイルシステムにアクセス可能です。
ファイルダイアログを呼び出し、読み書きされている方がいらっしゃいましたので、こちらのソースコードを参考にするとよいと思います。
他にもローカルサーバーを立てる方法がありますが、実際にユーザーにサーバーを立てさせるシーンが想像できないので用途は限定されると思います。
別ドメインのリソース(画像・動画など)を取得できない
通常のWebサイトであれば、別ドメインかつCORS許可していないリソースであってもimgタグ
やvideoタグ
に表示させることは可能ですが、UnityWebGLアプリはCanvasタグ上で動作するためCORSの制約を受け、別ドメインのリソースを表示しようとするとblocked by CORS policy
エラーが発生します。
これを回避するためには自身が管理するサーバーにリソースを配置、サーバーのCORS許可設定を許可してからアクセスする必要があります。
※scriptタグならCORSの影響を受けないのでjslibで取得することができるかも?※未検証
アプリを最新のものに差し替えたらエラーがでるようになる
多くの場合、何らかのキャッシュが残っている場合があります。
- ブラウザ側のキャッシュ
- アプリホスティング環境側(バックエンド)のキャッシュの更新
- GCS,S3,Blob等のクラウドストレージにホスティングシている場合は各クラウドの設定方法に従いキャッシュを保存しないように設定するか、更新時に都度キャッシュが削除されるようにしておく
- CloudFrontのようなCDNを利用している場合も同様にアプリ更新時にキャッシュがクリアする
〇〇〇のSDKやライブラリがWebGLビルドのみ対応していない
何らかの理由でUnity向けのSDKがWebGLのみ対応していないケースが無数に存在します。
そんな場合はJavaScriptに対応しているかを確認し、WebFront側からSDKを利用するような仕組みで実装する必要があります。
過去にFireBaseに接続するための方法を記録した記事を書いているので、よろしければ参考にしてみてください。
Next.JSのWebアプリにUnityを組み込みたい
React Unity WebGLを使うのが一般的です。
組み込み後はUnity→jsの方向だとjslibを介してメソッド名を指定してメッセージを送信、js→Unityの方向だとGameObject名&メソッド名を指定してメッセージを送信することも可能です。