【Unity】WebGL開発ハマりポイント

本記事は [オルトプラス Advent Calendar 2023](https://qiita.com/advent-calendar/2023/altplus2023) の12/17の記事です。

# はじめに
こんにちは！オルトプラスでクライアントエンジニアをしている渡辺です。
約1年半ほどUnityでWebGL向けのゲーム開発をしております。
今回はWebGL開発中に遭遇したハマりポイントとその解決方法についてご紹介したいと思います。

# 概要
- 背景を透過させたい
- iPad判定がされない
- キャッシュが残ってアプリが更新されない
- iOS対策
- サウンドが再生されない

# 詳細

### 背景を透過させる
Webページの上に別のページのWebページを重ねて表示させたい場合があります。
さらに重ねる方のページは元々表示していたページより小さく表示する場合は少し工夫が必要です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3592193/064dded8-1fe4-2b30-ab61-5f0b62923e6c.png" width=300>

私が検討した方法は下記になります。
- 背景を透過する
＝＞特定のシーン（ページ）でのみ適用できる
- ページそのもののサイズを小さくする
＝＞全てのシーン(ページ)のサイズが小さくなる

要件的に特定のページでのみ小さく表示したかったので、背景を透過させる方法を採用することにしました。

<table>
    <tr>
        <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3592193/c5629667-e939-243a-6990-69987f6e396f.png" width=200></td>
        <td>+</td>
        <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3592193/043731b0-ea24-3c04-e4ac-7b9ad223ed91.png" width=200></td>
        <td>=</td>
        <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3592193/064dded8-1fe4-2b30-ab61-5f0b62923e6c.png" width=200></td>
    </tr>
</table>

実際に背景を透過させる方法は下記になります。

#### カメラ設定
- Clear Flag：Solid Color
- Background：RGBAをすべて0

#### プラグイン追加
「Plugins」フォルダに下記のプラグイン(clear.jslib)を追加

```js
var LibraryGLClear = {
    glClear: function(mask)
    {
        if (mask == 0x00004000)
        {
            var v = GLctx.getParameter(GLctx.COLOR_WRITEMASK);
            if (!v[0] && !v[1] && !v[2] && v[3])
                return;
        }
        GLctx.clear(mask);
    }
};
mergeInto(LibraryManager.library, LibraryGLClear); 
```
プラグインの詳細はこちらになります。
https://forum.unity.com/threads/webgl-transparent-background.284699/#post-1880667

### iPad判定
起動画面でアプリが実行されている端末の種類を判定して設定を変えたい場合があります。
特にiPadの場合はアスペクト比等の対応で処理を分ける必要があります。
WebGLでは起動画面はhtmlファイルで描画するため、Javascriptで端末判定を行う必要があります。
JavascriptによるiPadの判定方法がいくつかありますが、私が使用している判定方法は下記になります。

■iPad判定
OSのバージョンによってユーザーエージェントが「Macintosh」になってしまう場合があるため条件に追加
```js
var isIPad = ua.indexOf('iPad') > -1 ||
    (ua.indexOf('Macintosh') > -1 && 'ontouchend' in document);
```

■タブレット判定
```js
var ua = navigator.userAgent;
var isSmartPhone = ua.indexOf('iPhone') > -1 ||
    ua.indexOf('iPod') > -1 ||
    (ua.indexOf('Android') > -1 && ua.indexOf('Mobile') > -1);
var isTablet = !isSmartPhone && (
    ua.indexOf('iPad') > -1 ||
    (ua.indexOf('Macintosh') > -1 && 'ontouchend' in document) ||
    ua.indexOf('Android') > -1);
```

### キャッシュ対策
WebGLのアプリをそのまま更新すると前のバージョンのキャッシュが残ってしまう場合があり、アプリの更新ができず正常にアプリをプレイできないといった問題が発生する場合があります。

以前は起動するURLをバージョン毎に変更するといったやり方でキャッシュが残らないようにしておりましたが、下記の問題が発生しておりました。
- 起動URLをアプリ更新の度に毎回変更しないといけず、運用コストがかかる状態になっていた（メンテナンス対応やサービス提供元への起動URL変更依頼等）
- 起動URLが変わるとPlayerPrefsがリセットされてしまうため、PlayerPrefsの使用が限定されていた

そこで現在は「NameFilesAsHashes」オプションを使用することで、起動URLはそのままでビルド成果物のファイル名を動的に変更することでキャッシュが残らないようにしております。

#### NameFilesAsHashesの設定
ProjectSettingのNameFilesAsHashesにチェックを入れる
![NameFilesAsHashes設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3592193/9e92d6bf-e156-e78d-33a3-551248a2250a.png)

またBuild成果物の名前が動的になるためindex.html内のconfigの設定を下記のようにする必要があります。

■NameFilesAsHashesが**有効**の場合
```js
var config = {
    dataUrl: buildUrl + "/{{{ DATA_FILENAME }}}",
    frameworkUrl: buildUrl + "/{{{ FRAMEWORK_FILENAME }}}",
    codeUrl: buildUrl + "/{{{ CODE_FILENAME }}}",
};
```

■NameFilesAsHashesが**無効**の場合
```js
var config = {
    dataUrl: buildUrl + "/web-gl.data.gz",
    frameworkUrl: buildUrl + "/web-gl.framework.js.gz",
    codeUrl: buildUrl + "/web-gl.wasm.gz"
};
```



### iOS対策
iOSではOSのバージョンによってWebGLの動作が不安定になる場合があるので注意が必要です。

#### iOS16.0〜16.3で動作が重くなる
iOS16.0〜16.3で特定の処理で動作が重くなる問題が発生しておりました。(iOS16.4以降では解決されているため、WebGLでアプリを公開する際は16.4以上をサポートにするようにしています)
- Fontの設定で「DynamicFont」を設定している場合、フォントのテクスチャのサイズが更新されるタイミングでアプリが重くなる
＝＞回避策：フォントの設定を「CustomSet」に設定する（BestFit機能が使えなくなるデメリットあり）

![フォント設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3592193/90294cb1-c8bf-a81f-3cef-3520524c6eec.png)


- キャプチャ機能等でRawImageを使用すると処理が重くなる・固まる
＝＞回避策は見つからなかったため、iOS16.4以降をサポート対象にしました。

#### iOS15.4でアプリがクラッシュする
iOS15.4のSafariに問題があり、アプリを起動しようとするとクラッシュしてしまいます。
UnityForumでUnityのエンジニアが対応策を公開しておりますので、こちらをご参考にしてください
https://forum.unity.com/threads/ios-15-webgl-2-issue.1176116/#post-7982496

### サウンドが再生されない・画面をタップしないと再生されない
WebGLではデフォルトのサウンド設定のままの場合、サウンドが再生されなかったり、アプリ起動後に画面を一度タップしないとサウンドが再生されないことがあります。
またiOSのバージョンによって挙動が違う場合もあります。

■サウンド設定
![WebGL_BGM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3592193/a02385d2-af83-af5d-52f5-749acc3987a7.png)

- Load Type：Decompress On Load
- Preload Audio Data：入
- Compression Format：AAC

※iOS17未満の場合Load Typeが「Compressed In Memory」でも正常に再生されましたが、iOS17の場合は上記の設定にしないとサウンドの再生までに時間がかかってしまいます

# まとめ
WebGLはブラウザでプレイ可能なためネイティブアプリより気軽に色々な人に遊んでもらいやすいと感じています。ただ開発する際はWebGL特有の問題がいくつかあるため、ハマると怖い部分もあります。ハマりそうになった場合は今回参考にさせて頂いた下記のサイトを確認したり、UnityForumで検索・質問すると色々と情報が得られることがありますので試してみてください。

# 参考
- [UnityのWebGLで透過（HTMLの背景を表示）](https://qiita.com/kumatta_ss/items/b89b03d7b4892ec3c93f)
- [UnityでモバイルWebGLゲーム開発を頑張る話](https://tech.mirrativ.stream/entry/2023/08/14/105252)
- [Unity WebGLビルドメモ](https://tech.framesynthesis.co.jp/unity/webgl/)