2026-06-15 리듬게임 자동기능

This commit is contained in:
2026-06-15 17:37:32 +09:00
parent 6fe34d8eec
commit 38936c211a
28 changed files with 502 additions and 18 deletions

View File

@@ -4,6 +4,7 @@
public class RhythmNoteInstance : MonoBehaviour
{
[HideInInspector] public float HitTime; // 이 노트의 도달 시각
[HideInInspector] public int Lane; // 이 노트의 레인 (오토플레이 스틱 매칭용)
[SerializeField] private float _missWindow = 0.15f; // 판정선을 이만큼 지나면 자동 Miss
@@ -14,13 +15,14 @@ public class RhythmNoteInstance : MonoBehaviour
private Action<RhythmNoteInstance> _onMiss; // 지나쳐서 Miss 났을 때 통지
// 스폰 시 목적지·타이밍 주입 (B 방식)
public void Setup(Vector3 start, Vector3 target, float spawnTime, float hitTime,
public void Setup(Vector3 start, Vector3 target, float spawnTime, float hitTime, int lane,
Func<float> songTime, Action<RhythmNoteInstance> onMiss = null)
{
_start = start;
_target = target;
_spawnTime = spawnTime;
HitTime = hitTime;
Lane = lane;
_songTime = songTime;
_onMiss = onMiss;

View File

@@ -34,7 +34,7 @@ public class LaneVisual
Vector3 target = _judgmentLine.position + worldOffset;
RhythmNoteInstance instance = Instantiate(prefab, start, origin.rotation, transform);
instance.Setup(start, target, spawnTime, note.Time, songTime, onMiss);
instance.Setup(start, target, spawnTime, note.Time, note.Lane, songTime, onMiss);
return instance;
}
}

View File

@@ -0,0 +1,99 @@
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;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6ae74de6faba37f4ca52b1c6177a98bf