【Unity】WebGL向けに作成したアプリにセーフエリア対応した

AvatarPosted by

この記事は GRIPHONE Advent Calendar 2019 2日目の記事です。

こんにちは、Unityエンジニアの黒板です。

今回はWebGL向けに作成したアプリにセーフエリア対応したときの実装方法を紹介したいと思います。複数解像度対応についても軽く触れています。

紹介するアプリのクラス構造については前回ブログで書いた記事を参考にしていただければと思います。

WebGL向けには、画面サイズを1138*640の固定サイズ前提で開発を行っていました。アスペクト比はほぼ16:9です。

実装

まずはルートのCanvasに対してCanvasScalerと今回実装したCanvasScalerInitializer をAddComponentし、下図のようにScaleModeをScaleWithScreenSizeに設定します。

UI表示用のルートCanvas設定

CanvasScalerInitializerでは、 Screen.safeArea を使ってUIを配置しても問題ない領域の縦横比を取得し、16:9に対してアスペクト比が縦横どちらに偏っているかを判定します。その後、セーフエリアをビルド対象の解像度から1138*640用の解像度に変換した分を考慮してCanvasScalerのreferenceResolutionに代入し、matchWidthOrHeightを切り替えています。

using UnityEngine;
using UnityEngine.UI;

/* CanvasScalerInitializer
    CanvasScalerの初期設定をするクラス
*/
[RequireComponent(typeof(CanvasScaler))]
public class CanvasScalerInitializer : MonoBehaviour
{
    private void Awake()
    {
        var canvasScaler = GetComponent<CanvasScaler>();
        var safeArea = Screen.safeArea;
        var aspect = safeArea.size.y / safeArea.size.x;
        var portrate = aspect > 640f / 1138f;
;

        if (canvasScaler.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize)
        {
            var width = 0f;
            var height = 0f;

            if (safeArea.x > 0f)
            {
                width = safeArea.x * 2f * 1138f / Screen.width;
            }

            if (safeArea.y > 0f)
            {
                height = safeArea.y * 640f / Screen.height;
            }

            canvasScaler.referenceResolution = new Vector2(1138f + width, 640f + height);
            canvasScaler.matchWidthOrHeight = portrate ? 0f : 1f;
        }
    }
}

これによって、Canvas領域を想定している画面サイズ(1138*640)+セーフエリア分確保することができます。

次に、Canvasの直下に置くWindowを以下のように設定します。

Windowの設定

SafeAreaScreenScalerでは、単純にセーフエリアを対象の解像度から1138*640用の解像度に変換した分をRectTransformのstretchに設定しているだけです。

using UnityEngine;

/* SafeAreaScreenScaler
    RectTransformのSafeArea設定をするクラス
*/
public class SafeAreaScreenScaler : MonoBehaviour
{
    private void Awake()
    {
        var safeArea = Screen.safeArea;
        var rectTransform = transform as RectTransform;

        if (rectTransform == null)
        {
            return;
        }

        var side = safeArea.x * 1138 / Screen.width;
        var bottom = safeArea.y * 640 / Screen.height;

        rectTransform.offsetMin = new Vector2(side, bottom);
        rectTransform.offsetMax = new Vector2(-side, 0f);
    }
}

これでUI表示領域を想定している画面サイズにマッチさせることができます。

16:9に対して横長の解像度に対しては1138以上のwidthが確保され、縦長の解像度に対しては640以上のheightが確保されるため、UI配置のアンカー設定は調整しておきます。16:9で組まれたUIを表示領域のセンターに配置する対応もありますが、マージンの位置を上下左右ではなく画面全体で吸収したかったので今回はセンター配置対応は選択しませんでした。

背景などのセーフエリアで見切れて欲しくない画面の端にかかる素材は画面サイズよりも余分に外側にはみ出るよう大きめに配置しています。画面外からアニメーションしてくるUIに関してもCanvasのサイズを意識しておく必要があります。

今後どのような解像度の端末が出てきても耐えられるような、柔軟なアプリ開発をしていけたらいいですね。