📗
【Unity】iPhoneやAndroidのノッチ対応
はじめに
初めての方も、そうでない方もこんにちは!
現役ゲームプログラマーのたむぼーです。
自己紹介を載せているので、気になる方は見ていただければ嬉しいです!
今回は
iPhoneやAndroidのノッチ対応
について、僕が実践している方法紹介します
ノッチについて
ノッチとは、スマートフォンの画面上部の切り欠きで、カメラやセンサーを収めるために使われ領域のこと
SafeAreaAdjusterについて
ノッチ対応の基底クラス
SafeAreaAdjusterBase.cs
using UnityEngine;
using Cysharp.Threading.Tasks;
namespace UI
{
abstract public class SafeAreaAdjusterBase : MonoBehaviour
{
protected RectTransform _rectTransform;
protected ScreenData _lastScreenData;
private bool _isSyncScreen;
/// <summary>
/// 破壊時
/// </summary>
private void OnDestroy()
{
_isSyncScreen = false;
}
/// <summary>
/// ScreenData
/// </summary>
protected struct ScreenData
{
public int width;
public int height;
public Rect safeArea;
public override readonly bool Equals(object obj)
{
if (obj is not ScreenData)
{
return false;
}
ScreenData other = (ScreenData)obj;
return width == other.width && height == other.height && safeArea == other.safeArea;
}
/// <summary>
/// GetHashCode
/// </summary>
public override readonly int GetHashCode()
{
return base.GetHashCode();
}
}
/// <summary>
/// 起動時
/// </summary>
private void Awake()
{
_rectTransform = GetComponent<RectTransform>();
ScreenData screenData = GetScreenData();
UpdateSafeArea(screenData);
SetLastScreenData(screenData);
#if UNITY_EDITOR
SnckScreenAsync().Forget();
#endif
}
/// <summary>
/// スクリーンデータの取得
/// </summary>
protected ScreenData GetScreenData()
{
ScreenData screenData = new ScreenData
{
width = Screen.width,
height = Screen.height,
safeArea = Screen.safeArea
};
return screenData;
}
/// <summary>
/// セーフエリアに入れる
/// </summary>
protected virtual void UpdateSafeArea(ScreenData screenData)
{
// 継承先で実装
}
/// <summary>
/// ラストデータの更新
/// </summary>
private void SetLastScreenData(ScreenData screenData)
{
_lastScreenData = screenData;
}
#if UNITY_EDITOR
/// <summary>
/// 非同期で画面同期
/// </summary>
private async UniTaskVoid SnckScreenAsync()
{
_isSyncScreen = true;
while (_isSyncScreen)
{
await UniTask.Yield(PlayerLoopTiming.Update);
ScreenData screenData = GetScreenData();
if (!screenData.Equals(_lastScreenData))
{
UpdateSafeArea(screenData);
SetLastScreenData(screenData);
}
}
}
#endif
}
}・
基本的なノッチ対応クラス
SafeAreaAdjusterForPanel.cs
using UnityEngine;
namespace UI
{
sealed public class SafeAreaAdjusterForPanel : SafeAreaAdjusterBase
{
public enum SafeAreaMode
{
None,
Vertical,
Horizontal,
Both
}
[Header("セーフモード")]
[SerializeField]
private SafeAreaMode _safeAreaMode = SafeAreaMode.Both;
[Space]
[Header("背景")]
[SerializeField]
private SafeAreaMode _backSafeAreaMode = SafeAreaMode.None;
[SerializeField]
private bool _isBackExpansion = false;
[SerializeField]
private RectTransform _backImage;
/// <summary>
/// セーフエリア内に収めえる
/// </summary>
protected override void UpdateSafeArea(ScreenData screenData)
{
if (_rectTransform == null)
{
return;
}
Vector2 anchorMin = screenData.safeArea.position;
Vector2 anchorMax = screenData.safeArea.position + screenData.safeArea.size;
anchorMin.x /= screenData.width;
anchorMin.y /= screenData.height;
anchorMax.x /= screenData.width;
anchorMax.y /= screenData.height;
Vector2 newAnchorMin = _rectTransform.anchorMin;
Vector2 newAnchorMax = _rectTransform.anchorMax;
switch (_safeAreaMode)
{
case SafeAreaMode.Vertical:
newAnchorMin.y = anchorMin.y;
newAnchorMax.y = anchorMax.y;
break;
case SafeAreaMode.Horizontal:
newAnchorMin.x = anchorMin.x;
newAnchorMax.x = anchorMax.x;
break;
case SafeAreaMode.Both:
newAnchorMin = anchorMin;
newAnchorMax = anchorMax;
break;
}
_rectTransform.anchorMin = newAnchorMin;
_rectTransform.anchorMax = newAnchorMax;
// 背景は含めたくない場合があるので別処理
if (_backImage != null)
{
Vector2 backAnchorMin = _backImage.anchorMin;
Vector2 backAnchorMax = _backImage.anchorMax;
if (_isBackExpansion)
{
switch (_backSafeAreaMode)
{
case SafeAreaMode.Vertical:
backAnchorMin.y = 0;
backAnchorMax.y = 1;
backAnchorMin.x = -0.1f;
backAnchorMax.x = 1.1f;
break;
case SafeAreaMode.Horizontal:
backAnchorMin.x = 0;
backAnchorMax.x = 1;
backAnchorMin.y = -0.1f;
backAnchorMax.y = 1.1f;
break;
case SafeAreaMode.Both:
backAnchorMin = new Vector2(-0.1f, -0.1f);
backAnchorMax = new Vector2(1.1f, 1.1f);
break;
}
}
else
{
switch (_backSafeAreaMode)
{
case SafeAreaMode.Vertical:
backAnchorMin.y = 0;
backAnchorMax.y = 1;
backAnchorMin.x = anchorMin.x;
backAnchorMax.x = anchorMax.x;
break;
case SafeAreaMode.Horizontal:
backAnchorMin.x = 0;
backAnchorMax.x = 1;
backAnchorMin.y = anchorMin.y;
backAnchorMax.y = anchorMax.y;
break;
case SafeAreaMode.Both:
backAnchorMin = Vector2.zero;
backAnchorMax = Vector2.one;
break;
}
}
_backImage.anchorMin = backAnchorMin;
_backImage.anchorMax = backAnchorMax;
}
}
}
}
型 | 変数 | 説明 |
---|---|---|
SafeAreaMode | _safeAreaMode | セーフモード |
SafeAreaMode | _backSafeAreaMode | 背景のセーフモード |
bool | _isBackExpansion | セーフモードの外に出すか |
RectTransform | _backImage | 背景のRectTransform |
左右のどちらかに付くノッチ対応クラス
SafeAreaAdjusterForOneSide.cs
using UnityEngine;
namespace UI
{
sealed public class SafeAreaAdjusterForOneSide : SafeAreaAdjusterBase
{
public enum SafeAreaAlignment
{
Left,
Right,
}
[Header("セーフモード")]
[SerializeField]
private SafeAreaAlignment _safeAreaAlignment = SafeAreaAlignment.Left;
[Header("背景")]
[SerializeField]
private RectTransform _backImage;
/// <summary>
/// 左右のみセーフエリアに入れる
/// </summary>
protected override void UpdateSafeArea(ScreenData screenData)
{
if (_rectTransform == null)
{
return;
}
Vector2 anchorMin = _rectTransform.anchorMin;
Vector2 anchorMax = _rectTransform.anchorMax;
float safeAreaLeft = screenData.safeArea.xMin / screenData.width;
float safeAreaRight = screenData.safeArea.xMax / screenData.width;
switch (_safeAreaAlignment)
{
case SafeAreaAlignment.Left:
anchorMin.x = safeAreaLeft;
anchorMax.x = 1.0f;
break;
case SafeAreaAlignment.Right:
anchorMin.x = 0.0f;
anchorMax.x = safeAreaRight;
break;
}
_rectTransform.anchorMin = anchorMin;
_rectTransform.anchorMax = anchorMax;
// 背景は含めたくない場合があるので別処理
if (_backImage != null)
{
Vector2 backgroundAnchorMin = _backImage.anchorMin;
Vector2 backgroundAnchorMax = _backImage.anchorMax;
switch (_safeAreaAlignment)
{
case SafeAreaAlignment.Left:
backgroundAnchorMin.x = -safeAreaLeft;
backgroundAnchorMax.x = safeAreaLeft;
break;
case SafeAreaAlignment.Right:
backgroundAnchorMin.x = safeAreaRight;
backgroundAnchorMax.x = 1.0f + (1.0f - safeAreaRight);
break;
}
_backImage.anchorMin = backgroundAnchorMin;
_backImage.anchorMax = backgroundAnchorMax;
}
}
}
}
型 | 変数 | 説明 |
---|---|---|
SafeAreaAlignment | _safeAreaAlignment | セーフモード |
RectTransform | _backImage | 背景のRectTransform |
使い方
対象のUIにパネルを作り、そのパネルにアタッチする。
セーフエリア対応したいUIを、そのパネルの子にすることで対応する
基本的には、SafeAreaAdjusterForPanelで良いが特別な処理をしたい場合は、SafeAreaAdjusterForOneSideのようにSafeAreaAdjusterBaseを継承して作成する
Discussion