[Unity] Safe Area

muchan
muchan
∙∙조회 19
[Unity] Safe Area

개요

모바일 게임을 만들때 노치(Notch), 카메라 홀, Dynamic Island, 홈 인디케이터, 라운드 코너 같은 영역을 피해
UI가 안전하게 보이도록 보장하는 영역을 Safe Area라고 한다.

필요한 이유

일부 폰에서는 다음과 같은 항목들 때문에 UI가 잘릴 수 있다

  • 상단 카메라 영역
  • 하단 제스처 바
  • 둥근 모서리

Safe Area를 보장해주지 않으면 UI 버튼이 노치 영역과 겹칠 수 있어 클릭을 못하게 되는 상황이 발생한다.

연습해보기

Safe Area를 보장해주는 코드를 작성해보겠다

우선 Stretch를 통해 UI를 배치했다.

이때 Rotate 오른쪽에 Safe Area를 클릭해보면

노란색 영역이 표시된다. 노란색 안쪽이 안전한 영역이라 이 안에 UI를 배치하는게 좋다

각각의 시뮬레이터마다 Safe Area가 다르다

csharp
using UnityEngine;

[ExecuteAlways]
// Rect가 있으면 넣어주고 없으면 안넣고 뭐 제약있고 그럼
[RequireComponent(typeof(RectTransform))]
public class SafeAreaPanel : MonoBehaviour
{
    private RectTransform rectTransform;
    private Canvas parentCanvas;

    private Rect lastSafeArea = Rect.zero;
    private Vector2Int lastScreenSize = Vector2Int.zero;

    private void Awake()
    {
        rectTransform = GetComponent<RectTransform>();
        parentCanvas = GetComponentInParent<Canvas>();
    }

    private void OnEnable()
    {
        Apply();
    }

    private void OnRectTransformDimensionsChange()
    {
        // isActiveAndEnabled 이게 true면 화면에 나와있다
        if (!isActiveAndEnabled)
            return;

        Apply();
    }

    private void Apply()
    {
        if (parentCanvas == null)
        {
            parentCanvas = GetComponentInParent<Canvas>();
            if (parentCanvas == null)
                return;
        }

        Rect pixelRect = parentCanvas.pixelRect;
        Rect safeArea = Screen.safeArea;
        Vector2Int screenSize = new Vector2Int(Screen.width, Screen.height);

        if (safeArea == lastSafeArea && screenSize == lastScreenSize)
            return;

        lastSafeArea = safeArea;
        lastScreenSize = screenSize;

        Vector2 anchorMin = safeArea.position;
        Vector2 anchorMax = safeArea.position + safeArea.size;

        anchorMin.x /= pixelRect.width;
        anchorMin.y /= pixelRect.height;
        anchorMax.x /= pixelRect.width;
        anchorMax.y /= pixelRect.height;

        rectTransform.anchorMin = anchorMin;
        rectTransform.anchorMax = anchorMax;

        // 앵커가 바뀌면 포지션도 상대위치로 바뀌기 때문에 초기화 해줘야함
        rectTransform.anchoredPosition = Vector2.zero;
        rectTransform.sizeDelta = Vector2.zero;
    }
}

원리

  1. 현재 Safe Area 얻기
  2. 화면 비율 기준 Anchor 값으로 변환
  3. RectTransform anchorMin/max 적용
  4. UI 패널을 Safe Area 영역으로 제한

[ExecuteAlways]를 통해 항상 실행된다

UI 오브젝트에 넣어주면 Safe Area에 맞춰주는것을 볼 수 있다.