using UnityEngine;
|
|
/// <summary>
|
/// 安全区域适配组件 - 自动适配刘海屏和异形屏
|
/// </summary>
|
[RequireComponent(typeof(RectTransform))]
|
[ExecuteInEditMode]
|
public class SafeAreaAdapter : MonoBehaviour
|
{
|
[Header("适配设置")]
|
[Tooltip("是否在编辑器模式下模拟安全区域")]
|
public bool simulateInEditor = false;
|
|
[Tooltip("适配边缘(Top, Bottom, Left, Right)")]
|
public bool adaptTop = true;
|
public bool adaptBottom = true;
|
public bool adaptLeft = true;
|
public bool adaptRight = true;
|
|
[Header("调试信息")]
|
[SerializeField] private Rect currentSafeArea;
|
[SerializeField] private Vector2Int screenSize;
|
|
private RectTransform _rectTransform;
|
private Rect _lastSafeArea = Rect.zero;
|
private Vector2Int _lastScreenSize = Vector2Int.zero;
|
|
private void Awake()
|
{
|
_rectTransform = GetComponent<RectTransform>();
|
ApplySafeArea();
|
}
|
|
private void Update()
|
{
|
// 检测屏幕变化或安全区域变化
|
if (HasScreenChanged())
|
{
|
ApplySafeArea();
|
}
|
}
|
|
/// <summary>
|
/// 检测屏幕是否发生变化
|
/// </summary>
|
private bool HasScreenChanged()
|
{
|
Rect safeArea = GetSafeArea();
|
Vector2Int screenSize = new Vector2Int(Screen.width, Screen.height);
|
|
bool changed = safeArea != _lastSafeArea || screenSize != _lastScreenSize;
|
|
_lastSafeArea = safeArea;
|
_lastScreenSize = screenSize;
|
|
return changed;
|
}
|
|
/// <summary>
|
/// 获取安全区域
|
/// </summary>
|
private Rect GetSafeArea()
|
{
|
#if UNITY_EDITOR
|
if (simulateInEditor)
|
{
|
// 编辑器模式下模拟 iPhone X 的安全区域
|
float screenWidth = Screen.width;
|
float screenHeight = Screen.height;
|
|
// 模拟刘海(顶部 44px)和底部手势条(底部 34px)
|
float topInset = 44f;
|
float bottomInset = 34f;
|
float leftInset = 0f;
|
float rightInset = 0f;
|
|
return new Rect(
|
leftInset,
|
bottomInset,
|
screenWidth - leftInset - rightInset,
|
screenHeight - topInset - bottomInset
|
);
|
}
|
#endif
|
|
return Screen.safeArea;
|
}
|
|
/// <summary>
|
/// 应用安全区域适配
|
/// </summary>
|
private void ApplySafeArea()
|
{
|
if (_rectTransform == null)
|
return;
|
|
Rect safeArea = GetSafeArea();
|
currentSafeArea = safeArea;
|
screenSize = new Vector2Int(Screen.width, Screen.height);
|
|
// 计算锚点位置
|
Vector2 anchorMin = safeArea.position;
|
Vector2 anchorMax = safeArea.position + safeArea.size;
|
|
// 转换为标准化坐标 (0-1)
|
anchorMin.x /= Screen.width;
|
anchorMin.y /= Screen.height;
|
anchorMax.x /= Screen.width;
|
anchorMax.y /= Screen.height;
|
|
// 根据设置决定是否适配各个边缘
|
Vector2 finalAnchorMin = _rectTransform.anchorMin;
|
Vector2 finalAnchorMax = _rectTransform.anchorMax;
|
|
if (adaptLeft)
|
finalAnchorMin.x = anchorMin.x;
|
|
if (adaptBottom)
|
finalAnchorMin.y = anchorMin.y;
|
|
if (adaptRight)
|
finalAnchorMax.x = anchorMax.x;
|
|
if (adaptTop)
|
finalAnchorMax.y = anchorMax.y;
|
|
_rectTransform.anchorMin = finalAnchorMin;
|
_rectTransform.anchorMax = finalAnchorMax;
|
|
// 重置偏移
|
_rectTransform.offsetMin = Vector2.zero;
|
_rectTransform.offsetMax = Vector2.zero;
|
|
Debug.Log($"[SafeAreaAdapter] 安全区域已应用: {safeArea}, 屏幕: {screenSize}");
|
}
|
|
/// <summary>
|
/// 强制重新应用安全区域
|
/// </summary>
|
public void ForceApply()
|
{
|
ApplySafeArea();
|
}
|
|
#if UNITY_EDITOR
|
private void OnValidate()
|
{
|
if (_rectTransform == null)
|
_rectTransform = GetComponent<RectTransform>();
|
|
ApplySafeArea();
|
}
|
#endif
|
}
|