2026-06-13 리듬게임 프로토타입
This commit is contained in:
Binary file not shown.
@@ -17,6 +17,8 @@ public class RhythmManager : MonoBehaviour
|
||||
[SerializeField] private float _goodWindow = 0.10f;
|
||||
[SerializeField] private float _badWindow = 0.15f; // 이 밖이면 입력 무시(헛침)
|
||||
|
||||
[SerializeField] private GameObject StartButtonObj;
|
||||
|
||||
public float SongTime => _audioSource.time; // 모든 타이밍의 기준
|
||||
|
||||
private List<Note> SongNoteList;
|
||||
@@ -25,6 +27,14 @@ public class RhythmManager : MonoBehaviour
|
||||
private Func<float> _songTimeProvider; // 노트에 넘길 시간 제공자 (매 스폰마다 람다 재생성 방지용 캐시)
|
||||
private readonly List<RhythmNoteInstance> _activeNotes = new(); // 화면에 떠 있는(아직 처리 안 된) 노트들
|
||||
|
||||
// 점수 상태 (HUD가 읽음). 누적 로직은 RhythmScore가 전담
|
||||
public RhythmScore Score { get; } = new();
|
||||
|
||||
public event Action OnSongStarted; // 곡 시작 (ScoreHud 표시 / ResultHud 숨김)
|
||||
public event Action<Result> OnJudged; // 노트 하나가 판정될 때마다 (HUD 연출용)
|
||||
public event Action<RhythmScore> OnScoreChanged; // 점수/콤보가 바뀔 때 (실시간 HUD)
|
||||
public event Action<RhythmScore> OnSongFinished; // 곡이 끝났을 때 (결과창)
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_songTimeProvider = () => SongTime; // 한 번만 만들어 모든 노트가 공유
|
||||
@@ -80,6 +90,11 @@ public void StartSong()
|
||||
_nextNoteIndex = 0;
|
||||
_activeNotes.Clear();
|
||||
|
||||
Score.Reset();
|
||||
OnSongStarted?.Invoke(); // ScoreHud 표시 / ResultHud 숨김
|
||||
OnScoreChanged?.Invoke(Score); // HUD 초기화(0점)
|
||||
StartButtonObj.SetActive(false);
|
||||
|
||||
_audioSource.Play();
|
||||
_isPlaying = true;
|
||||
|
||||
@@ -90,17 +105,21 @@ public void StartSong()
|
||||
// 곡 정지 + 주변음 복구
|
||||
public void StopSong()
|
||||
{
|
||||
if (!_isPlaying) return; // 중복 호출 방지(결과창 두 번 뜨는 것 차단)
|
||||
|
||||
_audioSource.Stop();
|
||||
_isPlaying = false;
|
||||
|
||||
if (SoundManager.Instance != null) SoundManager.Instance.ExitMinigameMode();
|
||||
|
||||
OnSongFinished?.Invoke(Score); // 결과창 표시
|
||||
}
|
||||
|
||||
// 노트가 판정선을 지나쳐 스스로 Miss 처리될 때 호출 (노트는 이미 자기 파괴됨)
|
||||
private void OnNoteMissed(RhythmNoteInstance note)
|
||||
{
|
||||
_activeNotes.Remove(note);
|
||||
// TODO: 점수/콤보 시스템 연결 시 Miss 반영
|
||||
ApplyJudge(Result.Miss);
|
||||
Debug.Log("[Rhythm] Miss (지나침)");
|
||||
}
|
||||
|
||||
@@ -134,10 +153,18 @@ public void OnPlayerInput()
|
||||
_activeNotes.Remove(target);
|
||||
Destroy(target.gameObject);
|
||||
|
||||
// TODO: 점수/콤보 시스템 연결 시 result 반영
|
||||
ApplyJudge(result);
|
||||
Debug.Log($"[Rhythm] {result} (diff {bestDiff:F3}s)");
|
||||
}
|
||||
|
||||
// 판정 결과를 점수에 반영하고 이벤트로 알림
|
||||
private void ApplyJudge(Result result)
|
||||
{
|
||||
Score.Apply(result);
|
||||
OnJudged?.Invoke(result);
|
||||
OnScoreChanged?.Invoke(Score);
|
||||
}
|
||||
|
||||
// diff = 절대 시간차(초). 윈도우 안이면 Perfect/Good/Bad, 밖이면 Miss(입력 무시)
|
||||
public Result Judge(float diff)
|
||||
{
|
||||
|
||||
59
Assets/02_Scripts/Rhythm/RhythmScore.cs
Normal file
59
Assets/02_Scripts/Rhythm/RhythmScore.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
// 리듬게임 점수/콤보/판정수 누적 (Unity 비의존 순수 로직 - 테스트하기 쉬움)
|
||||
public class RhythmScore
|
||||
{
|
||||
// 판정별 기본 점수
|
||||
private const int PerfectPoint = 100;
|
||||
private const int GoodPoint = 50;
|
||||
private const int BadPoint = 10;
|
||||
|
||||
public int Score { get; private set; }
|
||||
public int Combo { get; private set; }
|
||||
public int MaxCombo { get; private set; }
|
||||
|
||||
public int PerfectCount { get; private set; }
|
||||
public int GoodCount { get; private set; }
|
||||
public int BadCount { get; private set; }
|
||||
public int MissCount { get; private set; }
|
||||
|
||||
public int TotalJudged => PerfectCount + GoodCount + BadCount + MissCount;
|
||||
|
||||
// 판정 하나를 점수에 반영
|
||||
public void Apply(Result result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case Result.Perfect:
|
||||
PerfectCount++;
|
||||
Combo++;
|
||||
Score += PerfectPoint + Combo; // 콤보가 쌓일수록 보너스 (연속 보상)
|
||||
break;
|
||||
case Result.Good:
|
||||
GoodCount++;
|
||||
Combo++;
|
||||
Score += GoodPoint + Combo;
|
||||
break;
|
||||
case Result.Bad:
|
||||
BadCount++;
|
||||
Combo = 0; // 콤보 끊김
|
||||
Score += BadPoint;
|
||||
break;
|
||||
case Result.Miss:
|
||||
MissCount++;
|
||||
Combo = 0; // 콤보 끊김
|
||||
break;
|
||||
}
|
||||
|
||||
if (Combo > MaxCombo) MaxCombo = Combo;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Score = 0;
|
||||
Combo = 0;
|
||||
MaxCombo = 0;
|
||||
PerfectCount = 0;
|
||||
GoodCount = 0;
|
||||
BadCount = 0;
|
||||
MissCount = 0;
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/Rhythm/RhythmScore.cs.meta
Normal file
2
Assets/02_Scripts/Rhythm/RhythmScore.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adfd55d5980f58940ad4cb735db42f89
|
||||
50
Assets/02_Scripts/UI/RhythmResultHud.cs
Normal file
50
Assets/02_Scripts/UI/RhythmResultHud.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
// 곡 종료 후 결과창. 총점/판정수/정확도/등급/최대콤보를 표시한다.
|
||||
public class RhythmResultHud : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private RhythmManager _manager;
|
||||
[SerializeField] private GameObject _root; // 결과창 루트
|
||||
[SerializeField] private TMP_Text _scoreText;
|
||||
[SerializeField] private TMP_Text _maxComboText;
|
||||
[SerializeField] private TMP_Text _countText; // Perfect/Good/Bad/Miss 모아서 표시
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_root != null) _root.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_manager == null) return;
|
||||
_manager.OnSongStarted += Hide;
|
||||
_manager.OnSongFinished += Show;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_manager == null) return;
|
||||
_manager.OnSongStarted -= Hide;
|
||||
_manager.OnSongFinished -= Show;
|
||||
}
|
||||
|
||||
private void Hide()
|
||||
{
|
||||
if (_root != null) _root.SetActive(false); // 곡 시작/재시작 시 이전 결과창 숨김
|
||||
}
|
||||
|
||||
private void Show(RhythmScore score)
|
||||
{
|
||||
if (_scoreText != null) _scoreText.text = $"{score.Score:N0}";
|
||||
if (_maxComboText != null) _maxComboText.text = $"{score.MaxCombo}";
|
||||
if (_countText != null)
|
||||
_countText.text =
|
||||
$"Perfect {score.PerfectCount}\n" +
|
||||
$"Good {score.GoodCount}\n" +
|
||||
$"Bad {score.BadCount}\n" +
|
||||
$"Miss {score.MissCount}";
|
||||
|
||||
if (_root != null) _root.SetActive(true);
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/UI/RhythmResultHud.cs.meta
Normal file
2
Assets/02_Scripts/UI/RhythmResultHud.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b187e420986956a4ba9407759ed4050f
|
||||
56
Assets/02_Scripts/UI/RhythmScoreHud.cs
Normal file
56
Assets/02_Scripts/UI/RhythmScoreHud.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
// 플레이 중 실시간 점수/콤보 HUD. RhythmManager 이벤트만 구독하고 표시만 한다.
|
||||
public class RhythmScoreHud : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private RhythmManager _manager;
|
||||
[SerializeField] private GameObject _root; // HUD 루트 (곡 시작/종료에 켜고 끔)
|
||||
[SerializeField] private TMP_Text _scoreText;
|
||||
[SerializeField] private TMP_Text _comboText;
|
||||
[SerializeField] private TMP_Text _judgeText; // Perfect/Good 등 최근 판정 표시(선택)
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_root != null) _root.SetActive(false); // 곡 시작 전엔 숨김
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_manager == null) return;
|
||||
_manager.OnSongStarted += HandleSongStarted;
|
||||
_manager.OnScoreChanged += HandleScoreChanged;
|
||||
_manager.OnJudged += HandleJudged;
|
||||
_manager.OnSongFinished += HandleSongFinished;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_manager == null) return;
|
||||
_manager.OnSongStarted -= HandleSongStarted;
|
||||
_manager.OnScoreChanged -= HandleScoreChanged;
|
||||
_manager.OnJudged -= HandleJudged;
|
||||
_manager.OnSongFinished -= HandleSongFinished;
|
||||
}
|
||||
|
||||
private void HandleSongStarted()
|
||||
{
|
||||
if (_root != null) _root.SetActive(true); // 곡 시작 시 실시간 HUD 표시
|
||||
}
|
||||
|
||||
private void HandleScoreChanged(RhythmScore score)
|
||||
{
|
||||
if (_scoreText != null) _scoreText.text = $"SCORE {score.Score:N0}";
|
||||
if (_comboText != null) _comboText.text = score.Combo > 0 ? $"{score.Combo}" : "";
|
||||
}
|
||||
|
||||
private void HandleJudged(Result result)
|
||||
{
|
||||
if (_judgeText != null) _judgeText.text = result.ToString();
|
||||
}
|
||||
|
||||
private void HandleSongFinished(RhythmScore score)
|
||||
{
|
||||
if (_root != null) _root.SetActive(false); // 곡 끝나면 실시간 HUD는 숨김(결과창이 대신 뜸)
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/UI/RhythmScoreHud.cs.meta
Normal file
2
Assets/02_Scripts/UI/RhythmScoreHud.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d2c41822c633294a986c6803818303d
|
||||
Reference in New Issue
Block a user