神様は有休消化中です。

Unity関連の技術ネタを書いてます。

【Unity】iPhoneX対応の罠 なぜかCanvasのAnchorがずれる問題の解決策

概要

遅ればせながら、今開発中の新作ゲームでiPhoneX対応を行うことになりました。
ネットを検索するといろんな情報が溢れていますが、まずは公式情報を・・・ということでUnity公式のサンプルを元に対応を進めた結果、見事に地雷を踏んだので共有しておきます。

Unity公式情報

iPhoneXの画面問題は業界的にもかなり迷惑なものでした。
Unityもいち早くこの問題に向き合い、パッチリリースを経て正式にSafeAreaに対応しています。
helpdesk.unity3d.co.jp

Unity - Scripting API: Screen.safeArea

一つ目のリンクからUnityのサンプルをみることができます。
そのサンプルの内容は、要約すると以下の通りです。

void ApplySafeArea()
{
    var area = Screen.safeArea;
    
    var anchorMin = area.position;
    var anchorMax = area.position + area.size;
    anchorMin.x /= Screen.width;
    anchorMin.y /= Screen.height;
    anchorMax.x /= Screen.width;
    anchorMax.y /= Screen.height;
    panel.anchorMin = anchorMin;
    panel.anchorMax = anchorMax;
}

サンプルの罠

上記のサンプルですが、特定の状況下で不具合を引き起こします。
私が遭遇した不具合は、以下のようなものです。

  • CanvasのAnchorが右上にずれる
  • 一部のUIが明滅を繰り返すようになる

原因

上記に書いた不具合は、Screen.SetResolutionで画面解像度を変更している場合に発生します。
原因は、anchorMin/anchorMaxの各要素をScreen.width/heightで正規化している部分です。
Screen.width/heightは、SetResolution以降は指定した解像度を返すようになります。
しかし、Screen.safeAreaで返される値はディスプレイの解像度のため、正規化の計算が狂い、CanvasのAnchorが右上にずれてしまうわけです。

対応

バイスのディスプレイ解像度をDisplayクラスから取得し、その値で正規化することで問題を解決することができます。

void ApplySafeArea()
{
    var area = Screen.safeArea;
    
    var display = Display.displays[0];
    var screenSize = new Vector2Int(display.systemWidth, display.systemHeght);

    var anchorMin = area.position;
    var anchorMax = area.position + area.size;
    anchorMin.x /= screenSize.x;
    anchorMin.y /= screenSize.y;
    anchorMax.x /= screenSize.x;
    anchorMax.y /= screenSize.y;
    panel.anchorMin = anchorMin;
    panel.anchorMax = anchorMax;
}