유니티

유니티 - Simulator와 아이폰 노치에 UI 대응해보기

bugmin 2024. 7. 11. 23:22

 

UI 제작 연습을 위해 게임 명일방주를 역기획해서 UI를 만들어보았다.

 

다만 게임씬에서 보이는 게임 화면은 PC 기준으로 보여주고 있는 것이다. 물론 모바일 비율로 바꿀 수도 있지만 실제 기기에서는 어찌 적용될지 궁금함이 생긴다.

 

이럴 때 사용할 수 있는 것이 Simulator이다.

 

Simulator 사용

 

게임씬에서 Game 대신 Simulator를 선택해주면 된다.

 

 

Simulator로 바꾸면 여러가지 기기들이 보인다. 

 

실제 기기에서 어떤 식으로 보여지는지 알 게 된다.

 

아이폰의 경우 다이내믹 아일랜드나 노치로 인해 화면을 일부 영역을 가리고 갤럭시의 경우 펀치홀이 화면을 아이폰보다는 훨씬 덜하지만 화면을 조금이지만 가린다.

 

아이폰 12의 경우엔 노치가 상당히 큰 편인데 아이폰 12로 설정해보고 확인해보면

 

노치로 인해 UI 일부분이 짤리는 모습이다.

 

하단바 역시 이상하게 배치되어있다.

 

또한 각 pc화면과 다르게 하단 부분이 치우친 모습이고 각 모서리가 라운딩 처리되어있다보니 구석에 있는 버튼들이 이쁘지 않게 배치가 되어있는 모습이다.

 

 

여기서 Safe Area를 클릭하면

 

기기로 인해 간섭받지 않는 안전 영역을 노란 사각형으로 나타내주고 있다. 저 안에 들면 안전하다는 얘기다.

 

만일 아이폰12보다도 작은 홈버튼이 존재하는 구형 플랫폼인 아이폰SE2를 고르면 어떨까

 

 

UI크기가 너무 커져버린 모습이다...

 

그래도 노치나 펀치홀 같이 방해하는 요소는 없어서 안전 영역은 화면 전체를 해당된다.

 

UI커진걸 해결 하려면 어찌 해야할까?

 

일단 화면크기에 따라 UI 크기가 조절이 되어야만 할 거 같다.

 

하이라키창 캔버스를 보면

 

 

Canvas Scaler의 UI Scale Mode가 Constant Pixel Size로 되어있는데 이를 아래와 같이 바꿔보자

 

 

유동적으로 디바이스 해상도에 따라 변할 수 있도록 Scale With Screen Size로 하고 레퍼런스가 되는 해상도는 FHD로 설정하였다.

또한 Screen Match Mode는 Expand를 선택하여 반응형 UI가 되도록 하였다.

 

래퍼런스가 되는 PPU 사이즈도 정할 수 있다. 기본 세팅인 100으로 일단 설정

 

다시 아이폰 SE 2세대에서의 시뮬레이터 화면을 보면

 

일단 사이즈가 줄었다.

 

이젠 노치관련 문제를 해결해야 한다. 이를 위해선 

 

안전 영역에 UI들이 위치하도록 해야한다.

 

근데 여기서 궁금증이 생길 수 있다.

 

 Safe Area와 휴대폰 하단 베젤 사이에 간격이 발생한다.

왜 발생하는 걸까?

 

이는 홈으로 가는 바가 생성되는 공간을 비워준 것이다..

 

 

요즘 안드로이드도 비슷하게 바뀐 것으로 아는데 하단을 위로 쓸어 올리면 홈으로 갈 수 있는 바가 생긴다. (사진 참고)

해당 공간을 위해 비워준것이라 생각하면 된다.

 

아무쪼록 이 안전 영역에 UI들이 배치되도록 하면 문제는 해결될 것이다.

 

일단 캔버스에 빈오브젝트로 SafeArea으로 이름을 바꾸고 이 안에 기존에 있던 UI 오브젝트들을 다 넣어주었다.

다만 UI오브젝트들을 넣기 전 주의해야할 점이 있다.

 

주의

 

SafeArea의 RectTransform을 캔버스에 꽉채우도록 프리셋을 변경시킨 후에 UI 오브젝트들을 넣어줘야 문제가 안생긴다.

 

이제 앵커 위치를 조정하여 실제 안전 영역의 각 모서리에 해당 앵커들이 위치하도록 하면 됨

 

안전 영역인 노란색 사각형의 모서리에 앵커들이 각각 위치하도록 한다 이해하면 됨

 

 

Anchor 값은 뷰포트 좌표계로 이해하면 편한데 그냥 좌측하단이 (0, 0) 우측상단이 (1, 1)로 생각하면 된다.

 

여기서 잠깐 스크린 좌표계랑 뷰포트 좌표계를 집고 넘어가면

 

스크린 좌표계

스크린좌표계는 FHD 기준 좌측하단이 (0, 0) 우측상단이 (1920, 1080) 이고

(우측상단이 해상도를 따라간다 보면 된다)

 

뷰포트 좌표계

뷰포트 좌표계는 해상도가 FHD던 뭐던 우측 상단이 (1, 1)라고 생각하면 된다.

 

앵커를 이 뷰포트 좌표계로 이해하면 좋다.

 

고로 앵커 X의 Min 값을 0.5로 키우면 

 

X값은 최소 0.5부터 시작해야하기 때문에 앵커가 위와 같이 변한 것을 볼 수 있다.

 

 

만일 이 상태서 Y의 Max값을 0.5로 바꾼다면 Y는 최대 0.5까지의 값만 가질 수 있으니 사진과 같이 앵커가 내려온 모습을 볼 수 있다.

정중앙에 앵커가 위치한 이유는 X값이 최소 0.5의 값을 가져야 하기 때문에 저리 된 것이다.

 

이런 식으로 Anchor값의 Min, Max 값을 조정하여 실제 안전 영역(노란색 영역)의 네 모서리에 앵커가 착착 위치하게 하면 될것이다.

 

그리고 이를 위한 스크립트를 달아주면 된다.

 

 

RectTransform이 필수적으로 필요하니 RequireComponent를 적어주었고

Screen.safeArea로 안전 영역에 대한 정보가 담긴 Rect 구조체를 받아오고 이를 이용한 것이다.

 

여기서 safeArea에 담긴 position이나 size 정보는 위에서 설명한 screen 좌표계 기준이라. 좌측하단이 (0, 0) 우측 상단이 FHD면 (1920, 1080) 이다.

 

safeArea.position: 안전 영역의 왼쪽 하단 모서리 좌표임 (단 좌표는 스크린 좌표계 기준)

그림으로 이해하면 안전 영역의 좌측 하단 빨간색 점의 좌표가 담긴다 보면 된다. 다만 담길 때 스크린 좌표계의 좌표가 담긴다는 것이다.

 

저 점의 위치가 Anchor 중 하나가 위치해야할 위치이고 저 위치가 곧 앵커의 최솟값이기에 

Vector2 anchorMin = safeArea.position;

 

앵커의 최솟값으로 안전영역의 포지션 값을 넣은 것이다.

 

Vector2 anchorMax = safeArea.position + safeArea.size;

 

이제 이부분을 이해해야하는건데

 

앵커의 Max값으로 왜 안전영역의 포지션에 size값을 더해주는 걸까?

안전영역의 size는 안전 영역의 가로, 세로 정보를 Vector2 값으로 가지고 있다 생각하면 된다.

만일 예를 들어 안전 영역의 가로가 1000이고 세로가 490이면 안전 영역의 size 값은 (1000, 490)의 Vector2 값이다.

 

고로 이를 빨간색 점의 좌표에다가 더하면 초록색점의 좌표가 나오게 된다.

 

빨간색 점의 좌표는 아까 safeArea.position이라 했다.

 

고로 

Vector2 anchorMax = safeArea.position + safeArea.size;

 

해당 코드가 성립하는 것이다.

 

자 이제 앵커의 Min, Max 값은 구했다.

근데 문제가 있다. 이는 스크린 좌표계 값이지 우리는 인스팩터창에서 뷰포트 좌표계 값으로 앵커값을 넣어주고 있었다.

 

고로 이를 변환하는 작업을 거쳐야한다.

 

        anchorMin.x /= Screen.width;
        anchorMin.y /= Screen.height;

        anchorMax.x /= Screen.width;
        anchorMax.y /= Screen.height;

 

그것이 바로 해당 코드이다.

 

Screen.width가 바로 스크린 좌표계에서의 가로 길이, Screen.height는 세로 길이이다.

 

쉽게 말해 FHD 기준 Screen.width는 1920, Screen.height는 1080이란 거다.

 

아이폰 12기준 아이폰 12의 해상도가 2532x1170 이므로

 

Screen.width는 2532, Screen.height는 1170가 되겠지..

 

가 아니라 이는 그냥 이해를 위해 말한 것이지 실제로 기기 해상도가 바로 대응되는 것은 아니라 합니다.

다만 이해를 위해 위와 같이 설명을 한 점 양해바랍니다..

 

        anchorMin.x /= Screen.width;
        anchorMin.y /= Screen.height;

        anchorMax.x /= Screen.width;
        anchorMax.y /= Screen.height;

 

이제 Screen의 가로 세로 길이를 가지고 Min 과 Max 값을 뷰포트 좌표계로 변환하는 과정을 거치면 된다.

 

왜 Screen의 가로나 세로 길이로 나누고 있는 것이냐면

FHD로 생각해보자

스크린 좌표계에선 우측 상단의 좌표가 (1920, 1080) 이지만 뷰포트 좌표계에선 (1, 1)이다.

 

즉 (1920, 1080)이 (1, 1) 이 되려면 X값은 스크린의 가로 길이인 1920으로 나눠주면되고 1080은 스크린의 세로 길이인 1080으로 나눠주면 (1, 1)이 된다.

 

고로 

        anchorMin.x /= Screen.width;
        anchorMin.y /= Screen.height;

        anchorMax.x /= Screen.width;
        anchorMax.y /= Screen.height;

 

똑같이 x 값은 스크린의 가로길이로, y값은 스크린의 세로길이로 나누어 앵커의 Min/Max의 x, y 값을 구한 것이다.

 

자 이렇게해서 모든 변환 과정이 끝났고 마지막으로

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

 

구한 값들을 rectTransform의 anchorMin/Max에 넣어주면 된다.

 

자 그러면 코드 실행을 통해 확인을 해보자

 

됐다!!!!!

 

안전영역에 UI 요소들이 들어와있는 모습이다.

 

노치에 대해서만 진행했지만 펀치홀이나 다이나믹 아일랜드 역시 똑같이 진행하면 된다.

어차피 그들만의 안전 영역에 맞게 값이 세팅되기 때문에 걱정하지 않아도 된다.

 

오늘은 좀 TIL 작성에 오랜 시간이 소요됐다... 하지만 알아두면 좋은 내용일 것이라 생각든다.

 

더보기

제글을 보시는 분들께...

 

혹시 제 글을 보는 사람들에게 말씀을 드리자면 저도 코드를 텍스트로 올리고 싶은데 제가 티스토리에서 코드가 이쁘게 정렬되는 방법을 잘 모르겠어서 코드를 텍스트로 올리면 너무 가독성이 떨어져 캡쳐로 이용하는거라 양해바랍니다.. 짧은 코드의 경우 텍스트로 올려도 괜찮은데 길게 올리면 이상해지더라고요.. 어디까지나 제 개인공부를 위해 작성하는 것이라.. 배려가 좀 부족한 부분 양해바라겠습니다 ㅠㅠ 혹시 텍스트가 이쁘게 정렬되는 거 알려주시면 반영할게요!