- 패시브 트리 노드 배치를 삼각형 기반 자동 배치 구조로 전환하고 축 및 브릿지 반경을 재정리\n- 패시브 UI 프리팹과 런타임 렌더링을 수정해 노드 겹침, 링크 관통, 상태별 간격 변화, 하단 여백 문제를 정리\n- 프로토타입 패시브 노드, 트리, 프리셋 자산을 재생성해 최신 레이아웃과 확장 노드 구성을 반영
645 lines
33 KiB
C#
645 lines
33 KiB
C#
using System.IO;
|
|
|
|
using TMPro;
|
|
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
using Colosseum.Passives;
|
|
using Colosseum.UI;
|
|
|
|
namespace Colosseum.Editor
|
|
{
|
|
/// <summary>
|
|
/// 패시브 트리 uGUI 프리팹을 재생성하는 에디터 유틸리티입니다.
|
|
/// </summary>
|
|
public static class PassiveTreeUiPrefabBuilder
|
|
{
|
|
private const string PrefabFolder = "Assets/_Game/Prefabs/UI/PassiveTree";
|
|
private const string ViewPrefabPath = PrefabFolder + "/UI_PassiveTreeView.prefab";
|
|
private const string NodePrefabPath = PrefabFolder + "/UI_PassiveTreeNode.prefab";
|
|
private const string PlayerHudPrefabPath = "Assets/_Game/Prefabs/UI/UI_PlayerResources.prefab";
|
|
private const string PreferredFontAssetPath = "Assets/_Game/Fonts/TMP/TMP_MaruBuri.asset";
|
|
private const string NormalIconSpritePath = "Assets/_Game/Icons/Icon_Passive_Normal.png";
|
|
private const string SpecialIconSpritePath = "Assets/_Game/Icons/Icon_Passive_Special.png";
|
|
private const string PassiveCatalogAssetPath = "Assets/_Game/Data/Passives/Data_PassivePrototypeCatalog.asset";
|
|
|
|
private static readonly Color PanelBackgroundColor = new(0.07f, 0.07f, 0.10f, 0.98f);
|
|
private static readonly Color SectionBackgroundColor = new(0.12f, 0.12f, 0.16f, 0.96f);
|
|
private static readonly Color SectionOverlayColor = new(0f, 0f, 0f, 0.12f);
|
|
private static readonly Color ToggleButtonColor = new(0.18f, 0.21f, 0.16f, 0.96f);
|
|
private static readonly Color CloseButtonColor = new(0.34f, 0.20f, 0.20f, 0.96f);
|
|
private static readonly Color ButtonColor = new(0.20f, 0.20f, 0.24f, 0.96f);
|
|
private static readonly Color TextColor = new(0.90f, 0.88f, 0.82f, 1f);
|
|
private static readonly Color NodeColor = new(0.16f, 0.16f, 0.18f, 0.98f);
|
|
private static readonly Color NodeFillColor = new(0.08f, 0.08f, 0.10f, 0.98f);
|
|
private static readonly Color NodeOutlineColor = new(0f, 0f, 0f, 0.45f);
|
|
|
|
[MenuItem("Tools/Colosseum/Passives/Rebuild Passive UI Prefabs")]
|
|
public static void RebuildPassiveUiPrefabs()
|
|
{
|
|
EnsureFolders();
|
|
|
|
PassiveTreeNodeView nodePrefab = BuildNodePrefab();
|
|
PassiveTreeViewReferences viewPrefab = BuildViewPrefab();
|
|
BindPlayerHudPrefab(viewPrefab, nodePrefab);
|
|
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
|
|
Debug.Log("[PassiveTreeUiPrefabBuilder] 패시브 UI 프리팹 재생성을 완료했습니다.");
|
|
}
|
|
|
|
private static void EnsureFolders()
|
|
{
|
|
if (!AssetDatabase.IsValidFolder("Assets/_Game/Prefabs/UI"))
|
|
{
|
|
AssetDatabase.CreateFolder("Assets/_Game/Prefabs", "UI");
|
|
}
|
|
|
|
if (!AssetDatabase.IsValidFolder(PrefabFolder))
|
|
{
|
|
AssetDatabase.CreateFolder("Assets/_Game/Prefabs/UI", "PassiveTree");
|
|
}
|
|
}
|
|
|
|
private static PassiveTreeNodeView BuildNodePrefab()
|
|
{
|
|
GameObject root = new("UI_PassiveTreeNode", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button), typeof(Outline), typeof(PassiveTreeNodeView));
|
|
try
|
|
{
|
|
RectTransform rect = root.GetComponent<RectTransform>();
|
|
rect.sizeDelta = new Vector2(62f, 62f);
|
|
|
|
Image background = root.GetComponent<Image>();
|
|
background.sprite = LoadPassiveNodeSprite(false);
|
|
background.type = Image.Type.Simple;
|
|
background.preserveAspect = true;
|
|
background.color = NodeColor;
|
|
|
|
Outline outline = root.GetComponent<Outline>();
|
|
outline.effectColor = NodeOutlineColor;
|
|
outline.effectDistance = new Vector2(2f, 2f);
|
|
|
|
Button button = root.GetComponent<Button>();
|
|
button.targetGraphic = background;
|
|
|
|
GameObject fillObject = CreateUiObject("Fill", root.transform, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
|
RectTransform fillRect = fillObject.GetComponent<RectTransform>();
|
|
StretchRect(fillRect);
|
|
fillRect.offsetMin = new Vector2(17f, 17f);
|
|
fillRect.offsetMax = new Vector2(-17f, -17f);
|
|
|
|
Image fillImage = fillObject.GetComponent<Image>();
|
|
fillImage.sprite = GetBuiltinSprite();
|
|
fillImage.type = Image.Type.Sliced;
|
|
fillImage.color = NodeFillColor;
|
|
fillImage.raycastTarget = false;
|
|
|
|
GameObject innerObject = CreateUiObject("InnerIcon", root.transform, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
|
RectTransform innerRect = innerObject.GetComponent<RectTransform>();
|
|
StretchRect(innerRect);
|
|
innerRect.offsetMin = new Vector2(9f, 9f);
|
|
innerRect.offsetMax = new Vector2(-9f, -9f);
|
|
|
|
Image innerImage = innerObject.GetComponent<Image>();
|
|
innerImage.sprite = LoadPassiveNodeSprite(false);
|
|
innerImage.type = Image.Type.Simple;
|
|
innerImage.preserveAspect = true;
|
|
innerImage.color = new Color(0.32f, 0.32f, 0.34f, 0.36f);
|
|
innerImage.raycastTarget = false;
|
|
|
|
PassiveTreeNodeView references = root.GetComponent<PassiveTreeNodeView>();
|
|
SetSerializedReference(references, "rootRect", rect);
|
|
SetSerializedReference(references, "backgroundImage", background);
|
|
SetSerializedReference(references, "fillImage", fillImage);
|
|
SetSerializedReference(references, "innerImage", innerImage);
|
|
SetSerializedReference(references, "button", button);
|
|
SetSerializedReference(references, "outline", outline);
|
|
|
|
GameObject saved = SavePrefab(root, NodePrefabPath);
|
|
return saved.GetComponent<PassiveTreeNodeView>();
|
|
}
|
|
finally
|
|
{
|
|
Object.DestroyImmediate(root);
|
|
}
|
|
}
|
|
|
|
private static PassiveTreeViewReferences BuildViewPrefab()
|
|
{
|
|
GameObject root = new("UI_PassiveTreeView", typeof(RectTransform), typeof(PassiveTreeViewReferences));
|
|
try
|
|
{
|
|
RectTransform rootRect = root.GetComponent<RectTransform>();
|
|
StretchRect(rootRect);
|
|
|
|
Button toggleButton = CreateButton(root.transform, "Button_PassiveTree", "패시브", new Vector2(110f, 40f), 20f, FontStyles.Bold, ToggleButtonColor, TextAnchor.MiddleCenter);
|
|
RectTransform toggleRect = toggleButton.GetComponent<RectTransform>();
|
|
toggleRect.anchorMin = new Vector2(1f, 0f);
|
|
toggleRect.anchorMax = new Vector2(1f, 0f);
|
|
toggleRect.pivot = new Vector2(1f, 0f);
|
|
toggleRect.anchoredPosition = new Vector2(-132f, 48f);
|
|
|
|
GameObject overlayRootObject = CreateUiObject("OverlayRoot", root.transform, typeof(RectTransform));
|
|
RectTransform overlayRootRect = overlayRootObject.GetComponent<RectTransform>();
|
|
StretchRect(overlayRootRect);
|
|
overlayRootObject.SetActive(false);
|
|
|
|
Image backdrop = AddImage(overlayRootObject, "Backdrop", new Color(0f, 0f, 0f, 0.52f));
|
|
StretchRect(backdrop.rectTransform);
|
|
|
|
GameObject panelObject = CreateUiObject("Panel", overlayRootObject.transform, typeof(RectTransform), typeof(Image), typeof(VerticalLayoutGroup));
|
|
RectTransform panelRect = panelObject.GetComponent<RectTransform>();
|
|
panelRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
panelRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
panelRect.pivot = new Vector2(0.5f, 0.5f);
|
|
panelRect.sizeDelta = new Vector2(1440f, 900f);
|
|
|
|
Image panelImage = panelObject.GetComponent<Image>();
|
|
panelImage.sprite = GetBuiltinSprite();
|
|
panelImage.type = Image.Type.Sliced;
|
|
panelImage.color = PanelBackgroundColor;
|
|
|
|
VerticalLayoutGroup panelLayout = panelObject.GetComponent<VerticalLayoutGroup>();
|
|
panelLayout.padding = new RectOffset(22, 22, 22, 22);
|
|
panelLayout.spacing = 16f;
|
|
panelLayout.childAlignment = TextAnchor.UpperLeft;
|
|
panelLayout.childControlWidth = true;
|
|
panelLayout.childControlHeight = true;
|
|
panelLayout.childForceExpandWidth = true;
|
|
panelLayout.childForceExpandHeight = false;
|
|
|
|
BuildHeader(panelObject.transform, out TextMeshProUGUI pointsSummaryText, out Button closeButton);
|
|
BuildBody(panelObject.transform,
|
|
out TextMeshProUGUI selectionSummaryText,
|
|
out Button nonePresetButton,
|
|
out Button defensePresetButton,
|
|
out Button supportPresetButton,
|
|
out Button attackPresetButton,
|
|
out Button clearButton,
|
|
out RectTransform graphRect,
|
|
out RectTransform connectionLayer,
|
|
out RectTransform nodeLayer,
|
|
out RectTransform detailContent,
|
|
out TextMeshProUGUI detailText,
|
|
out Button selectNodeButton,
|
|
out TextMeshProUGUI selectNodeButtonLabel);
|
|
BuildFooter(panelObject.transform, out TextMeshProUGUI statusText);
|
|
|
|
PassiveTreeViewReferences references = root.GetComponent<PassiveTreeViewReferences>();
|
|
SetSerializedReference(references, "rootRect", rootRect);
|
|
SetSerializedReference(references, "overlayRoot", overlayRootObject);
|
|
SetSerializedReference(references, "panelRect", panelRect);
|
|
SetSerializedReference(references, "toggleButton", toggleButton);
|
|
SetSerializedReference(references, "toggleButtonLabel", toggleButton.GetComponentInChildren<TextMeshProUGUI>(true));
|
|
SetSerializedReference(references, "pointsSummaryText", pointsSummaryText);
|
|
SetSerializedReference(references, "closeButton", closeButton);
|
|
SetSerializedReference(references, "selectionSummaryText", selectionSummaryText);
|
|
SetSerializedReference(references, "nonePresetButton", nonePresetButton);
|
|
SetSerializedReference(references, "defensePresetButton", defensePresetButton);
|
|
SetSerializedReference(references, "supportPresetButton", supportPresetButton);
|
|
SetSerializedReference(references, "attackPresetButton", attackPresetButton);
|
|
SetSerializedReference(references, "clearButton", clearButton);
|
|
SetSerializedReference(references, "graphRect", graphRect);
|
|
SetSerializedReference(references, "connectionLayer", connectionLayer);
|
|
SetSerializedReference(references, "nodeLayer", nodeLayer);
|
|
SetSerializedReference(references, "detailContent", detailContent);
|
|
SetSerializedReference(references, "detailText", detailText);
|
|
SetSerializedReference(references, "selectNodeButton", selectNodeButton);
|
|
SetSerializedReference(references, "selectNodeButtonLabel", selectNodeButtonLabel);
|
|
SetSerializedReference(references, "statusText", statusText);
|
|
|
|
GameObject saved = SavePrefab(root, ViewPrefabPath);
|
|
return saved.GetComponent<PassiveTreeViewReferences>();
|
|
}
|
|
finally
|
|
{
|
|
Object.DestroyImmediate(root);
|
|
}
|
|
}
|
|
|
|
private static void BindPlayerHudPrefab(PassiveTreeViewReferences viewPrefab, PassiveTreeNodeView nodePrefab)
|
|
{
|
|
GameObject root = PrefabUtility.LoadPrefabContents(PlayerHudPrefabPath);
|
|
try
|
|
{
|
|
PassiveTreeUI passiveTreeUi = root.GetComponent<PassiveTreeUI>();
|
|
if (passiveTreeUi == null)
|
|
{
|
|
passiveTreeUi = root.AddComponent<PassiveTreeUI>();
|
|
}
|
|
|
|
SerializedObject passiveTreeUiObject = new(passiveTreeUi);
|
|
passiveTreeUiObject.FindProperty("viewPrefab").objectReferenceValue = viewPrefab;
|
|
passiveTreeUiObject.FindProperty("nodePrefab").objectReferenceValue = nodePrefab;
|
|
passiveTreeUiObject.FindProperty("passivePrototypeCatalog").objectReferenceValue = LoadPassivePrototypeCatalog();
|
|
passiveTreeUiObject.FindProperty("normalNodeSprite").objectReferenceValue = LoadPassiveNodeSprite(false);
|
|
passiveTreeUiObject.FindProperty("specialNodeSprite").objectReferenceValue = LoadPassiveNodeSprite(true);
|
|
passiveTreeUiObject.FindProperty("innerNodeSprite").objectReferenceValue = LoadPassiveNodeSprite(false);
|
|
passiveTreeUiObject.ApplyModifiedPropertiesWithoutUndo();
|
|
|
|
PrefabUtility.SaveAsPrefabAsset(root, PlayerHudPrefabPath);
|
|
}
|
|
finally
|
|
{
|
|
PrefabUtility.UnloadPrefabContents(root);
|
|
}
|
|
}
|
|
|
|
private static void BuildHeader(Transform parent, out TextMeshProUGUI pointsSummaryText, out Button closeButton)
|
|
{
|
|
GameObject headerObject = CreateUiObject("Header", parent, typeof(RectTransform), typeof(Image), typeof(LayoutElement), typeof(HorizontalLayoutGroup));
|
|
RectTransform headerRect = headerObject.GetComponent<RectTransform>();
|
|
headerObject.GetComponent<LayoutElement>().minHeight = 54f;
|
|
|
|
Image headerImage = headerObject.GetComponent<Image>();
|
|
headerImage.sprite = GetBuiltinSprite();
|
|
headerImage.type = Image.Type.Sliced;
|
|
headerImage.color = SectionBackgroundColor;
|
|
|
|
HorizontalLayoutGroup headerLayout = headerObject.GetComponent<HorizontalLayoutGroup>();
|
|
headerLayout.padding = new RectOffset(14, 14, 8, 8);
|
|
headerLayout.spacing = 10f;
|
|
headerLayout.childAlignment = TextAnchor.MiddleLeft;
|
|
headerLayout.childControlWidth = true;
|
|
headerLayout.childControlHeight = true;
|
|
headerLayout.childForceExpandWidth = false;
|
|
headerLayout.childForceExpandHeight = false;
|
|
|
|
GameObject titleGroup = CreateUiObject("TitleGroup", headerObject.transform, typeof(RectTransform), typeof(LayoutElement), typeof(VerticalLayoutGroup));
|
|
titleGroup.GetComponent<LayoutElement>().flexibleWidth = 1f;
|
|
VerticalLayoutGroup titleLayout = titleGroup.GetComponent<VerticalLayoutGroup>();
|
|
titleLayout.spacing = 0f;
|
|
titleLayout.childAlignment = TextAnchor.MiddleLeft;
|
|
titleLayout.childControlWidth = true;
|
|
titleLayout.childControlHeight = true;
|
|
titleLayout.childForceExpandWidth = true;
|
|
titleLayout.childForceExpandHeight = false;
|
|
|
|
TextMeshProUGUI title = CreateText(titleGroup.transform, "Label_Title", 24f, TextAlignmentOptions.Left, FontStyles.Bold);
|
|
title.text = "패시브 트리";
|
|
title.textWrappingMode = TextWrappingModes.NoWrap;
|
|
|
|
pointsSummaryText = CreateText(headerObject.transform, "Label_Points", 17f, TextAlignmentOptions.Right, FontStyles.Normal);
|
|
pointsSummaryText.text = string.Empty;
|
|
pointsSummaryText.textWrappingMode = TextWrappingModes.NoWrap;
|
|
pointsSummaryText.gameObject.AddComponent<LayoutElement>().preferredWidth = 420f;
|
|
|
|
closeButton = CreateButton(headerObject.transform, "Button_Close", "닫기", new Vector2(84f, 34f), 18f, FontStyles.Bold, CloseButtonColor, TextAnchor.MiddleCenter);
|
|
}
|
|
|
|
private static void BuildBody(
|
|
Transform parent,
|
|
out TextMeshProUGUI selectionSummaryText,
|
|
out Button nonePresetButton,
|
|
out Button defensePresetButton,
|
|
out Button supportPresetButton,
|
|
out Button attackPresetButton,
|
|
out Button clearButton,
|
|
out RectTransform graphRect,
|
|
out RectTransform connectionLayer,
|
|
out RectTransform nodeLayer,
|
|
out RectTransform detailContent,
|
|
out TextMeshProUGUI detailText,
|
|
out Button selectNodeButton,
|
|
out TextMeshProUGUI selectNodeButtonLabel)
|
|
{
|
|
GameObject bodyObject = CreateUiObject("Body", parent, typeof(RectTransform), typeof(LayoutElement), typeof(HorizontalLayoutGroup));
|
|
LayoutElement bodyLayoutElement = bodyObject.GetComponent<LayoutElement>();
|
|
bodyLayoutElement.flexibleHeight = 1f;
|
|
|
|
HorizontalLayoutGroup bodyLayout = bodyObject.GetComponent<HorizontalLayoutGroup>();
|
|
bodyLayout.spacing = 16f;
|
|
bodyLayout.childAlignment = TextAnchor.UpperLeft;
|
|
bodyLayout.childControlWidth = true;
|
|
bodyLayout.childControlHeight = true;
|
|
bodyLayout.childForceExpandWidth = true;
|
|
bodyLayout.childForceExpandHeight = true;
|
|
|
|
RectTransform graphSection = CreateSectionRoot(bodyObject.transform, "Section_Graph", 0f);
|
|
graphSection.GetComponent<LayoutElement>().flexibleWidth = 1f;
|
|
graphSection.GetComponent<LayoutElement>().minWidth = 820f;
|
|
CreateSectionTitle(graphSection, "트리 그래프");
|
|
|
|
GameObject graphSurface = CreateUiObject("GraphSurface", graphSection, typeof(RectTransform), typeof(LayoutElement), typeof(Image));
|
|
graphRect = graphSurface.GetComponent<RectTransform>();
|
|
LayoutElement graphLayout = graphSurface.GetComponent<LayoutElement>();
|
|
graphLayout.flexibleHeight = 1f;
|
|
graphLayout.minHeight = 640f;
|
|
|
|
Image graphImage = graphSurface.GetComponent<Image>();
|
|
graphImage.sprite = GetBuiltinSprite();
|
|
graphImage.type = Image.Type.Sliced;
|
|
graphImage.color = SectionOverlayColor;
|
|
|
|
connectionLayer = CreateUiObject("Connections", graphSurface.transform, typeof(RectTransform)).GetComponent<RectTransform>();
|
|
StretchRect(connectionLayer);
|
|
nodeLayer = CreateUiObject("Nodes", graphSurface.transform, typeof(RectTransform)).GetComponent<RectTransform>();
|
|
StretchRect(nodeLayer);
|
|
|
|
RectTransform rightSection = CreateSectionRoot(bodyObject.transform, "Section_Right", 360f);
|
|
CreateSectionTitle(rightSection, "노드 상세");
|
|
RectTransform detailScroll = CreateScrollView(rightSection, "Scroll_Detail", out detailContent);
|
|
LayoutElement detailScrollLayout = detailScroll.gameObject.AddComponent<LayoutElement>();
|
|
detailScrollLayout.flexibleHeight = 1f;
|
|
detailScrollLayout.minHeight = 300f;
|
|
|
|
detailText = CreateText(detailContent, "Label_Detail", 19f, TextAlignmentOptions.TopLeft, FontStyles.Normal);
|
|
detailText.textWrappingMode = TextWrappingModes.Normal;
|
|
detailText.overflowMode = TextOverflowModes.Overflow;
|
|
detailText.margin = new Vector4(18f, 8f, 18f, 14f);
|
|
detailText.extraPadding = true;
|
|
detailText.gameObject.AddComponent<LayoutElement>().minHeight = 300f;
|
|
|
|
selectNodeButton = CreateButton(rightSection, "Button_SelectNode", "노드 선택", new Vector2(0f, 58f), 21f, FontStyles.Bold, ButtonColor, TextAnchor.MiddleCenter);
|
|
selectNodeButtonLabel = selectNodeButton.GetComponentInChildren<TextMeshProUGUI>(true);
|
|
|
|
CreateDivider(rightSection, "Divider_Actions");
|
|
|
|
selectionSummaryText = CreateInfoText(rightSection, "Label_SelectionSummary", 132f);
|
|
|
|
CreateSectionTitle(rightSection, "프리셋");
|
|
GameObject presetGrid = CreateUiObject("PresetGrid", rightSection, typeof(RectTransform), typeof(GridLayoutGroup));
|
|
GridLayoutGroup presetLayout = presetGrid.GetComponent<GridLayoutGroup>();
|
|
presetLayout.cellSize = new Vector2(154f, 56f);
|
|
presetLayout.spacing = new Vector2(8f, 8f);
|
|
presetLayout.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
|
|
presetLayout.constraintCount = 2;
|
|
|
|
nonePresetButton = CreateButton(presetGrid.transform, "Button_Preset_None", "패시브 없음", Vector2.zero, 18f, FontStyles.Normal, ButtonColor, TextAnchor.MiddleCenter);
|
|
defensePresetButton = CreateButton(presetGrid.transform, "Button_Preset_Defense", "방어형 패시브", Vector2.zero, 18f, FontStyles.Normal, ButtonColor, TextAnchor.MiddleCenter);
|
|
supportPresetButton = CreateButton(presetGrid.transform, "Button_Preset_Support", "지원형 패시브", Vector2.zero, 18f, FontStyles.Normal, ButtonColor, TextAnchor.MiddleCenter);
|
|
attackPresetButton = CreateButton(presetGrid.transform, "Button_Preset_Attack", "공격형 패시브", Vector2.zero, 18f, FontStyles.Normal, ButtonColor, TextAnchor.MiddleCenter);
|
|
clearButton = CreateButton(rightSection, "Button_Clear", "전체 초기화", new Vector2(0f, 52f), 18f, FontStyles.Bold, ButtonColor, TextAnchor.MiddleCenter);
|
|
}
|
|
|
|
private static void BuildFooter(Transform parent, out TextMeshProUGUI statusText)
|
|
{
|
|
GameObject footerObject = CreateUiObject("Footer", parent, typeof(RectTransform), typeof(Image), typeof(LayoutElement), typeof(HorizontalLayoutGroup));
|
|
footerObject.GetComponent<LayoutElement>().minHeight = 42f;
|
|
|
|
Image footerImage = footerObject.GetComponent<Image>();
|
|
footerImage.sprite = GetBuiltinSprite();
|
|
footerImage.type = Image.Type.Sliced;
|
|
footerImage.color = SectionBackgroundColor;
|
|
|
|
HorizontalLayoutGroup footerLayout = footerObject.GetComponent<HorizontalLayoutGroup>();
|
|
footerLayout.padding = new RectOffset(16, 16, 8, 8);
|
|
footerLayout.childAlignment = TextAnchor.MiddleLeft;
|
|
footerLayout.childControlWidth = true;
|
|
footerLayout.childControlHeight = true;
|
|
footerLayout.childForceExpandWidth = true;
|
|
footerLayout.childForceExpandHeight = true;
|
|
|
|
statusText = CreateText(footerObject.transform, "Label_Status", 17f, TextAlignmentOptions.MidlineLeft, FontStyles.Normal);
|
|
statusText.text = string.Empty;
|
|
statusText.textWrappingMode = TextWrappingModes.NoWrap;
|
|
statusText.overflowMode = TextOverflowModes.Ellipsis;
|
|
footerObject.SetActive(true);
|
|
}
|
|
|
|
private static RectTransform CreateSectionRoot(Transform parent, string name, float preferredWidth)
|
|
{
|
|
GameObject sectionObject = CreateUiObject(name, parent, typeof(RectTransform), typeof(Image), typeof(LayoutElement), typeof(VerticalLayoutGroup));
|
|
RectTransform rect = sectionObject.GetComponent<RectTransform>();
|
|
LayoutElement layoutElement = sectionObject.GetComponent<LayoutElement>();
|
|
layoutElement.flexibleHeight = 1f;
|
|
layoutElement.preferredWidth = preferredWidth;
|
|
if (preferredWidth > 0f)
|
|
{
|
|
layoutElement.minWidth = preferredWidth;
|
|
layoutElement.flexibleWidth = 0f;
|
|
}
|
|
|
|
Image sectionImage = sectionObject.GetComponent<Image>();
|
|
sectionImage.sprite = GetBuiltinSprite();
|
|
sectionImage.type = Image.Type.Sliced;
|
|
sectionImage.color = SectionBackgroundColor;
|
|
|
|
VerticalLayoutGroup layout = sectionObject.GetComponent<VerticalLayoutGroup>();
|
|
layout.padding = new RectOffset(16, 16, 16, 16);
|
|
layout.spacing = 12f;
|
|
layout.childAlignment = TextAnchor.UpperLeft;
|
|
layout.childControlWidth = true;
|
|
layout.childControlHeight = true;
|
|
layout.childForceExpandWidth = true;
|
|
layout.childForceExpandHeight = false;
|
|
|
|
return rect;
|
|
}
|
|
|
|
private static TextMeshProUGUI CreateSectionTitle(RectTransform parent, string text)
|
|
{
|
|
TextMeshProUGUI label = CreateText(parent, "Label_Title", 28f, TextAlignmentOptions.Left, FontStyles.Bold);
|
|
label.text = text;
|
|
return label;
|
|
}
|
|
|
|
private static TextMeshProUGUI CreateInfoText(RectTransform parent, string name, float minHeight)
|
|
{
|
|
GameObject containerObject = CreateUiObject($"{name}_Container", parent, typeof(RectTransform), typeof(Image), typeof(LayoutElement), typeof(VerticalLayoutGroup));
|
|
RectTransform containerRect = containerObject.GetComponent<RectTransform>();
|
|
containerObject.GetComponent<LayoutElement>().minHeight = minHeight;
|
|
|
|
Image containerImage = containerObject.GetComponent<Image>();
|
|
containerImage.sprite = GetBuiltinSprite();
|
|
containerImage.type = Image.Type.Sliced;
|
|
containerImage.color = SectionOverlayColor;
|
|
|
|
VerticalLayoutGroup layout = containerObject.GetComponent<VerticalLayoutGroup>();
|
|
layout.padding = new RectOffset(16, 16, 12, 12);
|
|
layout.childAlignment = TextAnchor.UpperLeft;
|
|
layout.childControlWidth = true;
|
|
layout.childControlHeight = true;
|
|
layout.childForceExpandWidth = true;
|
|
layout.childForceExpandHeight = false;
|
|
|
|
TextMeshProUGUI label = CreateText(containerRect, name, 18f, TextAlignmentOptions.TopLeft, FontStyles.Normal);
|
|
label.textWrappingMode = TextWrappingModes.Normal;
|
|
label.overflowMode = TextOverflowModes.Overflow;
|
|
label.margin = new Vector4(14f, 6f, 14f, 10f);
|
|
label.extraPadding = true;
|
|
return label;
|
|
}
|
|
|
|
private static void CreateDivider(Transform parent, string name)
|
|
{
|
|
GameObject dividerObject = CreateUiObject(name, parent, typeof(RectTransform), typeof(Image), typeof(LayoutElement));
|
|
dividerObject.GetComponent<Image>().color = new Color(0f, 0f, 0f, 0.22f);
|
|
LayoutElement layout = dividerObject.GetComponent<LayoutElement>();
|
|
layout.minHeight = 2f;
|
|
layout.preferredHeight = 2f;
|
|
}
|
|
|
|
private static RectTransform CreateScrollView(Transform parent, string name, out RectTransform content)
|
|
{
|
|
GameObject root = CreateUiObject(name, parent, typeof(RectTransform), typeof(Image), typeof(ScrollRect));
|
|
RectTransform rootRect = root.GetComponent<RectTransform>();
|
|
|
|
Image rootImage = root.GetComponent<Image>();
|
|
rootImage.sprite = GetBuiltinSprite();
|
|
rootImage.type = Image.Type.Sliced;
|
|
rootImage.color = SectionOverlayColor;
|
|
|
|
ScrollRect scrollRect = root.GetComponent<ScrollRect>();
|
|
scrollRect.horizontal = false;
|
|
scrollRect.vertical = true;
|
|
scrollRect.scrollSensitivity = 30f;
|
|
|
|
GameObject viewportObject = CreateUiObject("Viewport", root.transform, typeof(RectTransform), typeof(Image), typeof(Mask));
|
|
RectTransform viewport = viewportObject.GetComponent<RectTransform>();
|
|
StretchRect(viewport);
|
|
viewportObject.GetComponent<Image>().color = new Color(1f, 1f, 1f, 0.01f);
|
|
viewportObject.GetComponent<Mask>().showMaskGraphic = false;
|
|
|
|
GameObject contentObject = CreateUiObject("Content", viewportObject.transform, typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
|
|
content = contentObject.GetComponent<RectTransform>();
|
|
content.anchorMin = new Vector2(0f, 1f);
|
|
content.anchorMax = new Vector2(1f, 1f);
|
|
content.pivot = new Vector2(0f, 1f);
|
|
|
|
VerticalLayoutGroup contentLayout = contentObject.GetComponent<VerticalLayoutGroup>();
|
|
contentLayout.padding = new RectOffset(16, 16, 14, 14);
|
|
contentLayout.spacing = 8f;
|
|
contentLayout.childAlignment = TextAnchor.UpperLeft;
|
|
contentLayout.childControlWidth = true;
|
|
contentLayout.childControlHeight = true;
|
|
contentLayout.childForceExpandWidth = true;
|
|
contentLayout.childForceExpandHeight = false;
|
|
|
|
ContentSizeFitter fitter = contentObject.GetComponent<ContentSizeFitter>();
|
|
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
|
|
|
scrollRect.viewport = viewport;
|
|
scrollRect.content = content;
|
|
return rootRect;
|
|
}
|
|
|
|
private static Button CreateButton(Transform parent, string name, string labelText, Vector2 size, float fontSize, FontStyles fontStyle, Color color, TextAnchor anchor)
|
|
{
|
|
GameObject buttonObject = CreateUiObject(name, parent, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
|
|
RectTransform rect = buttonObject.GetComponent<RectTransform>();
|
|
if (size.sqrMagnitude > 0f)
|
|
{
|
|
rect.sizeDelta = size;
|
|
}
|
|
|
|
Image image = buttonObject.GetComponent<Image>();
|
|
image.sprite = GetBuiltinSprite();
|
|
image.type = Image.Type.Sliced;
|
|
image.color = color;
|
|
|
|
Button button = buttonObject.GetComponent<Button>();
|
|
button.targetGraphic = image;
|
|
|
|
TextMeshProUGUI label = CreateText(buttonObject.transform, "Label", fontSize, ToTmpAlignment(anchor), fontStyle);
|
|
label.text = labelText;
|
|
StretchRect(label.rectTransform);
|
|
label.color = Color.white;
|
|
label.textWrappingMode = TextWrappingModes.Normal;
|
|
|
|
return button;
|
|
}
|
|
|
|
private static TextMeshProUGUI CreateText(Transform parent, string name, float fontSize, TextAlignmentOptions alignment, FontStyles fontStyle)
|
|
{
|
|
GameObject textObject = CreateUiObject(name, parent, typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
|
|
RectTransform rect = textObject.GetComponent<RectTransform>();
|
|
rect.anchorMin = new Vector2(0f, 1f);
|
|
rect.anchorMax = new Vector2(1f, 1f);
|
|
rect.pivot = new Vector2(0f, 1f);
|
|
|
|
TextMeshProUGUI text = textObject.GetComponent<TextMeshProUGUI>();
|
|
text.font = LoadPreferredFontAsset();
|
|
text.fontSize = fontSize;
|
|
text.fontStyle = fontStyle;
|
|
text.color = TextColor;
|
|
text.alignment = alignment;
|
|
text.textWrappingMode = TextWrappingModes.Normal;
|
|
text.extraPadding = true;
|
|
text.raycastTarget = false;
|
|
return text;
|
|
}
|
|
|
|
private static TMP_FontAsset LoadPreferredFontAsset()
|
|
{
|
|
TMP_FontAsset preferredFont = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(PreferredFontAssetPath);
|
|
if (preferredFont != null)
|
|
return preferredFont;
|
|
|
|
return TMP_Settings.defaultFontAsset;
|
|
}
|
|
|
|
private static Sprite LoadPassiveNodeSprite(bool special)
|
|
{
|
|
string assetPath = special ? SpecialIconSpritePath : NormalIconSpritePath;
|
|
return AssetDatabase.LoadAssetAtPath<Sprite>(assetPath);
|
|
}
|
|
|
|
private static PassivePrototypeCatalogData LoadPassivePrototypeCatalog()
|
|
{
|
|
return AssetDatabase.LoadAssetAtPath<PassivePrototypeCatalogData>(PassiveCatalogAssetPath);
|
|
}
|
|
|
|
private static Image AddImage(GameObject gameObject, string childName, Color color)
|
|
{
|
|
GameObject imageObject = CreateUiObject(childName, gameObject.transform, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
|
Image image = imageObject.GetComponent<Image>();
|
|
image.sprite = GetBuiltinSprite();
|
|
image.type = Image.Type.Sliced;
|
|
image.color = color;
|
|
return image;
|
|
}
|
|
|
|
private static GameObject CreateUiObject(string name, Transform parent, params System.Type[] componentTypes)
|
|
{
|
|
GameObject gameObject = new(name, componentTypes);
|
|
gameObject.transform.SetParent(parent, false);
|
|
return gameObject;
|
|
}
|
|
|
|
private static void StretchRect(RectTransform rectTransform)
|
|
{
|
|
rectTransform.anchorMin = Vector2.zero;
|
|
rectTransform.anchorMax = Vector2.one;
|
|
rectTransform.offsetMin = Vector2.zero;
|
|
rectTransform.offsetMax = Vector2.zero;
|
|
rectTransform.anchoredPosition = Vector2.zero;
|
|
rectTransform.sizeDelta = Vector2.zero;
|
|
}
|
|
|
|
private static void SetSerializedReference(Object target, string propertyName, Object value)
|
|
{
|
|
SerializedObject serializedObject = new(target);
|
|
serializedObject.FindProperty(propertyName).objectReferenceValue = value;
|
|
serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
|
}
|
|
|
|
private static GameObject SavePrefab(GameObject root, string path)
|
|
{
|
|
PrefabUtility.SaveAsPrefabAsset(root, path);
|
|
return AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
|
}
|
|
|
|
private static Sprite GetBuiltinSprite()
|
|
{
|
|
return AssetDatabase.GetBuiltinExtraResource<Sprite>("UI/Skin/UISprite.psd");
|
|
}
|
|
|
|
private static TextAlignmentOptions ToTmpAlignment(TextAnchor anchor)
|
|
{
|
|
return anchor switch
|
|
{
|
|
TextAnchor.MiddleCenter => TextAlignmentOptions.Center,
|
|
TextAnchor.MiddleLeft => TextAlignmentOptions.MidlineLeft,
|
|
_ => TextAlignmentOptions.Center,
|
|
};
|
|
}
|
|
}
|
|
}
|