Files
jongjae0305 c335995a9a feat: update song selection, score UI, and song creator features
- SongSelectManager/SongDetailPanel: 곡 선택 및 상세 패널 개선
- SongCreatorManager: 곡 생성 기능 추가
- FinalScoreLabel/ScoreManager: 결과 화면 점수 UI 업데이트
- MarqueeText: 마퀴 텍스트 컴포넌트 개선
- NoteData/SongController: 노트 데이터 및 컨트롤러 보완

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:29:50 +09:00

341 lines
13 KiB
C#

using UnityEngine;
using TMPro;
namespace VRBeats
{
public class FinalScoreLabel : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI scoreText = null;
[SerializeField] private int length = 10;
private string initialValue = "";
private ScoreManager scoreManager = null;
private GameObject resultRoot = null;
private TextMeshProUGUI rankBackText = null;
private TextMeshProUGUI rankShadowText = null;
private TextMeshProUGUI rankDepthText = null;
private TextMeshProUGUI rankRimText = null;
private TextMeshProUGUI rankMainText = null;
private TextMeshProUGUI rankHighlightText = null;
private TextMeshProUGUI resultScoreText = null;
private TextMeshProUGUI resultAccuracyText = null;
private TextMeshProUGUI resultComboText = null;
private CanvasGroup scoreHudCanvasGroup = null;
private void Awake()
{
for (int n = 0; n < length; n++)
initialValue += "0";
scoreManager = FindFirstObjectByType<ScoreManager>();
if (scoreManager != null)
scoreHudCanvasGroup = scoreManager.GetComponent<CanvasGroup>() ??
scoreManager.gameObject.AddComponent<CanvasGroup>();
ApplyPopupTextStyle();
BuildResultLayout();
}
public void ShowScore()
{
if (scoreText == null || scoreManager == null)
return;
SetTitleActive(false);
SetScoreHudVisible(false);
gameObject.CancelAllTweens();
if (resultRoot != null)
{
scoreText.gameObject.SetActive(false);
PopulateResultLayout();
resultRoot.SetActive(true);
}
else
{
scoreText.text = scoreManager.BuildResultSummary(length);
}
}
public void ResetValues()
{
gameObject.CancelAllTweens();
if (resultRoot != null)
resultRoot.SetActive(false);
if (scoreText != null)
scoreText.gameObject.SetActive(true);
SetTitleActive(true);
SetScoreHudVisible(true);
ApplyPopupTextStyle();
if (scoreText != null)
scoreText.text = initialValue;
}
private void PopulateResultLayout()
{
if (scoreManager == null ||
rankBackText == null ||
rankShadowText == null ||
rankDepthText == null ||
rankRimText == null ||
rankMainText == null ||
rankHighlightText == null ||
resultScoreText == null ||
resultAccuracyText == null ||
resultComboText == null)
return;
string rank = scoreManager.Rank;
Color mainColor = HexToColor(scoreManager.RankColorHex);
Color depthColor = HexToColor(GetRankDepthColorHex(rank));
Color rimColor = HexToColor(GetRankRimColorHex(rank));
rankBackText.text = rank;
rankShadowText.text = rank;
rankDepthText.text = rank;
rankDepthText.color = depthColor;
rankRimText.text = rank;
rankMainText.text = rank;
rankHighlightText.text = rank;
ApplyMetalRankColors(mainColor, depthColor, rimColor);
resultScoreText.text =
$"<size=56%><color=#A0C8FF>SCORE</color></size>\n{scoreManager.CurrentScore:N0}";
resultAccuracyText.text =
$"<color=#A0C8FF>ACCURACY</color> {scoreManager.AccuracyPercent:0.0}%";
resultComboText.text = $"MAX COMBO {scoreManager.MaxCombo}";
}
private void SetTitleActive(bool active)
{
Transform titleObj = scoreText != null
? scoreText.rectTransform.parent?.Find("Title")
: null;
if (titleObj != null)
titleObj.gameObject.SetActive(active);
}
private void ApplyPopupTextStyle()
{
if (scoreText == null)
return;
RectTransform rect = scoreText.rectTransform;
rect.anchorMin = new Vector2(0.5f, 0.5f);
rect.anchorMax = new Vector2(0.5f, 0.5f);
rect.anchoredPosition = new Vector2(0.0f, 0.0f);
rect.sizeDelta = new Vector2(620.0f, 250.0f);
scoreText.enableAutoSizing = true;
scoreText.fontSizeMin = 1.2f;
scoreText.fontSizeMax = 5.5f;
scoreText.alignment = TextAlignmentOptions.MidlineLeft;
scoreText.overflowMode = TextOverflowModes.Truncate;
scoreText.textWrappingMode = TextWrappingModes.NoWrap;
scoreText.lineSpacing = -10.0f;
scoreText.color = Color.white;
scoreText.richText = true;
}
private void SetScoreHudVisible(bool visible)
{
if (scoreHudCanvasGroup == null)
return;
scoreHudCanvasGroup.alpha = visible ? 1.0f : 0.0f;
scoreHudCanvasGroup.interactable = false;
scoreHudCanvasGroup.blocksRaycasts = false;
}
private void BuildResultLayout()
{
if (scoreText == null)
return;
Transform parent = scoreText.rectTransform.parent;
if (parent == null)
return;
GameObject root = new GameObject("ResultLayoutRoot");
root.transform.SetParent(parent, false);
RectTransform rootRect = root.AddComponent<RectTransform>();
rootRect.anchorMin = new Vector2(0.5f, 0.5f);
rootRect.anchorMax = new Vector2(0.5f, 0.5f);
rootRect.anchoredPosition = new Vector2(5.0f, 3.1f);
rootRect.sizeDelta = new Vector2(82.0f, 34.0f);
root.SetActive(false);
resultRoot = root;
// Panel-local coordinates are small world-canvas units, not screen pixels.
rankBackText = MakeTmpLabel(root.transform, "RankBackText",
new Vector2(-20.9f, -1.1f), new Vector2(37.0f, 27.0f), 16.0f,
new Color(0.0f, 0.0f, 0.0f, 0.48f), TextAlignmentOptions.Midline);
rankShadowText = MakeTmpLabel(root.transform, "RankShadowText",
new Vector2(-21.35f, -0.55f), new Vector2(37.0f, 27.0f), 16.0f,
new Color(0.0f, 0.06f, 0.14f, 0.82f), TextAlignmentOptions.Midline);
rankDepthText = MakeTmpLabel(root.transform, "RankDepthText",
new Vector2(-21.8f, -0.1f), new Vector2(37.0f, 27.0f), 16.0f,
Color.white, TextAlignmentOptions.Midline);
rankRimText = MakeTmpLabel(root.transform, "RankRimText",
new Vector2(-22.15f, 0.22f), new Vector2(37.0f, 27.0f), 16.0f,
Color.white, TextAlignmentOptions.Midline);
rankMainText = MakeTmpLabel(root.transform, "RankMainText",
new Vector2(-22.45f, 0.5f), new Vector2(37.0f, 27.0f), 16.0f,
Color.white, TextAlignmentOptions.Midline);
rankHighlightText = MakeTmpLabel(root.transform, "RankHighlightText",
new Vector2(-22.85f, 1.0f), new Vector2(37.0f, 27.0f), 16.0f,
Color.white, TextAlignmentOptions.Midline);
resultScoreText = MakeTmpLabel(root.transform, "ResultScoreText",
new Vector2(16.8f, 7.4f), new Vector2(43.0f, 10.8f), 5.35f,
Color.white, TextAlignmentOptions.MidlineLeft);
resultAccuracyText = MakeTmpLabel(root.transform, "ResultAccuracyText",
new Vector2(16.8f, -1.2f), new Vector2(43.0f, 5.4f), 3.05f,
new Color(0.84f, 0.97f, 1.0f, 0.9f), TextAlignmentOptions.MidlineLeft);
resultComboText = MakeTmpLabel(root.transform, "ResultComboText",
new Vector2(16.8f, -6.8f), new Vector2(43.0f, 5.4f), 2.95f,
new Color(0.84f, 0.97f, 1.0f, 1.0f), TextAlignmentOptions.MidlineLeft);
ConfigureRankLayer(rankBackText, new Color(0.0f, 0.0f, 0.0f, 0.72f), 0.34f);
ConfigureRankLayer(rankShadowText, new Color(0.0f, 0.0f, 0.0f, 0.74f), 0.2f);
ConfigureRankLayer(rankDepthText, new Color(0.0f, 0.0f, 0.0f, 0.55f), 0.13f);
ConfigureRankLayer(rankRimText, new Color(1.0f, 1.0f, 1.0f, 0.82f), 0.08f);
ConfigureRankLayer(rankMainText, new Color(0.0f, 0.16f, 0.28f, 0.7f), 0.06f);
ConfigureRankLayer(rankHighlightText, new Color(1.0f, 1.0f, 1.0f, 0.35f), 0.02f);
}
private TextMeshProUGUI MakeTmpLabel(Transform parent, string name,
Vector2 pos, Vector2 size, float fontSize, Color color, TextAlignmentOptions align)
{
GameObject go = new GameObject(name);
go.transform.SetParent(parent, false);
RectTransform rect = go.AddComponent<RectTransform>();
rect.anchorMin = new Vector2(0.5f, 0.5f);
rect.anchorMax = new Vector2(0.5f, 0.5f);
rect.anchoredPosition = pos;
rect.sizeDelta = size;
TextMeshProUGUI tmp = go.AddComponent<TextMeshProUGUI>();
tmp.fontSize = fontSize;
tmp.enableAutoSizing = true;
tmp.fontSizeMin = fontSize * 0.6f;
tmp.fontSizeMax = fontSize;
tmp.color = color;
tmp.alignment = align;
tmp.overflowMode = TextOverflowModes.Truncate;
tmp.textWrappingMode = TextWrappingModes.NoWrap;
tmp.lineSpacing = -4.0f;
tmp.raycastTarget = false;
if (scoreText != null && scoreText.font != null)
{
tmp.font = scoreText.font;
tmp.fontSharedMaterial = scoreText.fontSharedMaterial;
}
return tmp;
}
private static void ConfigureRankLayer(TextMeshProUGUI tmp, Color outlineColor, float outlineWidth)
{
if (tmp == null)
return;
tmp.fontSizeMin = tmp.fontSize * 0.75f;
tmp.outlineColor = outlineColor;
tmp.outlineWidth = outlineWidth;
tmp.characterSpacing = -1.0f;
}
private void ApplyMetalRankColors(Color mainColor, Color depthColor, Color rimColor)
{
Color darkMetal = new Color(0.02f, 0.08f, 0.14f, 0.82f);
Color steel = new Color(0.70f, 0.95f, 1.0f, 1.0f);
Color whiteHot = new Color(1.0f, 1.0f, 1.0f, 1.0f);
Color lowerMain = Color.Lerp(mainColor, depthColor, 0.55f);
SetSolidRankLayer(rankBackText, new Color(0.0f, 0.0f, 0.0f, 0.50f));
SetSolidRankLayer(rankShadowText, darkMetal);
SetRankGradient(rankDepthText,
Color.Lerp(depthColor, steel, 0.22f),
depthColor,
new Color(0.0f, 0.12f, 0.22f, 0.95f),
new Color(0.0f, 0.05f, 0.10f, 0.95f));
SetRankGradient(rankRimText,
whiteHot,
rimColor,
Color.Lerp(rimColor, mainColor, 0.35f),
Color.Lerp(mainColor, depthColor, 0.45f));
SetRankGradient(rankMainText,
whiteHot,
Color.Lerp(whiteHot, rimColor, 0.45f),
Color.Lerp(mainColor, steel, 0.18f),
lowerMain);
SetRankGradient(rankHighlightText,
new Color(1.0f, 1.0f, 1.0f, 0.42f),
new Color(0.92f, 1.0f, 1.0f, 0.28f),
new Color(1.0f, 1.0f, 1.0f, 0.08f),
new Color(1.0f, 1.0f, 1.0f, 0.02f));
}
private static void SetSolidRankLayer(TextMeshProUGUI tmp, Color color)
{
if (tmp == null)
return;
tmp.enableVertexGradient = false;
tmp.color = color;
}
private static void SetRankGradient(TextMeshProUGUI tmp,
Color topLeft, Color topRight, Color bottomLeft, Color bottomRight)
{
if (tmp == null)
return;
tmp.enableVertexGradient = true;
tmp.color = Color.white;
tmp.colorGradient = new VertexGradient(topLeft, topRight, bottomLeft, bottomRight);
}
private static string GetRankDepthColorHex(string rank)
{
switch (rank)
{
case "M": return "#7EEBFF";
case "S+": return "#116BFF";
case "S": return "#B56A16";
case "A": return "#5CAA30";
case "B": return "#B89E20";
case "C": return "#C05A10";
case "D": return "#B02040";
default: return "#606870";
}
}
private static string GetRankRimColorHex(string rank)
{
switch (rank)
{
case "M": return "#FFFFFF";
case "S+": return "#E8FFFF";
case "S": return "#FFF4B8";
case "A": return "#F1FFD8";
case "B": return "#FFF5B8";
case "C": return "#FFE0B8";
case "D": return "#FFD5D5";
default: return "#E8F0F8";
}
}
private static Color HexToColor(string hex)
{
if (ColorUtility.TryParseHtmlString(hex, out Color color))
return color;
return Color.white;
}
}
}