using UnityEngine; using System; public class HoldNoteController : MonoBehaviour { private bool isInsideJudgeZone = false; public event Action OnJudgeZoneChanged; private bool isMoving = false; private float speed = 0f; // 运动速度 private float activationTime; // 何时开始移动(Time.time 基准) private void Update() { // 到达激活时间后开始移动 if (!isMoving && Time.time >= activationTime) { isMoving = true; } if (isMoving) { transform.Translate(Vector3.down * speed * Time.deltaTime); } } /// /// 设置该段的“到达判定线的目标时间”(hitTime) 与分段延迟(delay),并由此推导移动开始时间。 /// 注意:这里必须使用与 HoldNote.hitTime 一致的时间基准(NoteSpawner 传入的谱面实时点), /// 否则用 Time.time + delay 会在首段/暂停/开歌延迟时产生系统性错位,导致永远 Miss。 /// public void ConfigureTiming(float baseHitTime, float segmentDelay, float travelTime) { // baseHitTime = startTime + noteData.time (+ globalHitDelay) float hitTime = baseHitTime + segmentDelay; activationTime = hitTime - travelTime; isMoving = false; } /// /// 兼容旧接口:不推荐。旧版用 Time.time 会导致时间基准不一致。 /// public void SetSegmentDelay(float segmentDelay) { // Fallback to old behavior if caller didn't use ConfigureTiming. activationTime = Time.time + segmentDelay; isMoving = false; } public void SetSpeed(float newSpeed) { speed = newSpeed; } public void StopMovement() { isMoving = false; } private void OnTriggerEnter2D(Collider2D collision) { if (!collision.CompareTag("JudgmentLine")) return; isInsideJudgeZone = true; OnJudgeZoneChanged?.Invoke(true); } private void OnTriggerExit2D(Collider2D collision) { if (!collision.CompareTag("JudgmentLine")) return; isInsideJudgeZone = false; OnJudgeZoneChanged?.Invoke(false); } public bool IsInsideJudgArea() { return isInsideJudgeZone; } public float ActivationTime => activationTime; public float CurrentSpeed => speed; }