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(); if (scoreManager != null) scoreHudCanvasGroup = scoreManager.GetComponent() ?? scoreManager.gameObject.AddComponent(); 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 = $"SCORE\n{scoreManager.CurrentScore:N0}"; resultAccuracyText.text = $"ACCURACY {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(); 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(); 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(); 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; } } }