using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; namespace Gpm.Ui { /// /// 이 컴포넌트가 부착된 RectTransform의 크기가 변경될 때, 지정된 'target' RectTransform들의 크기를 동일하게 맞추는 역할을 합니다. /// 주로 ContentSizeFitter와 함께 사용되어 동적으로 변하는 콘텐츠의 크기에 맞춰 배경 이미지 등의 크기를 조절할 때 유용합니다. /// /// /// UI 계층 구조 예시: /// /// - Background (RectTransform) /// - Content (RectTransform, VerticalLayoutGroup, ContentSizeFitter, ContentSizeSetter) /// - Item1 (Image) /// - Item2 (Image) /// - ... /// /// 위와 같은 구조에서 'Content' GameObject에 이 스크립트를 추가하고, /// 'target' 배열에 'Background'의 RectTransform을 할당하면, /// 'Content'의 크기가 내부 아이템(Item1, Item2)에 의해 변경될 때마다 'Background'의 크기도 함께 조절됩니다. /// [ExecuteAlways] // 에디터 모드에서도 스크립트가 실행되도록 하여, UI 변경을 실시간으로 확인할 수 있게 합니다. [RequireComponent(typeof(RectTransform))] // 이 컴포넌트는 RectTransform이 필수적으로 필요함을 명시합니다. public class ContentSizeSetter : UIBehaviour { // 성능 최적화를 위해 RectTransform 컴포넌트를 캐싱하는 변수입니다. [System.NonSerialized] private RectTransform m_Rect; /// /// 이 컴포넌트가 부착된 GameObject의 RectTransform에 대한 참조입니다. /// 처음 접근할 때 GetComponent를 통해 초기화하고, 이후에는 캐시된 값을 사용합니다. /// private RectTransform rectTransform { get { if (m_Rect == null) { m_Rect = GetComponent(); } return m_Rect; } } /// /// 크기를 조절할 때 추가할 여백(margin)입니다. /// x, y 값을 설정하여 target의 너비와 높이에 각각 추가적인 공간을 줄 수 있습니다. /// public Vector2 margin; /// /// 이 컴포넌트의 크기에 맞춰 함께 크기가 조절될 RectTransform(들)의 배열입니다. /// 인스펙터 창에서 크기를 동기화할 UI 요소들을 여기에 할당합니다. /// public RectTransform[] target; /// /// 컴포넌트가 활성화될 때 호출되는 Unity 생명주기 함수입니다. /// 레이아웃을 다시 계산하도록 시스템에 요청합니다. /// protected override void OnEnable() { SetDirty(); } /// /// 이 컴포넌트가 부착된 RectTransform의 크기나 앵커 등이 변경될 때 호출됩니다. /// 이 스크립트의 핵심 로직으로, target 배열에 있는 모든 RectTransform의 크기를 현재 RectTransform의 크기에 맞게 업데이트합니다. /// protected override void OnRectTransformDimensionsChange() { base.OnRectTransformDimensionsChange(); // 현재 RectTransform의 크기에 margin 값을 더하여 최종 크기를 계산합니다. Vector2 sizeDelta = new Vector2(rectTransform.sizeDelta.x + margin.x, rectTransform.sizeDelta.y + margin.y); if (target != null) { // target 배열의 모든 RectTransform에 대해 계산된 크기를 적용합니다. for (int i = 0; i < target.Length; i++) { target[i].sizeDelta = sizeDelta; // target의 레이아웃을 갱신하도록 표시하여 UI가 올바르게 다시 그려지도록 합니다. LayoutRebuilder.MarkLayoutForRebuild(target[i]); } } } /// /// UI 레이아웃을 다시 계산해야 함을 시스템에 알립니다. /// /// true일 경우, 즉시 레이아웃을 다시 계산합니다. false일 경우, 다음 프레임에 다시 계산하도록 예약합니다. protected void SetDirty(bool force = false) { if (IsActive() == false) { return; } if (force == true) { // 즉시 레이아웃을 강제로 다시 계산합니다. LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform); } else { // 다음 레이아웃 계산 주기에 맞춰 다시 계산하도록 표시만 해둡니다. (일반적인 경우) LayoutRebuilder.MarkLayoutForRebuild(rectTransform); } } #if UNITY_EDITOR /// /// (Unity 에디터 전용) 스크립트가 로드되거나 인스펙터에서 값이 변경될 때 호출됩니다. /// 에디터에서 margin과 같은 값을 변경했을 때, 실시간으로 UI에 반영되도록 레이아웃 업데이트를 요청합니다. /// protected override void OnValidate() { SetDirty(false); } #endif } }