using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; using System.IO; using TMPro; using UnityEngine.InputSystem; public class MapEditor : MonoBehaviour { [Header("Components")] public AudioSource audioSource; public Slider timelineSlider; public TMP_Text timeText; [Header("Song Settings")] [Tooltip("확장자와 Map_을 제외한 저장할 노래 제목을 적으세요 (예: Life, Virus)")] public string songName = "Life"; [Header("Note Guide (Large Background Numbers)")] public TMP_Text[] guideTexts; // 4, 5, 1, 2 순서로 배치된 큰 텍스트들 [Header("Timeline (On Slider)")] public RectTransform[] timelineRows; // Slider 내부 NoteContainer 안의 4개 줄 [Header("Prefabs")] public GameObject noteUIPrefab; // 텍스트가 없는 10x10 크기의 작은 사각형 프리팹 private List recordedNotes = new List(); private Dictionary visualNoteMap = new Dictionary(); void Start() { // 슬라이더 클릭 시 해당 시간대로 이동하는 리스너 timelineSlider.onValueChanged.AddListener(OnSliderValueChanged); } void Update() { var kb = Keyboard.current; if (kb == null) return; // 1. 재생/정지 제어 (Space) if (kb.spaceKey.wasPressedThisFrame) { if (audioSource.isPlaying) audioSource.Pause(); else audioSource.Play(); } // 2. 10초 되감기 (Left Arrow) if (kb.leftArrowKey.wasPressedThisFrame) { audioSource.time = Mathf.Max(0, audioSource.time - 10f); } // 3. 시간 및 UI 업데이트 if (audioSource.isPlaying) { UpdateTimelineUI(); } // 4. 입력 처리 (가이드 색상 변경 + 노트 기록) HandleEditorInput(kb); } void UpdateTimelineUI() { float currentTime = audioSource.time; float totalTime = audioSource.clip.length; timelineSlider.value = currentTime / totalTime; timeText.text = string.Format("{0:00}:{1:00} / {2:00}:{3:00}", (int)currentTime / 60, (int)currentTime % 60, (int)totalTime / 60, (int)totalTime % 60); } void HandleEditorInput(Keyboard kb) { // 1. 단일 노트 컬러 선택 (Q: 빨강, W: 파랑) int soloColor = kb.qKey.isPressed ? 0 : (kb.wKey.isPressed ? 1 : -1); // 가이드 비주얼 업데이트 UpdateGuideVisuals(kb, soloColor); if (!audioSource.isPlaying) return; // 2. E 키를 누르고 있을 때 (더블 노트 모드: 1,4 빨강 / 2,5 파랑) if (kb.eKey.isPressed) { // E를 누른 상태에서 숫자패드를 누르는 순간 기록 if (kb.numpad4Key.wasPressedThisFrame) ProcessNote(0, 0); // 4번 위치 빨강 if (kb.numpad5Key.wasPressedThisFrame) ProcessNote(1, 1); // 5번 위치 파랑 if (kb.numpad1Key.wasPressedThisFrame) ProcessNote(2, 0); // 1번 위치 빨강 if (kb.numpad2Key.wasPressedThisFrame) ProcessNote(3, 1); // 2번 위치 파랑 } // 3. E 키를 누르하지 않았을 때 (기존 Q/W 단일 노트 모드) else if (soloColor != -1) { if (kb.numpad4Key.wasPressedThisFrame) ProcessNote(0, soloColor); if (kb.numpad5Key.wasPressedThisFrame) ProcessNote(1, soloColor); if (kb.numpad1Key.wasPressedThisFrame) ProcessNote(2, soloColor); if (kb.numpad2Key.wasPressedThisFrame) ProcessNote(3, soloColor); } } void UpdateGuideVisuals(Keyboard kb, int color) { // 기본 흰색으로 초기화 foreach (var txt in guideTexts) txt.color = Color.white; if (color == -1) return; Color activeColor = (color == 0) ? Color.red : Color.blue; // 키패드 누르고 있을 때 가이드 텍스트 색 변경 if (kb.numpad4Key.isPressed) guideTexts[0].color = activeColor; if (kb.numpad5Key.isPressed) guideTexts[1].color = activeColor; if (kb.numpad1Key.isPressed) guideTexts[2].color = activeColor; if (kb.numpad2Key.isPressed) guideTexts[3].color = activeColor; } void ProcessNote(int pos, int color) { float currentTime = audioSource.time; // 수정 로직: 현재 시간 0.1초 내에 같은 줄에 이미 노트가 있다면 제거 (덮어쓰기 준비) NoteData existingNote = recordedNotes.Find(n => n.position == pos && Mathf.Abs(n.time - currentTime) < 0.1f); if (existingNote != null) RemoveNote(existingNote); RecordNote(pos, color); } void RecordNote(int pos, int color) { NoteData newNote = new NoteData { time = audioSource.time, position = pos, colorType = color }; recordedNotes.Add(newNote); // 1. 생성 및 부모 설정 GameObject obj = Instantiate(noteUIPrefab, timelineRows[pos]); RectTransform rt = obj.GetComponent(); // 2. 크기 및 앵커 강제 고정 (길어짐 방지) rt.anchorMin = new Vector2(0, 0.5f); rt.anchorMax = new Vector2(0, 0.5f); rt.pivot = new Vector2(0, 0.5f); rt.sizeDelta = new Vector2(1, 15); rt.localScale = Vector3.one; // 스케일 1로 초기화 // 3. 색상 강제 적용 (색상 사라짐 방지) Image noteImage = obj.GetComponent(); if (noteImage != null) { // 0이면 빨강, 1이면 파랑 적용 noteImage.color = (color == 0) ? Color.red : Color.blue; } // 4. 위치 계산 float rowWidth = timelineRows[pos].rect.width; float xPos = (newNote.time / audioSource.clip.length) * rowWidth; rt.anchoredPosition = new Vector2(xPos, 0); // 5. 관리 리스트에 추가 visualNoteMap.Add(newNote, obj); obj.GetComponent