Unityでセーフエリアをエディタ上でシミュレートする方法です。
Unity2018.3.1
作るもの
Unityにはセーフエリアを簡単に取得できるプロパティが用意されています。
ただしこれは(当たり前ですが)セーフエリアが画面サイズと異なるサイズでしか効果がありません。
しかしこれではエディタでUIを並べているときに正しくセーフエリア対応ができているかわかりません。
そこでこんな感じで解像度の比率が一定以上 / 一定以下になったときにSafeAreaを設定する仕組みを作ります。
Screenクラスの代替となるものを作る
画面サイズは通常Screenクラスを使って取りますが、これには
- ランタイムでしか使えない
- セーフエリアがエディタでシミュレートできない
という問題があります。
なのでまずはこのScreenクラスを代替するクラスを作ります。
public static class ScreenUtility { #if !UNITY_EDITOR /// <summary> /// エディタ上でSafeAreaを再現する際の最大アスペクト比の値 /// </summary> private const float EDITOR_SAFE_AREA_RATIO_THRESHOLD = 2; private static float _lastAspectRatio; #endif private static Rect _safeArea; /// <summary> /// 画面全体の横幅 /// </summary> public static int FullWidth { get { #if !UNITY_EDITOR if (Application.isPlaying) { return Screen.width; } else { // エディタかつ再生中じゃない場合はUnityStatsからGameViewのサイズを取得 string[] screenres = UnityStats.screenRes.Split('x'); return int.Parse(screenres[0]); } #else return Screen.width; #endif } } /// <summary> /// 画面全体の高さ /// </summary> public static int FullHeight { get { #if !UNITY_EDITOR if (Application.isPlaying) { return Screen.height; } else { // エディタかつ再生中じゃない場合はUnityStatsからGameViewのサイズを取得 string[] screenres = UnityStats.screenRes.Split('x'); return int.Parse(screenres[1]); } #else return Screen.height; #endif } } /// <summary> /// 画面全体の範囲 /// </summary> public static Rect FullArea { get { return new Rect(Vector2.zero, new Vector2(FullWidth, FullHeight)); } } /// <summary> /// 画面サイズの比の値 /// </summary> public static float FullAspectRatio { get{ return (float)FullWidth / FullHeight; } } /// <summary> /// セーフエリアの横幅 /// </summary> public static int SafeWidth { get { return (int)SafeArea.width; } } /// <summary> /// セーフエリアの高さ /// </summary> public static int SafeHeight { get { return (int)SafeArea.height; } } /// <summary> /// セーフエリアの範囲 /// </summary> public static Rect SafeArea { get{ #if !UNITY_EDITOR if (FullAspectRatio != _lastAspectRatio) { if (FullAspectRatio > EDITOR_SAFE_AREA_RATIO_THRESHOLD) { // 画面が横長すぎたら補正 var width = FullHeight * EDITOR_SAFE_AREA_RATIO_THRESHOLD; var diffWidth = FullWidth - width; _safeArea = new Rect(diffWidth / 2, 0, width, FullHeight); } else if (FullAspectRatio < 1.0f / EDITOR_SAFE_AREA_RATIO_THRESHOLD) { // 画面が縦長すぎたら補正 var height = FullWidth * EDITOR_SAFE_AREA_RATIO_THRESHOLD; var diffHeight = FullHeight - height; _safeArea = new Rect(0, diffHeight / 2, FullWidth, height); } else { _safeArea = FullArea; } _lastAspectRatio = FullAspectRatio; } #else _safeArea = Screen.safeArea; #endif return _safeArea; } } /// <summary> /// セーフエリアのサイズの比の値 /// </summary> public static float SafeAspectRatio { get{ return (float)SafeWidth / SafeHeight; } } }
エディタから実行している場合、SafeArea
プロパティでシミュレート用のSafeAreaを作っています。
また、非ランタイムでGameビューのサイズを取るためにはUnityStats
クラスを使う必要があります。
これについては下記のサイトを参考にさせていただきました。
セーフエリアをRectTransformに適用する
あとはセーフエリアをRectTransformに適用するだけです。
[ExecuteInEditMode] [RequireComponent(typeof(RectTransform))] public class ScreenSizeFitter : MonoBehaviour { private enum AreaType { FullArea, SafeArea } [SerializeField] private AreaType _areaType = AreaType.FullArea; private Rect _lastArea = new Rect(); private RectTransform _rectTransform; public RectTransform RectTransform { get { return (_rectTransform = GetComponent<RectTransform>()); } } private void ApplyArea() { switch (_areaType) { case AreaType.FullArea: ApplyFullArea(); break; case AreaType.SafeArea: ApplySafeArea(); break; default: break; } } private void ApplyArea(Rect area) { if (_lastArea == area) { return; } RectTransform.anchoredPosition = Vector2.zero; RectTransform.sizeDelta = Vector2.zero; var anchorMin = area.position; var anchorMax = area.position + area.size; anchorMin.x /= ScreenUtility.FullWidth; anchorMin.y /= ScreenUtility.FullHeight ; anchorMax.x /= ScreenUtility.FullWidth; anchorMax.y /= ScreenUtility.FullHeight ; RectTransform.anchorMin = anchorMin; RectTransform.anchorMax = anchorMax; _lastArea = area; } private void ApplyFullArea() { ApplyArea(ScreenUtility.FullArea); } private void ApplySafeArea() { ApplyArea(ScreenUtility.SafeArea); } private void Start() { ApplyArea(); } private void Update() { #if UNITY_EDITOR ApplyArea(); #endif } }
こちらは特に特別なことはやっていません。
結果