이전에 해상도에 따른 UI 작업을 다룬 포스팅이 있었는데,
앵커를 통해 화면 일정 비율 위치에 UI를 배치하는 방법에 대해 소개를 드렸습니다.
하지만, 그 작업을 모든 UI에 하나하나 하고 있다면 시간 소모가 매우 커집니다.
따라서 그 작업을 자동화하여 아주 빠르고 간단히 진행할 수 있는 에디터 메뉴를 만들었기에 공유드립니다.

<< 적용 전 >>

<< 적용 후 >>
<< 주의사항 >>
1. 해당 스크립트를 만들어 Assets/Editor 폴더에 넣어주시고 주석으로 된 사용법을 따라해보세요.
2. Grid / Vertical / Horizontal Layout Group, ContentSizeFitter 과 같은 자동 정렬 컴포넌트는 계산을 덮어써 비율이 깨지기에 정렬 대상에서 제외되도록 처리해 두었습니다. >> 해당 오브젝트들은 Canvas Scaler의 Match를 조절하여 최대한 맞춰봅시다.
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
public class RectTransformAnchorSetter
{
// 1. 목적
// 캔버스 스케일러 - UI 스케일 모드를 쓰더라도 기준 화면 비율과 다르게 되면 깨질 수 있음 (pc에서의 창 크기 조절 or 모바일 폰 해상도 비율 다름 등)
// UI 작업 때 시간이 오래 걸림 + 이후 수정해야 할 수 있어 앵커를 다 맞춰주지는 않는데,
// 화면 비율이 달라지면 해당 UI들이 의도한 것과 다르게 배치되는 경우가 있기에
// 현재 위치를 앵커로 마킹한 후 포지션을 0으로 만들어 그 화면 위치 비율에 고정시키는 작업을 하나하나 수작업 해줘야 하는 2차 수고 발생
// 그 작업 시간을 획기적으로 줄이기 위해 자동화 메뉴를 만들었습니다.
// 2. 사용법
// 1) 하이어라키를 전체 선택(단축키 Ctrl + A) (UI가 아닌 요소들은 자동으로 걸러주기에 전체 선택)
// 2) 유니티 에디터 상단 메뉴 - Tools - UI - Set Anchors To Current Rect 메뉴(단축키 Ctrl + Shift + Q)
// 메뉴에 항목 추가
// %#q = Ctrl + Shift + Q 단축키
[MenuItem("Tools/UI/Set Anchors To Current Rect %#q")]
static void SetAnchorsToSelectedRect()
{
Canvas.ForceUpdateCanvases();
// 앵커 프리셋 Middle, Center 설정용
Vector2 anchorPreset_MiddleCenter = new Vector2(0.5f, 0.5f);
// 선택된 모든 오브젝트 순회
foreach (var obj in Selection.gameObjects)
{
// RectTransform 컴포넌트 가져오기
RectTransform rect = obj.GetComponent<RectTransform>();
// RectTransform이 아니거나 앵커의 기준이 될 부모가 없으면 제외
if (rect == null || rect.parent == null)
continue;
// UI 요소들은 Canvas (RectTransform 포함)의 자식이며 그 안의 요소들도 RectTransform으로 위치 결정
// 부모도 RectTransform이어야 계산 가능 >> 앵커의 기준
RectTransform parentRect = rect.parent as RectTransform;
if (parentRect == null)
continue;
// 슬라이더의 자식 오브젝트를 자동 앵커로 하면 Fill, Handle이 깨짐
// 슬라이더 컴포넌트가 들어간 오브젝트만 앵커를 맞춰주면 그 자식들은 자동으로 부모에 맞춰짐
Slider slider = rect.GetComponent<Slider>();
Slider parentSlider = rect.GetComponentInParent<Slider>(true);
if (parentSlider != null && slider != parentSlider)
{
Debug.LogWarning($"{obj.name} 계산 건너뜀.\n원인: 부모에 슬라이더 존재하여 계산 불필요");
continue;
}
// ContentSizeFitter 컴포넌트가 있는 요소 제외 >> preferred size 기준 RectTransform.sizeDelta 강제 조절 >> 앵커 기반 계산과 충돌하여 비율 어긋남
// >> 다른 컴포넌트나 스크립트에서 위치 계산을 다시해서 덮어쓰면 해당 변환을 수행해서는 안됨. 이하 레이아웃 그룹 제외도 같은 이유
if (rect.GetComponent<ContentSizeFitter>() != null)
{
Debug.LogWarning($"{obj.name} 계산 건너뜀.\n원인: ContentSizeFitter에서 위치 자동 계산");
continue;
}
// Layout 시스템에 의해 제어되는 요소 제외. 단 GetComponentInParent이 해당 탐색을 수행하는 자신도 포함인데, 레이아웃 그룹 컴포넌트가 있는 부모는 앵커 위치 고정을 해줘야 잘 맞음
LayoutGroup layoutGroup = rect.GetComponent<LayoutGroup>();
LayoutGroup layoutGroup_Parent = rect.GetComponentInParent<LayoutGroup>(true);
if (layoutGroup_Parent != null && layoutGroup != layoutGroup_Parent)
{
Debug.LogWarning($"{obj.name} 계산 건너뜀.\n원인: LayoutGroup이 있는 오브젝트의 자식이므로 위치 자동 계산");
continue;
}
// Undo 기록 (Ctrl+Z로 되돌리기 가능하게)
Undo.RecordObject(rect, "Set Anchors");
/*
핵심 개념 설명
offsetMin = 왼쪽 아래 기준 앵커로부터 얼마나 떨어져 있는지 (픽셀)
offsetMax = 오른쪽 위 기준 앵커로부터 얼마나 떨어져 있는지 (픽셀)
우리는 이 offset을 부모 크기로 나눠서
"비율"로 변환 후 anchor에 더해줄 것이다.
*/
// 부모의 실제 너비/높이
float parentWidth = parentRect.rect.width;
float parentHeight = parentRect.rect.height;
// 0 나누기 방지: 부모 크기가 0이면 계산이 불가능하므로 로그를 남기고 스킵
// >> 자동으로 맞춰주면 문제 발생 우려가 있기에 작업자에게 알림
if (Mathf.Approximately(parentWidth, 0) || Mathf.Approximately(parentHeight, 0))
{
Debug.LogWarning($"{obj.name} 계산 건너뜀.\n원인: 부모 {parentRect.name}의 Width 혹은 Height가 0. 해당 값을 0보다 큰 값으로 바꾼 후 다시 실행");
continue;
}
// 현재 offset을 부모 크기 기준 비율로 변환
Vector2 newAnchorMin = new Vector2(
rect.anchorMin.x + rect.offsetMin.x / parentWidth,
rect.anchorMin.y + rect.offsetMin.y / parentHeight
);
Vector2 newAnchorMax = new Vector2(
rect.anchorMax.x + rect.offsetMax.x / parentWidth,
rect.anchorMax.y + rect.offsetMax.y / parentHeight
);
// 새 앵커 적용
rect.anchorMin = newAnchorMin;
rect.anchorMax = newAnchorMax;
/*
이제 offset을 anchor에 흡수했으므로
offset은 0으로 만들어도
화면상 위치/크기는 그대로 유지된다.
*/
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
}
}
}
#endif'기능 구현 방법 정리' 카테고리의 다른 글
| 유니티, C#) AES 암호화/복호화 + JSON 데이터 저장/불러오기 (0) | 2025.02.10 |
|---|---|
| 유니티 UI 이미지 마스크 (0) | 2025.01.27 |
| 유니티 UI에 스파인 애니메이션 넣기 (0) | 2025.01.26 |
| 유니티 2D) 부모 오브젝트 주위를 자식 오브젝트들이 원형으로 회전하게 하고 싶다면? (RotateAround 사용 버전도 포함) (0) | 2024.12.12 |
| 유니티) 레이어마스크(LayerMask) 사용법, 2D 가장 가까운 적 탐색 (0) | 2024.12.02 |