ガワネイティブなWebGLアプリをUnityで作った時の話

# 概要
あるネイティブアプリ上で動くゲームを作って欲しいという案件があり、いくつか調査をした上でUnity WebGLで作ることになりました。その際、詳細は伏せますが、アスペクト比を固定しないと、大きく破綻してしまう機能などがあることが発覚し、対応することになった話になります。

# アスペクト比を維持したスケーリング
概要にある通り、アスペクト比を固定する必要がでてきました。
WebGLTemplatesを作る事になるのですが、あまり元のテンプレートを弄らずに、スケーリング処理をするJavaScriptを追加することで対応を図りました。

``` javascript: resize.js
var aspect_with_height = 0.5625;
var aspect_with_witdh = 1.7777;

window.onload = function () {
    canvas = document.getElementById('unity-canvas');
    canvas.style.right = 0;
    canvas.style.left = 0;
    canvas.style.bottom = 0;
    canvas.style.top = 0;
    canvas.style.margin = 'auto';

    var setHeight = 0;
    var setWidth = 0;

    var scaledWidth = window.innerHeight * aspect_with_height;
    if( window.innerWidth < scaledWidth ){
        setWidth = window.innerWidth;
        setHeight = window.innerWidth * aspect_with_witdh;
    }else{
        setHeight = window.innerHeight;
        setWidth = scaledWidth;
    }

    canvas.width = setWidth;
    canvas.height = setHeight;
};
```

以下の条件を満たせばよかったので、実装はシンプルになりました。
- スマートフォン・タブレットでのみの展開
- 縦向きの16:9で固定
- inner全体に表示
- 再度リサイズされることはない

# スマートフォンで左右(上下)が見切れている
先程のものをBuild -> Deployした結果、どうも左右(または上下)が埋まってしまう現象が確認されました。
調べると、`canvas`は正常にリサイズされていましたが、表示されるWebGLは綺麗にリサイズされなかったようです。

恐らく`createUnityInstance`のコンフィグを見直す必要があるのですが、リリースまで1週間を切っていたタイミングでちゃんと調べることが出来ず、一先ず以下の対応をしました。

```javascript: resize.js
var aspect_with_height = 0.5625;
var aspect_with_witdh = 1.7777;
var fitScale = 0.95;

window.onload = function () {
    canvas = document.getElementById('unity-canvas');
    canvas.style.right = 0;
    canvas.style.left = 0;
    canvas.style.bottom = 0;
    canvas.style.top = 0;
    canvas.style.margin = 'auto';

    var setHeight = 0;
    var setWidth = 0;

    var scaledWidth = window.innerHeight * aspect_with_height;
    if( window.innerWidth < scaledWidth ){
        setWidth = window.innerWidth;
        setHeight = window.innerWidth * aspect_with_witdh;
    }else{
        setHeight = window.innerHeight;
        setWidth = scaledWidth;
    }

    canvas.width = setWidth * fitScale;
    canvas.height = setHeight * fitScale;
};
```

`fitScale`という名前で変数を作り、スケールを一括で0.95倍にしました。一先ずは上下左右見切れることがなくなりました。(現在はもう少しまともなコードになっています)

# iPadでは見切れたまま
先程の対応をしても、iPadでは見切れる不具合解消されませんでした。
調べると、iPadでは`viewport`に`content="initial-scale=1.0"`を設定してあげると正常に動作するのを確認しました。

「デスクトップ用Webサイトを表示」が有効になっているようで、強制的にスケーリングが入っていたようです。

ただし、この設定を全てに反映するとAndroidとiPhoneで崩れてしまうので、iPadでのみ反映されるように以下の記述を追加しました。

``` javascript
var agent = window.navigator.userAgent.toLowerCase();
if(agent.indexOf('ipad') > -1)
{
    document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=1.0');
}
```

`userAgent`に`iPad`の記述があるはずなので、それをチェックし`viewport`に`initial-scale=1.0`を反映するようにしました。

# まだiPadで見切れたまま
Android、iPhoneでは正常に動作するように戻りましたが、再びiPadで見切れるようになりました。

再度調べ直した結果、iOS13以降はiPadでは`userAgent`にiPadが**含まれない**ことがあることがわかりました。正確には「デスクトップ用Webサイトを表示」が有効になっている場合です。この場合`userAgent`には`Macintosh`が入ります。

その為、処理を以下のように書き直しました。

``` javascript
var agent = window.navigator.userAgent.toLowerCase();
if(agent.indexOf('macintosh') > -1 && 'ontouchend' in document)
{
    document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=1.0');
}
```

`'ontouchend' in document`を指定して、タッチに対応したデバイスに限定するように処理を書き直しました。これで治るはずでした。

# まだ直らない
一瞬理解に苦しみましたが、原因はすぐにわかりました。ブラウザではなく、WebView上で動かしていることです。
クライアント(依頼元)の話を聞く限りですが、WebViewは特に細かい設定をしていないとの事でした。その為デフォルトではWebViewに、仮にスマートフォンであっても`document`に`ontouchend`が含まれないことがここで発覚しました。

苦肉の策ではありましたが、`'ontouchend' in document`を削除し、以下のようになりました。

``` javascript
var agent = window.navigator.userAgent.toLowerCase();
if(agent.indexOf('macintosh') > -1)
{
    document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=1.0');
}
```

# 取り敢えずは動いた
ここまでの対応で、Android、iPhone、iPadの対応は全て完了しました。しかし、iPad対応に関する実装には不安しかありません。特に今回のiPadの件のように、Appleはまた何かやらかす可能性がありそうだからです。

これを完全に解決するのは、ネイティブアプリ側の実装に手を入れてもらう必要があります。

WebViewの`userAgent`は余程変な実装でもしない限りはカスタマイズができるはずなので、アプリ側でOSを判定し、その情報をuserAgentに入れるのが一番確実だという提案をして、この件は終了しました。

# 対応しきれてなかった
後から発覚しましたが、iPad Air(第4世代)ではこの方法では正常の動きませんでした。
困った事に`userAgent`が正常に動作するiPad(第8世代)と**全く同じ**なので、個別の対応も出来ませんでした。
こちら側だけで対応できる内容ではないので、現在もクライアントと相談中です。

# 終わりに
これらは利口な方法でも、最善の方法でも無いですが、一先ずはリリースに耐えられる形するのを最優先に行っていました。この間、別の不具合対応などもしていて、実質的に対応に回せた時間は実働1週間の内1日にも満ちません。(検証のために端末を買い足したりもしています)

余裕ができたら、ちゃんとした記事にまとめようと思います。