修改部分的长音符判定bug,优化多键位同时触发的判定逻辑
Signed-off-by: Jiangqvweihuan <>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
using UnityEngine;
|
||||
|
||||
public abstract class BaseNote : MonoBehaviour
|
||||
{
|
||||
// 新增:控制音符显示状态
|
||||
[System.NonSerialized] // 避免暴露在Inspector
|
||||
public bool shouldBeVisible = true;
|
||||
public int TrackIndex { get; protected set; }
|
||||
public string NoteColor { get; protected set; }
|
||||
public float Speed { get; protected set; }
|
||||
protected float hitTime;
|
||||
|
||||
public virtual void Initialize(int trackIndex, string noteColor, float speed, float hitTime)
|
||||
{
|
||||
TrackIndex = trackIndex;
|
||||
NoteColor = noteColor;
|
||||
Speed = speed;
|
||||
this.hitTime = hitTime;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1685ef287fcd6ee449c4bc269594b792
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,131 @@
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
|
||||
public class BeatmapManager : MonoBehaviour
|
||||
|
||||
{
|
||||
public Beatmap beatmap; // 当前谱面数据
|
||||
|
||||
// 音符生成器引用
|
||||
public NoteSpawner noteSpawner;
|
||||
|
||||
|
||||
|
||||
// 保存谱面数据到 JSON 文件
|
||||
//public void SaveBeatmap(string fileName)
|
||||
//{
|
||||
// string json = JsonUtility.ToJson(beatmap, true); // 格式化 JSON 输出
|
||||
// File.WriteAllText(Application.dataPath + "/" + fileName, json);
|
||||
// Debug.Log("谱面已保存:" + Application.dataPath + "/" + fileName);
|
||||
//}
|
||||
|
||||
// 从 JSON 文件加载谱面数据
|
||||
public void LoadBeatmap(string fileName)
|
||||
{
|
||||
string path = Application.streamingAssetsPath + "/" + fileName;
|
||||
if (File.Exists(path))
|
||||
{
|
||||
string json = File.ReadAllText(path);
|
||||
beatmap = JsonUtility.FromJson<Beatmap>(json);
|
||||
//Debug.Log("谱面已加载:" + json);
|
||||
|
||||
// 在加载后立即调用音符生成
|
||||
noteSpawner.LoadBeatmap(beatmap);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.LogError("文件不存在:" + path);
|
||||
}
|
||||
}
|
||||
public void LoadBeatmap(Beatmap loadedBeatmap)
|
||||
{
|
||||
beatmap = loadedBeatmap;
|
||||
//Debug.Log("谱面已加载:" + beatmap.title);
|
||||
|
||||
// 在加载后立即调用音符生成
|
||||
noteSpawner.LoadBeatmap(beatmap);
|
||||
}
|
||||
|
||||
//public void LoadBeatmapFromFile(string fileName)
|
||||
//{
|
||||
// string path = Application.persistentDataPath + "/" + fileName;
|
||||
// if (File.Exists(path))
|
||||
// {
|
||||
// string json = File.ReadAllText(path);
|
||||
// Beatmap beatmap = JsonUtility.FromJson<Beatmap>(json);
|
||||
// beatmapManager.LoadBeatmap(beatmap); // 使用 LoadBeatmap 方法传递 Beatmap 对象
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Debug.LogError("文件不存在:" + path);
|
||||
// }
|
||||
//}
|
||||
|
||||
// 创建一个示例谱面并保存
|
||||
//public void CreateSampleBeatmap()
|
||||
//{
|
||||
// beatmap = new Beatmap
|
||||
// {
|
||||
// title = "Moonlight Sonata",
|
||||
// composer = "Ludwig van Beethoven",
|
||||
// illustrator = "John Doe",
|
||||
// charter = "Jane Smith",
|
||||
// beatmapId = "ML-001",
|
||||
// duration = 120.5f,
|
||||
// bpm = 128f,
|
||||
// difficulty = 5,
|
||||
// difficultyName = "Hard",
|
||||
// createdDate = System.DateTime.Now.ToString("yyyy-MM-dd"),
|
||||
// musicFile = "moonlight_sonata.mp3",
|
||||
// backgroundFile = "moonlight_art.png",
|
||||
|
||||
// tracks = new TrackData[]
|
||||
// {
|
||||
// new TrackData { color = "red" },
|
||||
// new TrackData { color = "green" },
|
||||
// new TrackData { color = "yellow" },
|
||||
// new TrackData { color = "purple" },
|
||||
// new TrackData { color = "blue" }
|
||||
// },
|
||||
|
||||
// notes = new NoteData[]
|
||||
// {
|
||||
// new NoteData { trackIndex = 0, time = 1.0f, color = "red", type = "tap", length = 0.0f },
|
||||
// new NoteData { trackIndex = 4, time = 2.0f, color = "blue", type = "tap", length = 0.0f },
|
||||
// new NoteData { trackIndex = 1, time = 3.5f, color = "green", type = "tap", length = 0.0f },
|
||||
// new NoteData { trackIndex = 2, time = 3.5f, color = "yellow", type = "tap", length = 0.0f },
|
||||
// new NoteData { trackIndex = 2, time = 3.5f, color = "yellow", type = "tap", length = 0.0f },
|
||||
// new NoteData { trackIndex = 3, time = 3.5f, color = "purple", type = "tap", length = 0.0f },
|
||||
// }
|
||||
// };
|
||||
|
||||
// SaveBeatmap("test_beatmap.json");
|
||||
//}
|
||||
|
||||
// 读取并加载当前谱面到音符生成器
|
||||
public void LoadBeatmapFromFile()
|
||||
{
|
||||
string path = Application.streamingAssetsPath + "/Emilia_demo.json";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
string json = File.ReadAllText(path);
|
||||
beatmap = JsonUtility.FromJson<Beatmap>(json);
|
||||
noteSpawner.LoadBeatmap(beatmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.LogError("谱面文件未找到!");
|
||||
}
|
||||
}
|
||||
|
||||
// 在 Start() 方法中初始化
|
||||
void Start()
|
||||
{
|
||||
// 这里可以选择在启动时创建一个示例谱面并加载
|
||||
//CreateSampleBeatmap(); // 创建并保存一个示例谱面
|
||||
LoadBeatmap("Emilia_demo.json"); // 加载并实例化音符
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c80308880c1feca40bcf476848142e2b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,119 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using static HoldNote;
|
||||
public enum NoteScore
|
||||
{
|
||||
Perfect,
|
||||
Good,
|
||||
Okay,
|
||||
Miss
|
||||
}
|
||||
|
||||
public class GameManager : MonoBehaviour
|
||||
{
|
||||
public static GameManager Instance { get; private set; }
|
||||
public BeatmapManager beatmapManager; // 负责加载和管理谱面
|
||||
public NoteSpawner noteSpawner; // 音符生成器
|
||||
public AudioSource musicSource; // 音乐播放器
|
||||
public int CurrentScore { get; private set; }
|
||||
public int MaxCombo { get; private set; }
|
||||
public int CurrentCombo { get; private set; }
|
||||
|
||||
// 添加计分方法
|
||||
public void AddScore(NoteScore scoreType)
|
||||
{
|
||||
// 计分逻辑
|
||||
switch (scoreType)
|
||||
{
|
||||
case NoteScore.Perfect:
|
||||
CurrentScore += 100;
|
||||
Debug.Log("Perfect! +100");
|
||||
break;
|
||||
case NoteScore.Good:
|
||||
CurrentScore += 80;
|
||||
Debug.Log("Good! +80");
|
||||
break;
|
||||
case NoteScore.Okay:
|
||||
CurrentScore += 50;
|
||||
Debug.Log("Okay +50");
|
||||
break;
|
||||
case NoteScore.Miss:
|
||||
CurrentCombo = 0; // 连击中断
|
||||
Debug.Log("Miss! 连击中断");
|
||||
return; // 不增加分数
|
||||
}
|
||||
|
||||
// 连击处理
|
||||
CurrentCombo++;
|
||||
if (CurrentCombo > MaxCombo)
|
||||
MaxCombo = CurrentCombo;
|
||||
}
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
}
|
||||
void Start()
|
||||
{
|
||||
//Debug.Log("GameManager Start() 被调用");
|
||||
|
||||
// 检查关键组件是否为空
|
||||
if (beatmapManager == null) Debug.LogError("beatmapManager 未赋值!");
|
||||
if (noteSpawner == null) Debug.LogError("noteSpawner 未赋值!");
|
||||
if (musicSource == null) Debug.LogError("musicSource 未赋值!");
|
||||
|
||||
// 在游戏开始时加载并初始化谱面
|
||||
string beatmapFilePath = Path.Combine(Application.streamingAssetsPath, "Emilia_demo.json");
|
||||
|
||||
if (!File.Exists(beatmapFilePath))
|
||||
{
|
||||
//Debug.LogError($"谱面文件不存在: {beatmapFilePath}");
|
||||
return;
|
||||
}
|
||||
|
||||
string json = File.ReadAllText(beatmapFilePath);
|
||||
Beatmap beatmap = JsonUtility.FromJson<Beatmap>(json); // 解析 JSON 为 Beatmap 对象
|
||||
|
||||
if (beatmap == null)
|
||||
{
|
||||
//Debug.LogError("谱面解析失败,JSON 格式可能错误!");
|
||||
return;
|
||||
}
|
||||
|
||||
beatmapManager.LoadBeatmap(beatmap); // 传递给 BeatmapManager
|
||||
noteSpawner.LoadBeatmap(beatmap); // 传递给 NoteSpawner
|
||||
|
||||
|
||||
// 播放背景音乐
|
||||
if (!string.IsNullOrEmpty(beatmap.musicFile))
|
||||
{
|
||||
string musicPath = Path.Combine("song_songIndex/1002001_emilia", Path.GetFileNameWithoutExtension(beatmap.musicFile));
|
||||
AudioClip musicClip = Resources.Load<AudioClip>(musicPath);
|
||||
|
||||
if (musicClip == null)
|
||||
{
|
||||
//Debug.LogError($"音乐文件加载失败,尝试路径: {musicPath}");
|
||||
// 列出Resources文件夹下所有音频文件用于调试
|
||||
var allAudio = Resources.LoadAll<AudioClip>("");
|
||||
//Debug.Log("可用音频文件:" + string.Join(", ", allAudio.Select(a => a.name)));
|
||||
}
|
||||
else
|
||||
{
|
||||
musicSource.clip = musicClip;
|
||||
musicSource.Play();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.LogError("谱面中 musicFile 为空!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a3cb3ef9478edd4a8b6718a11a0fc3c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,444 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public enum NoteSegment { None, Start, Middle, End }
|
||||
|
||||
[RequireComponent(typeof(HoldNoteController))]
|
||||
public class HoldNote : BaseNote
|
||||
{
|
||||
[Header("判定容差")]
|
||||
public float startHitWindow = 0.1f; // 起始段判定窗口
|
||||
public float endHitWindow = 0.15f; // 结束段判定窗口
|
||||
public float segmentGracePeriod = 0.05f; // 分段间宽限时间
|
||||
|
||||
private KeyCode keyToPress = KeyCode.None;
|
||||
private string noteColor = string.Empty;
|
||||
private string noteID = string.Empty;
|
||||
private NoteSegment segment = NoteSegment.None;
|
||||
|
||||
private new float hitTime = 0f;
|
||||
private float releaseTime = 0f;
|
||||
private bool hasReleased = true;
|
||||
private bool hasEnteredLine = false;
|
||||
|
||||
private Coroutine autoReturnCoroutine;
|
||||
private HoldNoteController controller;
|
||||
private static Dictionary<int, bool> _holdNoteValidity = new Dictionary<int, bool>();
|
||||
|
||||
// 新增变量:按键按住检测
|
||||
private bool _isKeyHeld = false;
|
||||
private float _lastKeyPressTime = 0f;
|
||||
private const float KEY_CHECK_INTERVAL = 0.05f;
|
||||
private Coroutine _keyCheckCoroutine;
|
||||
|
||||
public int id;
|
||||
public int trackIndex;
|
||||
public float speed;
|
||||
public float startTime;
|
||||
public float delay;
|
||||
public bool isEnd;
|
||||
public float scheduledEndTime;
|
||||
public string color;
|
||||
public KeyCode key;
|
||||
public string type; // "start", "middle", or "end"
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
controller = GetComponent<HoldNoteController>();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
ResetState();
|
||||
}
|
||||
|
||||
public void Setup(int id, int trackIndex, float speed, float time, float delay,
|
||||
bool isEnd, float scheduledEnd, string color, KeyCode key, string type)
|
||||
{
|
||||
this.id = id;
|
||||
this.trackIndex = trackIndex;
|
||||
this.speed = speed;
|
||||
this.startTime = time;
|
||||
this.delay = delay;
|
||||
this.isEnd = isEnd;
|
||||
this.scheduledEndTime = scheduledEnd;
|
||||
this.color = color;
|
||||
this.key = key;
|
||||
this.type = type;
|
||||
|
||||
this.noteID = id.ToString();
|
||||
this.keyToPress = key;
|
||||
this.noteColor = color;
|
||||
this.hitTime = time + delay;
|
||||
this.segment = (delay == 0f) ? NoteSegment.Start :
|
||||
(isEnd ? NoteSegment.End : NoteSegment.Middle);
|
||||
this.hasEnteredLine = false;
|
||||
this.hasReleased = false;
|
||||
|
||||
JudgeManager.Instance?.RegisterNoteReleased(noteID, false);
|
||||
if (segment == NoteSegment.Start)
|
||||
JudgeManager.Instance?.RegisterStartJudged(noteID, false);
|
||||
|
||||
if (segment == NoteSegment.End)
|
||||
JudgeManager.Instance?.RegisterScheduledEndTime(noteID, scheduledEndTime);
|
||||
|
||||
// 如果是起始段,初始化状态为true
|
||||
if (type == "start")
|
||||
{
|
||||
_holdNoteValidity[id] = true;
|
||||
}
|
||||
|
||||
// 立即检查有效性
|
||||
if (type != "start" && (!_holdNoteValidity.ContainsKey(id) || !_holdNoteValidity[id]))
|
||||
{
|
||||
JudgeMiss();
|
||||
}
|
||||
|
||||
controller?.SetSpeed(speed);
|
||||
controller?.SetSegmentDelay(delay);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 检查长音符是否有效
|
||||
if (!IsHoldNoteValid()) return;
|
||||
|
||||
if (segment == NoteSegment.Start && Input.GetKeyDown(keyToPress))
|
||||
{
|
||||
if (hasEnteredLine)
|
||||
HandleStart();
|
||||
}
|
||||
else if (segment == NoteSegment.End && Input.GetKeyUp(keyToPress))
|
||||
{
|
||||
HandleEnd();
|
||||
}
|
||||
|
||||
// 更新按键状态
|
||||
if (segment == NoteSegment.Middle && hasEnteredLine)
|
||||
{
|
||||
if (JudgeManager.Instance.IsStartJudged(noteID) &&
|
||||
!JudgeManager.Instance.HasNoteReleased(noteID))
|
||||
{
|
||||
if (!Input.GetKey(keyToPress))
|
||||
{
|
||||
// 中途松开按键,判定失败
|
||||
_holdNoteValidity[id] = false;
|
||||
JudgeMiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsHoldNoteValid()
|
||||
{
|
||||
// 起始段总是有效(因为它决定有效性)
|
||||
if (segment == NoteSegment.Start) return true;
|
||||
|
||||
// 其他段检查有效性
|
||||
return _holdNoteValidity.ContainsKey(id) && _holdNoteValidity[id];
|
||||
}
|
||||
|
||||
private IEnumerator CheckKeyHold()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (Input.GetKey(keyToPress))
|
||||
{
|
||||
_isKeyHeld = true;
|
||||
_lastKeyPressTime = Time.time;
|
||||
}
|
||||
else if (_isKeyHeld && Time.time - _lastKeyPressTime > KEY_CHECK_INTERVAL)
|
||||
{
|
||||
// 按键松开超过检查间隔,判定为松开
|
||||
_isKeyHeld = false;
|
||||
if (segment == NoteSegment.Middle || segment == NoteSegment.End)
|
||||
{
|
||||
// 中间段或结束段松开按键,标记长音符无效
|
||||
_holdNoteValidity[id] = false;
|
||||
JudgeMiss();
|
||||
yield break; // 停止检查
|
||||
}
|
||||
}
|
||||
yield return new WaitForSeconds(KEY_CHECK_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerEnter2D(Collider2D collision)
|
||||
{
|
||||
if (!collision.CompareTag("JudgmentLine")) return;
|
||||
if (!IsHoldNoteValid()) return;
|
||||
|
||||
if (segment == NoteSegment.Start || segment == NoteSegment.Middle)
|
||||
{
|
||||
hasEnteredLine = true;
|
||||
}
|
||||
|
||||
if (segment == NoteSegment.Middle)
|
||||
{
|
||||
//Debug.Log($"[HoldNote] Middle段进入判定线: color={noteColor}, isStartJudged={JudgeManager.Instance.IsStartJudged(noteID)}, hasReleased={JudgeManager.Instance.HasNoteReleased(noteID)}, isKeyDown={Input.GetKey(keyToPress)}");
|
||||
|
||||
if (autoReturnCoroutine != null)
|
||||
StopCoroutine(autoReturnCoroutine);
|
||||
|
||||
autoReturnCoroutine = StartCoroutine(DelayedAutoReturnCheck());
|
||||
}
|
||||
|
||||
if (segment == NoteSegment.End)
|
||||
{
|
||||
if (autoReturnCoroutine != null)
|
||||
StopCoroutine(autoReturnCoroutine);
|
||||
|
||||
autoReturnCoroutine = StartCoroutine(DelayedAutoReturnCheck());
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerExit2D(Collider2D collision)
|
||||
{
|
||||
if (!collision.CompareTag("JudgmentLine")) return;
|
||||
if (!IsHoldNoteValid()) return;
|
||||
|
||||
if (segment == NoteSegment.Start)
|
||||
{
|
||||
if (!JudgeManager.Instance.IsStartJudged(noteID))
|
||||
{
|
||||
ReturnToPool();
|
||||
}
|
||||
}
|
||||
if (segment == NoteSegment.Middle)
|
||||
{
|
||||
if (!JudgeManager.Instance.IsStartJudged(noteID) ||
|
||||
JudgeManager.Instance.HasNoteReleased(noteID))
|
||||
{
|
||||
//Debug.Log($"[HoldNote] Middle段离开判定线强制回收: {noteColor}");
|
||||
ReturnToPool();
|
||||
}
|
||||
}
|
||||
else if (segment == NoteSegment.End)
|
||||
{
|
||||
if (!JudgeManager.Instance.IsStartJudged(noteID) || JudgeManager.Instance.HasNoteReleased(noteID))
|
||||
{
|
||||
ReturnToPool();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator DelayedAutoReturnCheck()
|
||||
{
|
||||
yield return null;
|
||||
|
||||
if (!IsHoldNoteValid()) yield break;
|
||||
|
||||
bool isStartJudgedBefore = JudgeManager.Instance.IsStartJudged(noteID);
|
||||
bool isKeyHeldNow = Input.GetKey(keyToPress);
|
||||
|
||||
//Debug.Log($"[HoldNote] 延迟检查开始: noteID={noteID}, color={noteColor}, isStartJudged={isStartJudgedBefore}, isKeyHeld={isKeyHeldNow}");
|
||||
|
||||
if (!isStartJudgedBefore && isKeyHeldNow)
|
||||
{
|
||||
//Debug.Log($"[HoldNote] 延迟回补 Start 判定触发: {noteColor}");
|
||||
HandleStart();
|
||||
}
|
||||
|
||||
bool isStartJudged = JudgeManager.Instance.IsStartJudged(noteID);
|
||||
bool hasReleased = JudgeManager.Instance.HasNoteReleased(noteID);
|
||||
bool isKeyHeld = Input.GetKey(keyToPress);
|
||||
|
||||
//Debug.Log($"[HoldNote] 延迟确认状态: isStartJudged={isStartJudged}, hasReleased={hasReleased}, isKeyHeld={isKeyHeld}");
|
||||
|
||||
if (segment == NoteSegment.Middle && isStartJudged && isKeyHeld)
|
||||
{
|
||||
float waitTime = Mathf.Max(0f, hitTime - Time.time);
|
||||
//Debug.Log($"[HoldNote] Middle段通过延迟确认,准备等待到 hitTime 回收: {noteColor}, 剩余时间={waitTime:F2}s");
|
||||
|
||||
yield return new WaitForSeconds(waitTime);
|
||||
|
||||
if (Input.GetKey(keyToPress) && !JudgeManager.Instance.HasNoteReleased(noteID))
|
||||
{
|
||||
//Debug.Log($"[HoldNote] Middle段在 hitTime 达成条件,回收: {noteColor}");
|
||||
ReturnToPool();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.Log($"[HoldNote] Middle段在 hitTime 条件不符,不回收: {noteColor}");
|
||||
}
|
||||
}
|
||||
else if (segment == NoteSegment.End && !hasReleased)
|
||||
{
|
||||
//Debug.Log($"[HoldNote] End段延迟检查后仍未释放: {noteColor}");
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.Log($"[HoldNote] DelayedAutoReturnCheck 无需处理: segment={segment}, color={noteColor}");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleStart()
|
||||
{
|
||||
if (!IsHoldNoteValid()) return;
|
||||
|
||||
float pressTime = Time.time;
|
||||
float offset = Mathf.Abs(pressTime - hitTime);
|
||||
|
||||
if (offset < startHitWindow)
|
||||
{
|
||||
//Debug.Log($"[HoldNote] START判定成功(偏差={offset:F2}秒): {noteColor}");
|
||||
JudgeManager.Instance.RegisterStartJudged(noteID, true);
|
||||
JudgeManager.Instance.RegisterHoldNoteStart(noteID, Time.time);
|
||||
|
||||
// 开始按键按住检测
|
||||
_isKeyHeld = true;
|
||||
_lastKeyPressTime = Time.time;
|
||||
if (_keyCheckCoroutine != null)
|
||||
StopCoroutine(_keyCheckCoroutine);
|
||||
_keyCheckCoroutine = StartCoroutine(CheckKeyHold());
|
||||
|
||||
hasEnteredLine = true;
|
||||
StartCoroutine(DelayedReturn());
|
||||
}
|
||||
else
|
||||
{
|
||||
_holdNoteValidity[id] = false;
|
||||
//Debug.Log($"[HoldNote] START Miss(偏差={offset:F2}秒,窗口={startHitWindow:F2}秒): {noteColor}");
|
||||
JudgeMiss();
|
||||
}
|
||||
}
|
||||
|
||||
private void JudgeMiss()
|
||||
{
|
||||
// 执行Miss判定逻辑
|
||||
//Debug.Log($"[HoldNote] 判定为Miss: {noteColor}");
|
||||
ReturnToPool();
|
||||
}
|
||||
|
||||
private IEnumerator DelayedReturn()
|
||||
{
|
||||
yield return new WaitForSeconds(0.05f);
|
||||
ReturnToPool();
|
||||
}
|
||||
|
||||
private void HandleEnd()
|
||||
{
|
||||
if (!IsHoldNoteValid()) return;
|
||||
if (!JudgeManager.Instance.IsStartJudged(noteID))
|
||||
{
|
||||
Debug.Log($"[HoldNote] 结束段判定失败:起始段未正确判定");
|
||||
return;
|
||||
}
|
||||
|
||||
releaseTime = Time.time;
|
||||
hasReleased = true;
|
||||
float diff = Mathf.Abs(releaseTime - scheduledEndTime);
|
||||
string result = "Miss";
|
||||
|
||||
if (diff < endHitWindow)
|
||||
{
|
||||
result = "Perfect";
|
||||
JudgeManager.Instance.RegisterNoteReleased(noteID, true);
|
||||
ReturnToPool();
|
||||
}
|
||||
else if (diff < endHitWindow * 2f)
|
||||
{
|
||||
result = "Good";
|
||||
JudgeManager.Instance.RegisterNoteReleased(noteID, true);
|
||||
ReturnToPool();
|
||||
}
|
||||
|
||||
Debug.Log($"[HoldNote] END判定结果: {noteColor} {result} (release={releaseTime:F2}, target={scheduledEndTime:F2})");
|
||||
}
|
||||
|
||||
private void OnTriggerStay2D(Collider2D collision)
|
||||
{
|
||||
if (!collision.CompareTag("JudgmentLine")) return;
|
||||
if (!IsHoldNoteValid()) return;
|
||||
|
||||
if (segment == NoteSegment.Middle &&
|
||||
JudgeManager.Instance.IsStartJudged(noteID) &&
|
||||
Input.GetKey(keyToPress))
|
||||
{
|
||||
hasEnteredLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (autoReturnCoroutine != null)
|
||||
{
|
||||
StopCoroutine(autoReturnCoroutine);
|
||||
autoReturnCoroutine = null;
|
||||
}
|
||||
|
||||
if (_keyCheckCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_keyCheckCoroutine);
|
||||
_keyCheckCoroutine = null;
|
||||
}
|
||||
|
||||
if (segment == NoteSegment.End &&
|
||||
!hasReleased &&
|
||||
!JudgeManager.Instance.HasNoteReleased(noteID) &&
|
||||
keyToPress != KeyCode.None &&
|
||||
!Input.GetKey(keyToPress))
|
||||
{
|
||||
hasReleased = true;
|
||||
JudgeManager.Instance.RegisterNoteReleased(noteID, true);
|
||||
//Debug.Log($"[HoldNote] OnDisable补偿KeyUp: {noteColor}");
|
||||
}
|
||||
|
||||
controller?.StopMovement();
|
||||
}
|
||||
|
||||
private void ReturnToPool()
|
||||
{
|
||||
if (!gameObject.activeSelf) return;
|
||||
|
||||
if (segment == NoteSegment.Start && !hasEnteredLine)
|
||||
{
|
||||
//Debug.LogWarning($"[HoldNote] 阻止回收尚未进入判定线的 Start段: {noteColor}");
|
||||
return;
|
||||
}
|
||||
|
||||
gameObject.SetActive(false);
|
||||
|
||||
if (segment == NoteSegment.Start)
|
||||
NotePool.Instance.ReturnStartNote(gameObject, noteColor);
|
||||
else
|
||||
NotePool.Instance.ReturnHoldNoteSegment(gameObject, noteColor);
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
hasReleased = false;
|
||||
segment = NoteSegment.None;
|
||||
keyToPress = KeyCode.None;
|
||||
noteColor = string.Empty;
|
||||
noteID = string.Empty;
|
||||
hitTime = 0f;
|
||||
scheduledEndTime = 0f;
|
||||
hasEnteredLine = false;
|
||||
_isKeyHeld = false;
|
||||
_lastKeyPressTime = 0f;
|
||||
|
||||
if (autoReturnCoroutine != null)
|
||||
{
|
||||
StopCoroutine(autoReturnCoroutine);
|
||||
autoReturnCoroutine = null;
|
||||
}
|
||||
|
||||
if (_keyCheckCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_keyCheckCoroutine);
|
||||
_keyCheckCoroutine = null;
|
||||
}
|
||||
|
||||
controller?.StopMovement();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (segment == NoteSegment.End && _holdNoteValidity.ContainsKey(id))
|
||||
{
|
||||
_holdNoteValidity.Remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86977693d5a2d364bb086303f60ca1b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user