using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Networking; using UnityEngine.SceneManagement; public class Spawner : MonoBehaviour { [Header("오디오 및 파일 설정")] public AudioSource audioSource; [Header("노트 프리팹 & 위치")] public GameObject[] cubePrefabs; public Transform[] spawnPoints; [Header("타이밍 설정")] public float noteSpeed = 2.0f; public float distanceToHit = 10.0f; [Header("씬 설정")] public string songSelectSceneName = "SongSelect"; private List mapNotes = new List(); private int nextNoteIndex = 0; private float travelTime; private bool isReady = false; // ── Unity ──────────────────────────────────────────────── void Start() { travelTime = distanceToHit / noteSpeed; if (GameSession.SelectedSong == null) { Debug.LogWarning("[Spawner] 선택된 곡 없음 → 곡 선택 화면으로 이동"); SceneManager.LoadScene(songSelectSceneName); return; } StartCoroutine(InitGame()); } void Update() { if (!isReady || audioSource == null || !audioSource.isPlaying) return; float currentTime = audioSource.time; while (nextNoteIndex < mapNotes.Count && currentTime + travelTime >= mapNotes[nextNoteIndex].time) { SpawnNote(mapNotes[nextNoteIndex]); nextNoteIndex++; } } // ── 초기화 ─────────────────────────────────────────────── private IEnumerator InitGame() { SongInfo song = GameSession.SelectedSong; string difficulty = GameSession.SelectedDifficulty; // 맵 JSON 로드 string mapPath = GetMapPath(song, difficulty); if (!File.Exists(mapPath)) { Debug.LogError($"[Spawner] 맵 파일 없음: {mapPath}"); yield break; } LoadMapJson(mapPath); // 오디오 로드 (로컬 파일 → AudioClip) string audioPath = GetAudioPath(song.id); yield return LoadAudioClip(audioPath); // LRU 갱신 SongLibrary.Instance?.TouchSong(song.id); isReady = true; audioSource.Play(); Debug.Log($"[Spawner] 시작: {song.title} ({difficulty})"); } private void LoadMapJson(string path) { string json = File.ReadAllText(path); MapData data = JsonUtility.FromJson(json); mapNotes = data?.target ?? new List(); mapNotes.Sort((a, b) => a.time.CompareTo(b.time)); Debug.Log($"[Spawner] 노트 로드: {mapNotes.Count}개"); } private IEnumerator LoadAudioClip(string path) { string uri = "file://" + path; using var req = UnityWebRequestMultimedia.GetAudioClip(uri, AudioType.MPEG); yield return req.SendWebRequest(); if (req.result != UnityWebRequest.Result.Success) { Debug.LogError($"[Spawner] 오디오 로드 실패: {req.error}"); yield break; } audioSource.clip = DownloadHandlerAudioClip.GetContent(req); } // ── 노트 스폰 ──────────────────────────────────────────── private void SpawnNote(NoteData data) { if (data.colorType >= cubePrefabs.Length || data.position >= spawnPoints.Length) return; GameObject obj = Instantiate( cubePrefabs[data.colorType], spawnPoints[data.position].position, spawnPoints[data.position].rotation); obj.transform.Rotate(transform.forward, 90 * Random.Range(0, 4)); } // ── 경로 헬퍼 ──────────────────────────────────────────── private static string CacheRoot => Path.Combine(Application.temporaryCachePath, "beatsaber"); private static string GetAudioPath(string songId) => Path.Combine(CacheRoot, songId, $"{songId}.mp3"); private static string GetMapPath(SongInfo song, string difficulty) { DifficultyInfo info = song.difficulties.Get(difficulty); if (info == null) return string.Empty; return Path.Combine(CacheRoot, song.id, Path.GetFileName(info.mapFile)); } }