Revert "Merge pull request '리듬게임' (#5) from CatsRhythmGame into main"
This reverts commit58385bf819, reversing changes made to8369e2ab51.
This commit is contained in:
@@ -3,8 +3,7 @@
|
||||
|
||||
public class RhythmNoteInstance : MonoBehaviour
|
||||
{
|
||||
[HideInInspector] public float HitTime; // 이 노트의 도달 시각
|
||||
[HideInInspector] public int Lane; // 이 노트의 레인 (오토플레이 스틱 매칭용)
|
||||
public float HitTime; // 이 노트의 도달 시각
|
||||
|
||||
[SerializeField] private float _missWindow = 0.15f; // 판정선을 이만큼 지나면 자동 Miss
|
||||
|
||||
@@ -15,14 +14,13 @@ public class RhythmNoteInstance : MonoBehaviour
|
||||
private Action<RhythmNoteInstance> _onMiss; // 지나쳐서 Miss 났을 때 통지
|
||||
|
||||
// 스폰 시 목적지·타이밍 주입 (B 방식)
|
||||
public void Setup(Vector3 start, Vector3 target, float spawnTime, float hitTime, int lane,
|
||||
public void Setup(Vector3 start, Vector3 target, float spawnTime, float hitTime,
|
||||
Func<float> songTime, Action<RhythmNoteInstance> onMiss = null)
|
||||
{
|
||||
_start = start;
|
||||
_target = target;
|
||||
_spawnTime = spawnTime;
|
||||
HitTime = hitTime;
|
||||
Lane = lane;
|
||||
_songTime = songTime;
|
||||
_onMiss = onMiss;
|
||||
|
||||
|
||||
@@ -3,38 +3,17 @@
|
||||
|
||||
public class RhythmNoteSpawner : MonoBehaviour
|
||||
{
|
||||
// 레인(손)별 설정 - 전용 프리팹 + 가로 위치 오프셋
|
||||
[Serializable]
|
||||
public class LaneVisual
|
||||
{
|
||||
public RhythmNoteInstance Prefab; // 이 레인 전용 노트 프리팹 (왼손/오른손 다른 모양)
|
||||
public Vector3 Offset; // 스폰/판정 위치 가로 오프셋 (왼손 -x, 오른손 +x 등)
|
||||
}
|
||||
|
||||
[SerializeField] private RhythmNoteInstance _notePrefab; // 기본 프리팹 (레인 전용 미지정 시 사용)
|
||||
[SerializeField] private RhythmNoteInstance _notePrefab; // 생성할 노트 프리팹
|
||||
[SerializeField] private Transform _spawnPoint; // 노트가 생겨나는 위치(미지정 시 자기 위치)
|
||||
[SerializeField] private Transform _judgmentLine; // 목적지(판정선)
|
||||
[SerializeField] private LaneVisual[] _lanes; // 인덱스 = Note.Lane (RhythmChart.LanePitches 순서와 일치)
|
||||
|
||||
// 노트 생성, 목적지·타이밍 주입. 레인에 따라 프리팹/위치만 다르게(판정은 동일)
|
||||
// 노트 프리팹 생성, 목적지·타이밍 주입
|
||||
public RhythmNoteInstance SpawnNote(Note note, float spawnTime,
|
||||
Func<float> songTime, Action<RhythmNoteInstance> onMiss = null)
|
||||
{
|
||||
Transform origin = _spawnPoint != null ? _spawnPoint : transform;
|
||||
|
||||
// 레인 범위 밖이면 기본 프리팹/오프셋 0
|
||||
LaneVisual lane = (_lanes != null && note.Lane >= 0 && note.Lane < _lanes.Length)
|
||||
? _lanes[note.Lane] : null;
|
||||
|
||||
RhythmNoteInstance prefab = (lane != null && lane.Prefab != null) ? lane.Prefab : _notePrefab;
|
||||
|
||||
// 오프셋을 origin 방향 기준으로 적용해 레인을 평행 이동(start·target 동일 오프셋이라 경로가 곧음)
|
||||
Vector3 worldOffset = lane != null ? origin.rotation * lane.Offset : Vector3.zero;
|
||||
Vector3 start = origin.position + worldOffset;
|
||||
Vector3 target = _judgmentLine.position + worldOffset;
|
||||
|
||||
RhythmNoteInstance instance = Instantiate(prefab, start, origin.rotation, transform);
|
||||
instance.Setup(start, target, spawnTime, note.Time, note.Lane, songTime, onMiss);
|
||||
RhythmNoteInstance instance = Instantiate(_notePrefab, origin.position, origin.rotation, transform);
|
||||
instance.Setup(origin.position, _judgmentLine.position, spawnTime, note.Time, songTime, onMiss);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
using Hovl;
|
||||
using UnityEngine;
|
||||
|
||||
// Hovl의 HS_ProjectileMover(에셋)를 "수정하지 않고" 히트 이펙트를 코드로 터뜨리기 위한 서브클래스.
|
||||
// 핵심: protected 멤버(hit/hitPS/projectilePS/collided)는 '파생 클래스'에서는 접근 가능하다.
|
||||
// 그래서 에셋 원본은 그대로 두고, 충돌(Collision) 없이 판정 성공 시 Detonate()로 이펙트만 재생한다.
|
||||
public class RhythmProjectile : HS_ProjectileMover
|
||||
{
|
||||
// 판정 성공 시 호출. 노트가 곧 Destroy되어도 이펙트가 보이도록 노트에서 분리 후 재생.
|
||||
public void Detonate()
|
||||
{
|
||||
if (collided) return;
|
||||
collided = true;
|
||||
|
||||
// 날아가는 동안의 트레일 파티클은 정지
|
||||
if (projectilePS != null)
|
||||
projectilePS.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
|
||||
|
||||
// 히트 이펙트 컨테이너(없으면 파티클 본체)를 노트에서 떼어내 현재 위치에서 재생
|
||||
GameObject fx = hit != null ? hit : (hitPS != null ? hitPS.gameObject : null);
|
||||
if (fx == null) return;
|
||||
|
||||
fx.transform.SetParent(null, true);
|
||||
fx.transform.position = transform.position;
|
||||
fx.SetActive(true);
|
||||
|
||||
if (hitPS != null)
|
||||
{
|
||||
hitPS.Clear(true);
|
||||
hitPS.Play(true);
|
||||
}
|
||||
|
||||
// 재생이 끝나면 분리한 이펙트도 정리
|
||||
float life = hitPS != null ? hitPS.main.duration + hitPS.main.startLifetime.constantMax : 2f;
|
||||
Destroy(fx, life);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9840334459414e240b44e1b0cfe4ebb2
|
||||
@@ -1,59 +0,0 @@
|
||||
// 리듬게임 점수/콤보/판정수 누적 (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;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adfd55d5980f58940ad4cb735db42f89
|
||||
@@ -1,99 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// 오토플레이용 스틱 포즈 드라이버.
|
||||
// 매니저가 매 프레임 Drive(dt)를 호출하면 dt(타격까지 남은 시간)에 따라 회전을 세팅한다.
|
||||
// 모션: 대기 → (윈드업) 위로 들기 → (스트라이크) 내려치며 대기 자세보다 _strikeOvershoot 만큼 더 깊이 →
|
||||
// (팔로스루) 대기 자세로 부드럽게 복귀.
|
||||
// 노트는 타격 순간 파괴돼 그 뒤 dt가 끊기므로, 팔로스루만 Time.deltaTime으로 스틱이 자체 처리한다.
|
||||
public class RhythmStick : MonoBehaviour
|
||||
{
|
||||
[Tooltip("들어올릴 때 회전할 로컬 축 (예: 손목 꺾이는 축)")]
|
||||
[SerializeField] private Vector3 _raiseAxis = Vector3.right;
|
||||
|
||||
[Tooltip("대기 자세에서 위로 들어올리는 각도(도)")]
|
||||
[SerializeField] private float _raiseAngle = 50f;
|
||||
|
||||
[Tooltip("타격 시 대기 자세보다 더 깊이 내려가는 각도(도)")]
|
||||
[SerializeField] private float _strikeOvershoot = 10f;
|
||||
|
||||
[Tooltip("타격 몇 초 전부터 들어올리기 시작하는지")]
|
||||
[SerializeField] private float _windupTime = 0.1f;
|
||||
|
||||
[Tooltip("내려치는 데 걸리는 시간(초). _windupTime 보다 작아야 한다")]
|
||||
[SerializeField] private float _strikeTime = 0.04f;
|
||||
|
||||
[Tooltip("타격 후 대기 자세로 되돌아오는 시간(초)")]
|
||||
[SerializeField] private float _recoverTime = 0.08f;
|
||||
|
||||
private Quaternion _restRot; // 대기 자세
|
||||
private Quaternion _raisedRot; // 들어올린 자세
|
||||
private Quaternion _overshootRot; // 타격 시 더 깊이 내려간 자세
|
||||
|
||||
private bool _armed; // 내려치는 중 → 다음 프레임 타격 예정
|
||||
private bool _recovering; // 타격 후 복귀 중
|
||||
private float _recoverElapsed;
|
||||
private Quaternion _recoverFrom; // 복귀 시작 회전(스냅 방지)
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_restRot = transform.localRotation;
|
||||
Vector3 axis = _raiseAxis.normalized;
|
||||
_raisedRot = _restRot * Quaternion.AngleAxis(_raiseAngle, axis);
|
||||
_overshootRot = _restRot * Quaternion.AngleAxis(-_strikeOvershoot, axis); // 반대 방향 = 더 내려감
|
||||
}
|
||||
|
||||
// dt = 다음 타격까지 남은 시간(초). 다가오는 노트가 없으면 +∞.
|
||||
public void Drive(float dt)
|
||||
{
|
||||
// 1) 임박한 스윙(윈드업~타격)이 최우선
|
||||
if (dt > 0f && dt <= _windupTime)
|
||||
{
|
||||
_recovering = false;
|
||||
_armed = true; // 곧 타격함
|
||||
|
||||
if (dt > _strikeTime)
|
||||
{
|
||||
// 들어올리는 구간: dt가 _windupTime→_strikeTime 으로 줄며 rest→raised
|
||||
float u = Mathf.InverseLerp(_windupTime, _strikeTime, dt);
|
||||
transform.localRotation = Quaternion.Slerp(_restRot, _raisedRot, Mathf.SmoothStep(0f, 1f, u));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 내려치는 구간: dt가 _strikeTime→0 으로 줄며 raised→overshoot (dt=0에 가장 깊이)
|
||||
float s = Mathf.InverseLerp(_strikeTime, 0f, dt);
|
||||
transform.localRotation = Quaternion.Slerp(_raisedRot, _overshootRot, Mathf.SmoothStep(0f, 1f, s));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) 방금 타격이 끝났다면(노트 파괴로 dt가 끊김) 팔로스루 시작
|
||||
if (_armed)
|
||||
{
|
||||
_armed = false;
|
||||
_recovering = true;
|
||||
_recoverElapsed = 0f;
|
||||
_recoverFrom = transform.localRotation; // 현재 위치에서 복귀(스냅 방지)
|
||||
}
|
||||
|
||||
// 3) 팔로스루: 현재 → 대기 자세로 부드럽게
|
||||
if (_recovering)
|
||||
{
|
||||
_recoverElapsed += Time.deltaTime;
|
||||
float r = _recoverTime > 0f ? Mathf.Clamp01(_recoverElapsed / _recoverTime) : 1f;
|
||||
transform.localRotation = Quaternion.Slerp(_recoverFrom, _restRot, Mathf.SmoothStep(0f, 1f, r));
|
||||
if (r >= 1f) _recovering = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 4) 평상시 대기 자세
|
||||
transform.localRotation = _restRot;
|
||||
}
|
||||
|
||||
// 즉시 대기 자세로 되돌림 (오토플레이 종료 시 호출)
|
||||
public void ResetPose()
|
||||
{
|
||||
_armed = false;
|
||||
_recovering = false;
|
||||
transform.localRotation = _restRot;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ae74de6faba37f4ca52b1c6177a98bf
|
||||
Reference in New Issue
Block a user