using System; using UnityEngine; using UnityEngine.UI; namespace UVC.Extension { public static class LayoutGroupEx { /// /// LayoutGroup의 직접 자식 요소들의 정렬을 시도합니다. /// 각 자식의 앵커와 피벗은 (0.5, 0.5)로 설정되며, localPosition이 조정됩니다. /// /// 정렬할 LayoutGroup입니다. /// 적용할 정렬 방식입니다. 현재 MiddleLeft, MiddleCenter, MiddleRight만 특정 동작을 수행합니다. /// /// [초보자 설명] /// 이 메서드는 LayoutGroup의 직접 자식들의 위치를 수동으로 조정합니다. /// 각 자식의 앵커(anchorMin, anchorMax)와 피벗(pivot)은 모두 (0.5, 0.5)로 설정됩니다. /// 이는 자식의 위치와 크기 기준점이 중앙으로 설정된다는 의미입니다. /// /// 중요: 이 메서드는 HorizontalLayoutGroup, VerticalLayoutGroup, GridLayoutGroup과 같은 /// 활성 레이아웃 컴포넌트의 자동 레이아웃 계산과 충돌할 수 있습니다. /// 레이아웃 컴포넌트가 활성화되어 있다면, 이 메서드에 의한 변경 사항이 다음 레이아웃 업데이트 시 덮어쓰여질 수 있습니다. /// 이 메서드는 레이아웃 컴포넌트가 비활성화되었거나, 특정 상황에서 수동 제어가 필요할 때 제한적으로 사용해야 합니다. /// /// 지원되는 'alignment' 값에 따른 동작: /// - MiddleLeft: 자식의 왼쪽 가장자리가 부모(LayoutGroup)의 가로 중앙선에 정렬됩니다. /// - MiddleCenter: 자식의 중앙이 부모의 중앙에 정렬됩니다. /// - MiddleRight: 자식의 오른쪽 가장자리가 부모의 가로 중앙선에 정렬됩니다. /// 다른 TextAnchor 값은 현재 특별한 동작을 수행하지 않거나 경고를 출력할 수 있습니다. /// /// /// /// using UnityEngine; /// using UnityEngine.UI; /// using UVC.Extension; // LayoutGroupEx가 포함된 네임스페이스 /// /// public class LayoutAlignmentExample : MonoBehaviour /// { /// public LayoutGroup myLayoutGroup; // 인스펙터에서 할당 /// /// void Start() /// { /// if (myLayoutGroup != null) /// { /// // 예시: 모든 직접 자식들을 중앙 정렬 시도 /// // 주의: myLayoutGroup에 활성 Horizontal/Vertical/Grid LayoutGroup이 있다면 충돌 가능 /// myLayoutGroup.AlignChildren(TextAnchor.MiddleCenter); /// } /// } /// } /// /// public static void AlignChildren(this UnityEngine.UI.LayoutGroup layoutGroup, UnityEngine.TextAnchor alignment) { if (layoutGroup == null) throw new ArgumentNullException(nameof(layoutGroup)); for (int i = 0; i < layoutGroup.transform.childCount; i++) { RectTransform rect = layoutGroup.transform.GetChild(i) as RectTransform; if (rect == null) continue; rect.anchorMin = new UnityEngine.Vector2(0.5f, 0.5f); rect.anchorMax = new UnityEngine.Vector2(0.5f, 0.5f); rect.pivot = new UnityEngine.Vector2(0.5f, 0.5f); switch (alignment) { case UnityEngine.TextAnchor.MiddleLeft: rect.localPosition = new UnityEngine.Vector3(-rect.rect.width / 2, 0, 0); break; case UnityEngine.TextAnchor.MiddleCenter: rect.localPosition = UnityEngine.Vector3.zero; break; case UnityEngine.TextAnchor.MiddleRight: rect.localPosition = new UnityEngine.Vector3(rect.rect.width / 2, 0, 0); break; default: Debug.LogWarning($"LayoutGroupEx.AlignChildren: Alignment '{alignment}' is not explicitly supported or might not produce expected results with the current implementation. Only MiddleLeft, MiddleCenter, MiddleRight have defined behaviors."); break; } } // 변경 사항을 즉시 반영하려면 레이아웃 리빌드가 필요할 수 있습니다. // 하지만 활성 레이아웃 그룹이 있다면 이 변경을 무시할 수 있습니다. // if (layoutGroup.GetComponent() != null) // { // LayoutRebuilder.ForceRebuildLayoutImmediate(layoutGroup.GetComponent()); // } } /// /// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 flexibleWidth와 flexibleHeight를 설정하여 강제로 확장/축소하도록 합니다. /// /// 설정을 적용할 LayoutGroup입니다. /// true로 설정하면 자식 요소가 사용 가능한 공간을 채우도록 확장되고 (flexible = 1), false면 축소됩니다 (flexible = 0). /// /// [초보자 설명] /// LayoutElement 컴포넌트는 UI 요소가 LayoutGroup 내에서 어떻게 크기를 조절하고 배치될지 상세하게 제어합니다. /// 'flexibleWidth' 또는 'flexibleHeight' 속성이 0보다 큰 값(보통 1)으로 설정되면, 해당 UI 요소는 LayoutGroup 내에서 /// 다른 'flexible' 요소들과 함께 사용 가능한 여유 공간을 나눠 가집니다. 'expand'를 true로 하면 이 값을 1로, false로 하면 0으로 설정합니다. /// 또한, 'expand'가 true일 때 'preferredWidth'와 'preferredHeight'를 -1로 설정하여 LayoutElement가 크기를 유연하게 조절하도록 합니다. /// 이 메서드는 각 직접 자식 GameObject에 LayoutElement 컴포넌트가 이미 존재한다고 가정합니다. 없다면 해당 자식은 영향을 받지 않습니다. /// /// /// /// using UnityEngine; /// using UnityEngine.UI; /// using UVC.Extension; /// /// public class LayoutExpandExample : MonoBehaviour /// { /// public VerticalLayoutGroup myLayoutGroup; /// /// void Start() /// { /// if (myLayoutGroup != null) /// { /// // 모든 자식들이 세로 및 가로로 확장되도록 설정 /// myLayoutGroup.SetChildForceExpand(true); /// } /// } /// } /// /// public static void SetChildForceExpand(this UnityEngine.UI.LayoutGroup layoutGroup, bool expand) { if (layoutGroup == null) throw new ArgumentNullException(nameof(layoutGroup)); for (int i = 0; i < layoutGroup.transform.childCount; i++) { Transform child = layoutGroup.transform.GetChild(i); LayoutElement element = child.GetComponent(); if (element != null) { element.flexibleWidth = expand ? 1 : 0; element.flexibleHeight = expand ? 1 : 0; // preferredWidth/Height를 -1로 설정하면 LayoutGroup이 크기를 결정하도록 합니다. // expand가 false일 때는 기존 값을 유지하거나 특정 값으로 설정할 수 있습니다. 여기서는 기존 값을 유지하도록 합니다. if (expand) { element.preferredWidth = -1; element.preferredHeight = -1; } } } } /// /// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 flexibleWidth를 설정하여 가로 방향으로 강제 확장/축소하도록 합니다. /// /// 설정을 적용할 LayoutGroup입니다. /// true로 설정하면 자식 요소가 사용 가능한 가로 공간을 채우도록 확장되고 (flexibleWidth = 1), false면 축소됩니다 (flexibleWidth = 0). /// /// [초보자 설명] /// 'flexibleWidth'가 1이면 요소는 LayoutGroup 내의 가용 너비를 다른 flexible 요소들과 나눠 가집니다. /// 'expand'가 true일 때 'preferredWidth'는 -1로 설정되어 너비가 유연하게 조절됩니다. /// 각 직접 자식에 LayoutElement 컴포넌트가 있어야 합니다. /// /// /// /// public HorizontalLayoutGroup myHLayoutGroup; /// void MakeChildrenExpandWidth() /// { /// myHLayoutGroup.SetChildForceExpandWidth(true); /// } /// /// public static void SetChildForceExpandWidth(this UnityEngine.UI.LayoutGroup layoutGroup, bool expand) { if (layoutGroup == null) throw new ArgumentNullException(nameof(layoutGroup)); for (int i = 0; i < layoutGroup.transform.childCount; i++) { Transform child = layoutGroup.transform.GetChild(i); LayoutElement element = child.GetComponent(); if (element != null) { element.flexibleWidth = expand ? 1 : 0; if (expand) { element.preferredWidth = -1; } } } } /// /// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 flexibleHeight를 설정하여 세로 방향으로 강제 확장/축소하도록 합니다. /// /// 설정을 적용할 LayoutGroup입니다. /// true로 설정하면 자식 요소가 사용 가능한 세로 공간을 채우도록 확장되고 (flexibleHeight = 1), false면 축소됩니다 (flexibleHeight = 0). /// /// [초보자 설명] /// 'flexibleHeight'가 1이면 요소는 LayoutGroup 내의 가용 높이를 다른 flexible 요소들과 나눠 가집니다. /// 'expand'가 true일 때 'preferredHeight'는 -1로 설정되어 높이가 유연하게 조절됩니다. /// 각 직접 자식에 LayoutElement 컴포넌트가 있어야 합니다. /// /// /// /// public VerticalLayoutGroup myVLayoutGroup; /// void MakeChildrenExpandHeight() /// { /// myVLayoutGroup.SetChildForceExpandHeight(true); /// } /// /// public static void SetChildForceExpandHeight(this UnityEngine.UI.LayoutGroup layoutGroup, bool expand) { if (layoutGroup == null) throw new ArgumentNullException(nameof(layoutGroup)); for (int i = 0; i < layoutGroup.transform.childCount; i++) { Transform child = layoutGroup.transform.GetChild(i); LayoutElement element = child.GetComponent(); if (element != null) { element.flexibleHeight = expand ? 1 : 0; if (expand) { element.preferredHeight = -1; } } } } /// /// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 최소 너비와 높이를 설정합니다. /// /// 설정을 적용할 LayoutGroup입니다. /// 설정할 최소 너비입니다. /// 설정할 최소 높이입니다. /// /// [초보자 설명] /// 'minWidth'와 'minHeight'는 LayoutElement가 가질 수 있는 최소 크기를 정의합니다. /// LayoutGroup은 자식 요소들을 배치할 때 이 최소 크기보다 작게 만들지 않으려고 시도합니다. /// 각 직접 자식에 LayoutElement 컴포넌트가 있어야 합니다. /// /// /// /// public LayoutGroup myLayoutGroup; /// void SetMinimumSizeForChildren() /// { /// myLayoutGroup.SetChildMinSize(50f, 30f); // 모든 자식의 최소 크기를 너비 50, 높이 30으로 설정 /// } /// /// public static void SetChildMinSize(this UnityEngine.UI.LayoutGroup layoutGroup, float minWidth, float minHeight) { if (layoutGroup == null) throw new ArgumentNullException(nameof(layoutGroup)); for (int i = 0; i < layoutGroup.transform.childCount; i++) { Transform child = layoutGroup.transform.GetChild(i); LayoutElement element = child.GetComponent(); if (element != null) { element.minWidth = minWidth; element.minHeight = minHeight; } } } /// /// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 기본 너비와 높이(preferred size)를 설정합니다. /// /// 설정을 적용할 LayoutGroup입니다. /// 설정할 기본 너비입니다. -1로 설정하면 시스템이 계산한 값을 사용합니다. /// 설정할 기본 높이입니다. -1로 설정하면 시스템이 계산한 값을 사용합니다. /// /// [초보자 설명] /// 'preferredWidth'와 'preferredHeight'는 LayoutElement가 기본적으로 차지하려는 크기를 나타냅니다. /// LayoutGroup은 이 값을 기준으로 자식 요소들을 배치하지만, 공간이 부족하거나 'flexible' 속성이 설정된 경우 실제 크기는 달라질 수 있습니다. /// 값을 -1로 설정하면 LayoutElement는 해당 차원의 기본 크기를 가지지 않고, 다른 요소나 내용에 따라 크기가 결정됩니다. /// 각 직접 자식에 LayoutElement 컴포넌트가 있어야 합니다. /// /// /// /// public LayoutGroup myLayoutGroup; /// void SetPreferredSizeForChildren() /// { /// myLayoutGroup.SetChildPreferredSize(100f, 50f); // 모든 자식의 선호 크기를 너비 100, 높이 50으로 설정 /// } /// /// public static void SetChildPreferredSize(this UnityEngine.UI.LayoutGroup layoutGroup, float preferredWidth, float preferredHeight) { if (layoutGroup == null) throw new ArgumentNullException(nameof(layoutGroup)); for (int i = 0; i < layoutGroup.transform.childCount; i++) { Transform child = layoutGroup.transform.GetChild(i); LayoutElement element = child.GetComponent(); if (element != null) { element.preferredWidth = preferredWidth; element.preferredHeight = preferredHeight; } } } /// /// LayoutGroup의 모든 직접 자식 요소에 대해 LayoutElement의 유연한 너비와 높이(flexible size)를 설정합니다. /// /// 설정을 적용할 LayoutGroup입니다. /// 설정할 유연한 너비 값입니다. 보통 0 (확장 안 함) 또는 1 (확장 함)을 사용합니다. /// 설정할 유연한 높이 값입니다. 보통 0 (확장 안 함) 또는 1 (확장 함)을 사용합니다. /// /// [초보자 설명] /// 'flexibleWidth'와 'flexibleHeight' 값은 LayoutGroup 내에서 여유 공간이 있을 때 해당 요소가 얼마나 그 공간을 차지할지를 결정합니다. /// 값이 0이면 여유 공간을 차지하지 않고, 0보다 큰 값이면 다른 flexible 요소들과 비례하여 공간을 나눠 가집니다. /// 예를 들어, 모든 자식의 flexibleWidth를 1로 설정하면 모든 자식이 동일한 비율로 가로 여유 공간을 나눠 갖습니다. /// 각 직접 자식에 LayoutElement 컴포넌트가 있어야 합니다. /// /// /// /// public LayoutGroup myLayoutGroup; /// void SetFlexibleSizeForChildren() /// { /// myLayoutGroup.SetChildFlexibleSize(1f, 0f); // 모든 자식이 가로로 유연하게 확장되지만, 세로는 아님 /// } /// /// public static void SetChildFlexibleSize(this UnityEngine.UI.LayoutGroup layoutGroup, float flexibleWidth, float flexibleHeight) { if (layoutGroup == null) throw new ArgumentNullException(nameof(layoutGroup)); for (int i = 0; i < layoutGroup.transform.childCount; i++) { Transform child = layoutGroup.transform.GetChild(i); LayoutElement element = child.GetComponent(); if (element != null) { element.flexibleWidth = flexibleWidth; element.flexibleHeight = flexibleHeight; } } } /// /// LayoutGroup의 RectTransform 크기를 지정된 부모 RectTransform의 크기에 맞춥니다. /// /// 크기를 조절할 LayoutGroup입니다. /// 기준이 될 부모 RectTransform입니다. /// /// [초보자 설명] /// 이 메서드는 LayoutGroup 자체의 UI 영역 크기(`sizeDelta`)를 `parentRectTransform`의 실제 렌더링 크기(`size.width`, `size.height`)와 동일하게 만듭니다. /// LayoutGroup의 앵커 설정에 따라 `sizeDelta`의 의미가 달라질 수 있지만, 일반적으로 이 메서드는 LayoutGroup이 부모의 영역을 꽉 채우도록 만듭니다. /// /// /// /// public LayoutGroup myLayoutGroup; /// public RectTransform parentPanel; // myLayoutGroup을 이 패널에 맞추고 싶음 /// /// void FitLayoutToPanel() /// { /// if (myLayoutGroup != null && parentPanel != null) /// { /// myLayoutGroup.FitToParent(parentPanel); /// } /// } /// /// public static void FitToParent(this UnityEngine.UI.LayoutGroup layoutGroup, UnityEngine.RectTransform parentRectTransform) { if (layoutGroup == null) throw new ArgumentNullException(nameof(layoutGroup)); if (parentRectTransform == null) throw new ArgumentNullException(nameof(parentRectTransform)); var rectTransform = layoutGroup.GetComponent(); if (rectTransform == null) { Debug.LogError("LayoutGroupEx.FitToParent: LayoutGroup does not have a RectTransform component.", layoutGroup); return; } rectTransform.sizeDelta = new UnityEngine.Vector2(parentRectTransform.rect.width, parentRectTransform.rect.height); } /// /// LayoutGroup의 RectTransform 넓이와 높이를 모든 자식 요소들을 포함하는 데 필요한 선호 높이(preferred width, height)에 맞춥니다. /// /// 넓이, 높이를 조절할 LayoutGroup입니다. /// /// [초보자 설명] /// 이 메서드는 먼저 `LayoutRebuilder.ForceRebuildLayoutImmediate`를 호출하여 LayoutGroup과 그 자식들의 레이아웃 정보를 최신 상태로 강제 업데이트합니다. /// 그런 다음 `LayoutUtility.GetPreferredWidth, LayoutUtility.GetPreferredHeight`를 사용하여 모든 자식들을 배치하는 데 필요한 전체 넓이, 높이를 계산하고, /// LayoutGroup의 `RectTransform`의 넓이, 높이(`sizeDelta.x`, `sizeDelta.y`)를 이 계산된 선호 넓이, 높이로 설정합니다. /// 이는 주로 ScrollView 내부의 Content Panel처럼 내용에 따라 넓이, 높이가 동적으로 변해야 하는 UI에 유용합니다. /// /// /// /// public VerticalLayoutGroup contentPanelLayoutGroup; // ScrollView의 Content Panel에 붙어있는 LayoutGroup /// /// public void AddNewItemToContent() /// { /// // (새로운 아이템을 contentPanelLayoutGroup의 자식으로 추가하는 코드...) /// // Instantiate(newItemPrefab, contentPanelLayoutGroup.transform); /// /// // 자식들이 추가/변경되었으므로 Content Panel의 높이를 자식들에 맞게 조절 /// contentPanelLayoutGroup.FitToChildren(); /// } /// /// public static void FitToChildren(this UnityEngine.UI.LayoutGroup layoutGroup, bool width = true, bool height = true) // 이름 변경: FitToChdildren -> FitHeightToChildren { if (layoutGroup == null) throw new ArgumentNullException(nameof(layoutGroup)); RectTransform rectTransform = layoutGroup.GetComponent(); if (rectTransform != null) { LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform); Vector2 vector = new Vector2(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y); if (width) { float preferredWidth = LayoutUtility.GetPreferredWidth(rectTransform); vector.x = preferredWidth; } if (height) { float preferredHeight = LayoutUtility.GetPreferredHeight(rectTransform); vector.y = preferredHeight; } rectTransform.sizeDelta = vector; } else { Debug.LogError("LayoutGroupEx.FitHeightToChildren: LayoutGroup does not have a RectTransform component.", layoutGroup); } } } }