Files
BeatSaber/Assets/Script/SongDetailPanel.cs
whdwo798 c73ff7f412 노래 만들기 수정 — NAS 업로드 완성, Easy 제거 및 ExpertPlus 추가, 다운로드 버그 수정
- NasPublisher: DSM 7.2 multipart body 수동 구성으로 업로드 401 오류 해결
- NasPublisher: 비밀번호 StreamingAssets/nas_config.json 분리, .gitignore 등록
- NasPublisher: staticBaseUrl 포트 8180 → 80 수정
- BeatSageUploader: Easy 난이도 제거, ExpertPlus(.dat) 추가
- NoteData: DifficultyMap에서 easy 제거, expertplus 추가
- SongCreatorManager: toggleEasy → toggleExpertPlus 교체
- SongDetailPanel: btnEasy → btnExpertPlus 교체
- DownloadManager: DownloadHandlerFile 경로 정규화(Path.GetFullPath), mapFile 빈 값 방어 처리
- PersistentXRRig: FindObjectsOfType obsolete 경고 수정

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 23:39:27 +09:00

223 lines
7.5 KiB
C#

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class SongDetailPanel : MonoBehaviour
{
[Header("곡 정보")]
[SerializeField] private TMP_Text titleText;
[SerializeField] private TMP_Text artistText;
[SerializeField] private TMP_Text infoText;
[Header("난이도 버튼")]
[SerializeField] private Button btnNormal;
[SerializeField] private Button btnHard;
[SerializeField] private Button btnExpert;
[SerializeField] private Button btnExpertPlus;
[Header("액션 버튼")]
[SerializeField] private Button downloadButton;
[SerializeField] private Button deleteButton;
[SerializeField] private Button playButton;
[SerializeField] private Button closeButton;
[Header("진행률")]
[SerializeField] private GameObject progressGroup;
[SerializeField] private Slider progressSlider;
[SerializeField] private TMP_Text progressText;
[Header("씬 이름")]
[SerializeField] private string gameSceneName = "Game";
private static readonly Color SelectedColor = new Color(0.4f, 0.8f, 1f);
private static readonly Color DeselectedColor = Color.white;
private SongInfo currentSong;
private string selectedDifficulty;
private DownloadManager downloadManager;
private SongSelectManager selectManager;
private readonly (string key, System.Func<SongDetailPanel, Button> btn)[] diffSlots =
{
("normal", p => p.btnNormal),
("hard", p => p.btnHard),
("expert", p => p.btnExpert),
("expertplus", p => p.btnExpertPlus),
};
// ── Public API ───────────────────────────────────────────
public void Show(SongInfo song, DownloadManager dm, SongSelectManager sm)
{
currentSong = song;
downloadManager = dm;
selectManager = sm;
selectedDifficulty = null;
titleText.text = song.title;
artistText.text = song.artist;
infoText.text = $"BPM {song.bpm} | {FormatDuration(song.duration)}";
RefreshUI();
}
// ── UI 갱신 ──────────────────────────────────────────────
private void RefreshUI()
{
bool downloaded = SongLibrary.Instance.IsSongDownloaded(currentSong.id);
// 난이도 버튼 세팅
foreach (var (key, getBtn) in diffSlots)
{
Button btn = getBtn(this);
bool exists = currentSong.difficulties.Get(key) != null;
btn.interactable = downloaded && exists;
btn.onClick.RemoveAllListeners();
if (downloaded && exists)
{
string captured = key;
btn.onClick.AddListener(() => SelectDifficulty(captured));
}
}
UpdateDiffColors();
// 액션 버튼
downloadButton.gameObject.SetActive(!downloaded);
deleteButton.gameObject.SetActive(downloaded);
playButton.interactable = downloaded && selectedDifficulty != null;
progressGroup.SetActive(false);
downloadButton.onClick.RemoveAllListeners();
downloadButton.onClick.AddListener(OnDownloadClicked);
deleteButton.onClick.RemoveAllListeners();
deleteButton.onClick.AddListener(OnDeleteClicked);
playButton.onClick.RemoveAllListeners();
playButton.onClick.AddListener(OnPlayClicked);
closeButton?.onClick.RemoveAllListeners();
closeButton?.onClick.AddListener(() => gameObject.SetActive(false));
}
private void SelectDifficulty(string difficulty)
{
selectedDifficulty = difficulty;
playButton.interactable = true;
UpdateDiffColors();
}
private void UpdateDiffColors()
{
foreach (var (key, getBtn) in diffSlots)
{
Button btn = getBtn(this);
var colors = btn.colors;
colors.normalColor = (key == selectedDifficulty) ? SelectedColor : DeselectedColor;
btn.colors = colors;
}
}
// ── 다운로드 (곡 단위 전체) ───────────────────────────────
private void OnDownloadClicked()
{
StartCoroutine(DownloadAllCoroutine());
}
private IEnumerator DownloadAllCoroutine()
{
// 존재하는 난이도 수집
var diffs = new List<string>();
foreach (var (key, _) in diffSlots)
if (currentSong.difficulties.Get(key) != null)
diffs.Add(key);
if (diffs.Count == 0) yield break;
SetInteractable(false);
progressGroup.SetActive(true);
int totalSteps = diffs.Count;
int doneSteps = 0;
bool failed = false;
foreach (string diff in diffs)
{
bool stepDone = false;
downloadManager.DownloadSong(
currentSong, diff,
onProgress: p =>
{
float overall = (doneSteps + p) / totalSteps;
progressSlider.value = overall;
progressText.text = $"{diffs[doneSteps < diffs.Count ? doneSteps : diffs.Count - 1].ToUpper()} {(int)(overall * 100)}%";
},
onComplete: () =>
{
SongLibrary.Instance.MarkDownloaded(currentSong.id, diff);
doneSteps++;
stepDone = true;
},
onError: err =>
{
Debug.LogError($"[SongDetailPanel] {err}");
failed = true;
stepDone = true;
});
yield return new WaitUntil(() => stepDone);
if (failed) break;
}
SetInteractable(true);
selectManager.RefreshCards();
RefreshUI();
if (!failed)
Debug.Log($"[SongDetailPanel] '{currentSong.title}' 전체 다운로드 완료");
}
// ── 삭제 ─────────────────────────────────────────────────
private void OnDeleteClicked()
{
downloadManager.DeleteSong(currentSong.id);
SongLibrary.Instance.MarkSongRemoved(currentSong.id);
selectedDifficulty = null;
selectManager.RefreshCards();
RefreshUI();
}
// ── 플레이 ───────────────────────────────────────────────
private void OnPlayClicked()
{
GameSession.SelectedSong = currentSong;
GameSession.SelectedDifficulty = selectedDifficulty;
SceneManager.LoadScene(gameSceneName);
}
// ── 유틸 ─────────────────────────────────────────────────
private void SetInteractable(bool value)
{
downloadButton.interactable = value;
deleteButton.interactable = value;
playButton.interactable = value && selectedDifficulty != null;
foreach (var (_, getBtn) in diffSlots)
getBtn(this).interactable = value;
}
private static string FormatDuration(int seconds)
=> $"{seconds / 60}:{seconds % 60:D2}";
}