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>
This commit is contained in:
@@ -11,12 +11,16 @@ namespace VRBeats
|
||||
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()
|
||||
{
|
||||
@@ -24,6 +28,9 @@ namespace VRBeats
|
||||
initialValue += "0";
|
||||
|
||||
scoreManager = FindFirstObjectByType<ScoreManager>();
|
||||
if (scoreManager != null)
|
||||
scoreHudCanvasGroup = scoreManager.GetComponent<CanvasGroup>() ??
|
||||
scoreManager.gameObject.AddComponent<CanvasGroup>();
|
||||
ApplyPopupTextStyle();
|
||||
BuildResultLayout();
|
||||
}
|
||||
@@ -34,6 +41,7 @@ namespace VRBeats
|
||||
return;
|
||||
|
||||
SetTitleActive(false);
|
||||
SetScoreHudVisible(false);
|
||||
gameObject.CancelAllTweens();
|
||||
|
||||
if (resultRoot != null)
|
||||
@@ -58,6 +66,7 @@ namespace VRBeats
|
||||
scoreText.gameObject.SetActive(true);
|
||||
|
||||
SetTitleActive(true);
|
||||
SetScoreHudVisible(true);
|
||||
ApplyPopupTextStyle();
|
||||
|
||||
if (scoreText != null)
|
||||
@@ -67,9 +76,12 @@ namespace VRBeats
|
||||
private void PopulateResultLayout()
|
||||
{
|
||||
if (scoreManager == null ||
|
||||
rankBackText == null ||
|
||||
rankShadowText == null ||
|
||||
rankDepthText == null ||
|
||||
rankRimText == null ||
|
||||
rankMainText == null ||
|
||||
rankHighlightText == null ||
|
||||
resultScoreText == null ||
|
||||
resultAccuracyText == null ||
|
||||
resultComboText == null)
|
||||
@@ -78,16 +90,20 @@ namespace VRBeats
|
||||
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;
|
||||
rankMainText.color = mainColor;
|
||||
rankHighlightText.text = rank;
|
||||
ApplyMetalRankColors(mainColor, depthColor, rimColor);
|
||||
resultScoreText.text =
|
||||
$"<size=48%><color=#A0C8FF>SCORE</color></size>\n{scoreManager.CurrentScore:N0}";
|
||||
$"<size=56%><color=#A0C8FF>SCORE</color></size>\n{scoreManager.CurrentScore:N0}";
|
||||
resultAccuracyText.text =
|
||||
$"<size=70%><color=#A0C8FF>ACCURACY</color></size> {scoreManager.AccuracyPercent:0.0}%";
|
||||
$"<color=#A0C8FF>ACCURACY</color> {scoreManager.AccuracyPercent:0.0}%";
|
||||
resultComboText.text = $"MAX COMBO {scoreManager.MaxCombo}";
|
||||
}
|
||||
|
||||
@@ -123,6 +139,16 @@ namespace VRBeats
|
||||
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)
|
||||
@@ -137,32 +163,47 @@ namespace VRBeats
|
||||
RectTransform rootRect = root.AddComponent<RectTransform>();
|
||||
rootRect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
rootRect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rootRect.anchoredPosition = Vector2.zero;
|
||||
rootRect.sizeDelta = new Vector2(620.0f, 250.0f);
|
||||
rootRect.anchoredPosition = new Vector2(5.0f, 3.1f);
|
||||
rootRect.sizeDelta = new Vector2(82.0f, 34.0f);
|
||||
root.SetActive(false);
|
||||
resultRoot = root;
|
||||
|
||||
// Rank badge left side — hierarchy order = draw order (shadow first, main on top)
|
||||
// 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(-166.0f, 6.0f), new Vector2(200.0f, 200.0f), 14.0f,
|
||||
new Color(0.0f, 0.0f, 0.0f, 0.55f), TextAlignmentOptions.Midline);
|
||||
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(-168.0f, 8.0f), new Vector2(200.0f, 200.0f), 14.0f,
|
||||
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(-170.0f, 10.0f), new Vector2(200.0f, 200.0f), 14.0f,
|
||||
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);
|
||||
|
||||
// Score, accuracy, combo right side
|
||||
resultScoreText = MakeTmpLabel(root.transform, "ResultScoreText",
|
||||
new Vector2(70.0f, 58.0f), new Vector2(260.0f, 90.0f), 5.5f,
|
||||
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(70.0f, 0.0f), new Vector2(260.0f, 46.0f), 3.4f,
|
||||
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(70.0f, -52.0f), new Vector2(260.0f, 44.0f), 3.0f,
|
||||
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,
|
||||
@@ -178,11 +219,14 @@ namespace VRBeats
|
||||
|
||||
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.Overflow;
|
||||
tmp.overflowMode = TextOverflowModes.Truncate;
|
||||
tmp.textWrappingMode = TextWrappingModes.NoWrap;
|
||||
tmp.lineSpacing = -8.0f;
|
||||
tmp.lineSpacing = -4.0f;
|
||||
tmp.raycastTarget = false;
|
||||
|
||||
if (scoreText != null && scoreText.font != null)
|
||||
@@ -194,6 +238,68 @@ namespace VRBeats
|
||||
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)
|
||||
@@ -209,6 +315,21 @@ namespace VRBeats
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
@@ -142,6 +142,25 @@ namespace VRBeats
|
||||
UpdateScoreTween();
|
||||
}
|
||||
|
||||
public void ApplyForcedResult(int noteCount, int perfect, int great, int good, int miss, int forcedMaxCombo)
|
||||
{
|
||||
totalNoteCount = Mathf.Max(0, noteCount);
|
||||
perfectCount = Mathf.Max(0, perfect);
|
||||
greatCount = Mathf.Max(0, great);
|
||||
goodCount = Mathf.Max(0, good);
|
||||
missCount = Mathf.Max(0, miss);
|
||||
judgedNoteCount = perfectCount + greatCount + goodCount + missCount;
|
||||
earnedAccuracyPoints = perfectCount * 1000 + greatCount * 900 + goodCount * 700;
|
||||
maxCombo = Mathf.Clamp(forcedMaxCombo, 0, Mathf.Max(totalNoteCount, judgedNoteCount));
|
||||
currentCombo = maxCombo;
|
||||
currentMultiplier = missCount > 0 ? 1.0f : GetComboMultiplier(currentCombo);
|
||||
lastJudgement = missCount > 0 ? BeatJudgement.Miss : BeatJudgement.Perfect;
|
||||
judgementTimer = 0.45f;
|
||||
resultFinalized = false;
|
||||
UpdateScoreTween();
|
||||
UpdateMultiplierLoaderValue();
|
||||
}
|
||||
|
||||
public void CompleteSong()
|
||||
{
|
||||
if (resultFinalized)
|
||||
|
||||
Reference in New Issue
Block a user