feat: 패시브 트리 레이아웃 자동화 및 UI 정리
- 패시브 트리 노드 배치를 삼각형 기반 자동 배치 구조로 전환하고 축 및 브릿지 반경을 재정리\n- 패시브 UI 프리팹과 런타임 렌더링을 수정해 노드 겹침, 링크 관통, 상태별 간격 변화, 하단 여백 문제를 정리\n- 프로토타입 패시브 노드, 트리, 프리셋 자산을 재생성해 최신 레이아웃과 확장 노드 구성을 반영
This commit is contained in:
@@ -10,12 +10,14 @@ namespace Colosseum.UI
|
||||
{
|
||||
[SerializeField] private RectTransform rootRect;
|
||||
[SerializeField] private Image backgroundImage;
|
||||
[SerializeField] private Image fillImage;
|
||||
[SerializeField] private Image innerImage;
|
||||
[SerializeField] private Button button;
|
||||
[SerializeField] private Outline outline;
|
||||
|
||||
public RectTransform RootRect => rootRect;
|
||||
public Image BackgroundImage => backgroundImage;
|
||||
public Image FillImage => fillImage;
|
||||
public Image InnerImage => innerImage;
|
||||
public Button Button => button;
|
||||
public Outline Outline => outline;
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Colosseum.UI
|
||||
public RectTransform RectTransform;
|
||||
public Button Button;
|
||||
public Image Image;
|
||||
public Image FillImage;
|
||||
public Image InnerImage;
|
||||
public Outline Outline;
|
||||
}
|
||||
@@ -69,9 +70,10 @@ namespace Colosseum.UI
|
||||
[SerializeField] private Color statusErrorColor = new Color(1f, 0.52f, 0.45f, 1f);
|
||||
[SerializeField] private Color lineColor = new Color(0.72f, 0.67f, 0.53f, 0.4f);
|
||||
[SerializeField] private Color activeLineColor = new Color(0.98f, 0.9f, 0.7f, 0.92f);
|
||||
[SerializeField] private float graphCenterYOffset = -118f;
|
||||
private const float LeftPanelWidth = 292f;
|
||||
private const float RightPanelWidth = 308f;
|
||||
private const float GraphPadding = 52f;
|
||||
private const float GraphPadding = 24f;
|
||||
private const float ConnectionThickness = 7f;
|
||||
|
||||
private readonly Dictionary<PassiveNodeData, NodeVisual> nodeVisuals = new();
|
||||
@@ -399,8 +401,8 @@ namespace Colosseum.UI
|
||||
|
||||
Rect canvasRect = canvasRectTransform.rect;
|
||||
panelRectTransform.sizeDelta = new Vector2(
|
||||
Mathf.Max(1120f, canvasRect.width - 48f),
|
||||
Mathf.Max(680f, canvasRect.height - 48f));
|
||||
Mathf.Max(1160f, canvasRect.width - 24f),
|
||||
Mathf.Max(720f, canvasRect.height - 24f));
|
||||
}
|
||||
|
||||
private void CreateToggleButton()
|
||||
@@ -884,6 +886,11 @@ namespace Colosseum.UI
|
||||
image.sprite = GetNodeSprite(node);
|
||||
image.type = Image.Type.Simple;
|
||||
image.preserveAspect = true;
|
||||
Image fillImage = nodeView.FillImage;
|
||||
if (fillImage != null)
|
||||
{
|
||||
fillImage.type = Image.Type.Sliced;
|
||||
}
|
||||
Image innerImage = nodeView.InnerImage;
|
||||
if (innerImage != null)
|
||||
{
|
||||
@@ -913,6 +920,7 @@ namespace Colosseum.UI
|
||||
RectTransform = rectTransform,
|
||||
Button = button,
|
||||
Image = image,
|
||||
FillImage = fillImage,
|
||||
InnerImage = innerImage,
|
||||
Outline = nodeView.Outline != null ? nodeView.Outline : button.GetComponent<Outline>(),
|
||||
};
|
||||
@@ -943,7 +951,11 @@ namespace Colosseum.UI
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.AppendLine($"<size=30><b>{focusedNode.DisplayName}</b></size>");
|
||||
if (!string.IsNullOrWhiteSpace(focusedNode.DisplayName))
|
||||
{
|
||||
builder.AppendLine($"<size=30><b>{focusedNode.DisplayName}</b></size>");
|
||||
}
|
||||
|
||||
builder.AppendLine($"<size=18>{PassivePresentationUtility.GetBranchLabel(focusedNode.Branch)} | {PassivePresentationUtility.GetNodeKindLabel(focusedNode.NodeKind)} | 축 {PassivePresentationUtility.GetAxisSummary(focusedNode.AxisMask)}</size>");
|
||||
builder.AppendLine($"<size=18>비용 {focusedNode.Cost}</size>");
|
||||
|
||||
@@ -1091,12 +1103,16 @@ namespace Colosseum.UI
|
||||
visual.Button.interactable = true;
|
||||
visual.Image.sprite = GetNodeSprite(node);
|
||||
visual.Image.color = fillColor;
|
||||
if (visual.FillImage != null)
|
||||
{
|
||||
visual.FillImage.color = GetNodeFillColor(selected, selectable, focused);
|
||||
}
|
||||
if (visual.InnerImage != null)
|
||||
{
|
||||
visual.InnerImage.sprite = GetInnerNodeSprite();
|
||||
visual.InnerImage.color = GetInnerNodeColor(selected, selectable, focused);
|
||||
}
|
||||
visual.RectTransform.localScale = selected ? new Vector3(1.05f, 1.05f, 1f) : focused ? new Vector3(1.03f, 1.03f, 1f) : Vector3.one;
|
||||
visual.RectTransform.localScale = Vector3.one;
|
||||
|
||||
Outline outline = visual.Outline != null ? visual.Outline : visual.Button.GetComponent<Outline>();
|
||||
if (outline == null)
|
||||
@@ -1125,12 +1141,11 @@ namespace Colosseum.UI
|
||||
private Vector2 GetGraphAnchoredPosition(Vector2 layoutPosition)
|
||||
{
|
||||
Rect rect = graphRectTransform != null ? graphRectTransform.rect : new Rect(0f, 0f, 640f, 480f);
|
||||
float halfWidth = Mathf.Max(0f, rect.width * 0.5f - GraphPadding);
|
||||
float halfHeight = Mathf.Max(0f, rect.height * 0.5f - GraphPadding);
|
||||
float halfExtent = Mathf.Max(0f, Mathf.Min(rect.width, rect.height) * 0.5f - GraphPadding);
|
||||
|
||||
return new Vector2(
|
||||
Mathf.Clamp(layoutPosition.x, -1f, 1f) * halfWidth,
|
||||
Mathf.Clamp(layoutPosition.y, -1f, 1f) * halfHeight);
|
||||
Mathf.Clamp(layoutPosition.x, -1f, 1f) * halfExtent,
|
||||
Mathf.Clamp(layoutPosition.y, -1f, 1f) * halfExtent + graphCenterYOffset);
|
||||
}
|
||||
|
||||
private static string BuildConnectionKey(PassiveNodeData leftNode, PassiveNodeData rightNode)
|
||||
@@ -1142,7 +1157,7 @@ namespace Colosseum.UI
|
||||
|
||||
private Sprite GetNodeSprite(PassiveNodeData node)
|
||||
{
|
||||
bool useSpecialSprite = node != null && (node.NodeKind == PassiveNodeKind.Hub || node.NodeKind == PassiveNodeKind.Bridge || node.NodeKind == PassiveNodeKind.Capstone);
|
||||
bool useSpecialSprite = node != null && (node.NodeKind == PassiveNodeKind.Bridge || node.NodeKind == PassiveNodeKind.Capstone);
|
||||
Sprite fallbackSprite = useSpecialSprite ? normalNodeSprite : specialNodeSprite;
|
||||
Sprite preferredSprite = useSpecialSprite ? specialNodeSprite : normalNodeSprite;
|
||||
return preferredSprite != null ? preferredSprite : fallbackSprite;
|
||||
@@ -1170,6 +1185,20 @@ namespace Colosseum.UI
|
||||
return new Color(0.3f, 0.3f, 0.32f, 0.34f);
|
||||
}
|
||||
|
||||
private static Color GetNodeFillColor(bool selected, bool selectable, bool focused)
|
||||
{
|
||||
if (selected)
|
||||
return new Color(0.09f, 0.09f, 0.11f, 0.96f);
|
||||
|
||||
if (focused)
|
||||
return new Color(0.08f, 0.08f, 0.10f, 0.94f);
|
||||
|
||||
if (selectable)
|
||||
return new Color(0.07f, 0.07f, 0.09f, 0.92f);
|
||||
|
||||
return new Color(0.06f, 0.06f, 0.08f, 0.90f);
|
||||
}
|
||||
|
||||
private Color GetNodeBaseColor(PassiveNodeData node)
|
||||
{
|
||||
if (node.Branch == PassiveNodeBranch.Bridge)
|
||||
@@ -1198,19 +1227,21 @@ namespace Colosseum.UI
|
||||
{
|
||||
return node.NodeKind switch
|
||||
{
|
||||
PassiveNodeKind.Hub => new Vector2(88f, 88f),
|
||||
PassiveNodeKind.Capstone => new Vector2(78f, 78f),
|
||||
PassiveNodeKind.Bridge => new Vector2(64f, 64f),
|
||||
_ => new Vector2(70f, 70f),
|
||||
PassiveNodeKind.Hub => new Vector2(78f, 78f),
|
||||
PassiveNodeKind.Capstone => new Vector2(70f, 70f),
|
||||
PassiveNodeKind.Bridge => new Vector2(56f, 56f),
|
||||
_ => new Vector2(62f, 62f),
|
||||
};
|
||||
}
|
||||
|
||||
private static float GetConnectionInset(PassiveNodeData node)
|
||||
{
|
||||
Vector2 size = GetNodeSize(node);
|
||||
float radius = Mathf.Min(size.x, size.y) * 0.42f;
|
||||
float radius = Mathf.Min(size.x, size.y) * 0.5f + ConnectionThickness * 0.5f + 2f;
|
||||
if (node != null && node.NodeKind == PassiveNodeKind.Bridge)
|
||||
radius += 3f;
|
||||
radius += 4f;
|
||||
else if (node != null && (node.NodeKind == PassiveNodeKind.Hub || node.NodeKind == PassiveNodeKind.Capstone))
|
||||
radius += 2f;
|
||||
|
||||
return radius;
|
||||
}
|
||||
@@ -1380,7 +1411,13 @@ namespace Colosseum.UI
|
||||
Transform footerTransform = statusText.transform.parent;
|
||||
if (footerTransform != null)
|
||||
{
|
||||
footerTransform.gameObject.SetActive(hasMessage);
|
||||
Image footerImage = footerTransform.GetComponent<Image>();
|
||||
if (footerImage != null)
|
||||
{
|
||||
Color color = sectionBackgroundColor;
|
||||
color.a = hasMessage ? sectionBackgroundColor.a : 0f;
|
||||
footerImage.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
statusText.text = hasMessage ? lastStatusMessage : string.Empty;
|
||||
|
||||
Reference in New Issue
Block a user