修复短音符延迟
This commit is contained in:
@@ -5,7 +5,19 @@ public class TrackKeyManager : MonoBehaviour
|
||||
{
|
||||
public static TrackKeyManager Instance { get; private set; }
|
||||
|
||||
private Dictionary<int, Queue<KeyCode>> trackKeyMappings = new Dictionary<int, Queue<KeyCode>>();
|
||||
// store note instance IDs (as strings) in queue order per track
|
||||
private Dictionary<int, Queue<string>> trackKeyMappings = new Dictionary<int, Queue<string>>();
|
||||
|
||||
// Track notes currently being judged to prevent double-judging the same note
|
||||
// Format: trackIndex -> HashSet of note IDs being judged
|
||||
private Dictionary<int, HashSet<string>> trackNotesBeingJudged = new Dictionary<int, HashSet<string>>();
|
||||
|
||||
// transient per-frame consumption: ensures one physical press only affects one note per track per frame
|
||||
private Dictionary<int, int> trackConsumedFrame = new Dictionary<int, int>();
|
||||
|
||||
// track last frame we checked for focus loss
|
||||
private int lastCheckedFrame = -1;
|
||||
private bool lastFocusedState = true;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -18,41 +30,147 @@ public class TrackKeyManager : MonoBehaviour
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当音符进入判定区域时,添加按键映射
|
||||
/// </summary>
|
||||
public void RegisterKey(int trackIndex, KeyCode key)
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!trackKeyMappings.ContainsKey(trackIndex))
|
||||
trackKeyMappings[trackIndex] = new Queue<KeyCode>();
|
||||
|
||||
trackKeyMappings[trackIndex].Enqueue(key);
|
||||
// Clear per-frame consumption dictionary when focus is regained or at frame boundaries
|
||||
int currentFrame = Time.frameCount;
|
||||
bool isFocused = Application.isFocused;
|
||||
|
||||
// If we've moved to a new frame or focus state changed, clear consumption tracking
|
||||
if (lastCheckedFrame != currentFrame || (isFocused && !lastFocusedState))
|
||||
{
|
||||
// On focus regain, clear the consumed frame dictionary to allow input again
|
||||
if (isFocused && !lastFocusedState)
|
||||
{
|
||||
trackConsumedFrame.Clear();
|
||||
if (GameConfig.verboseLogs) Debug.Log("[TrackKeyManager] Focus regained, cleared track consumption");
|
||||
}
|
||||
}
|
||||
|
||||
lastCheckedFrame = currentFrame;
|
||||
lastFocusedState = isFocused;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当音符离开判定区域时,移除按键映射
|
||||
/// 当音符进入判定区域时,添加音符 id 到队列
|
||||
/// </summary>
|
||||
public void UnregisterKey(int trackIndex, KeyCode key)
|
||||
public void RegisterKey(int trackIndex, string noteInstanceId)
|
||||
{
|
||||
if (!trackKeyMappings.ContainsKey(trackIndex))
|
||||
trackKeyMappings[trackIndex] = new Queue<string>();
|
||||
|
||||
trackKeyMappings[trackIndex].Enqueue(noteInstanceId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当音符离开判定区域时,移除音符 id
|
||||
/// </summary>
|
||||
public void UnregisterKey(int trackIndex, string noteInstanceId)
|
||||
{
|
||||
if (!trackKeyMappings.ContainsKey(trackIndex) || trackKeyMappings[trackIndex].Count == 0)
|
||||
return;
|
||||
|
||||
if (trackKeyMappings[trackIndex].Peek() == key)
|
||||
// If the head matches, dequeue. Otherwise try to remove from queue by rebuilding it.
|
||||
if (trackKeyMappings[trackIndex].Peek() == noteInstanceId)
|
||||
{
|
||||
trackKeyMappings[trackIndex].Dequeue();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise remove matching id if present (preserve order of others)
|
||||
var q = trackKeyMappings[trackIndex];
|
||||
var temp = new Queue<string>();
|
||||
while (q.Count > 0)
|
||||
{
|
||||
var v = q.Dequeue();
|
||||
if (v != noteInstanceId) temp.Enqueue(v);
|
||||
}
|
||||
trackKeyMappings[trackIndex] = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取轨道当前的按键(队列头)
|
||||
/// 获取轨道当前排在队首的音符实例 id(队列头)
|
||||
/// </summary>
|
||||
public KeyCode GetCurrentKey(int trackIndex)
|
||||
public string GetCurrentNoteId(int trackIndex)
|
||||
{
|
||||
if (trackKeyMappings.ContainsKey(trackIndex) && trackKeyMappings[trackIndex].Count > 0)
|
||||
{
|
||||
return trackKeyMappings[trackIndex].Peek();
|
||||
}
|
||||
return KeyCode.None; // 无按键
|
||||
return null; // 无音符
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to register a note as being judged on a track. Returns true if successful (no other note is currently being judged).
|
||||
/// This prevents multiple notes on the same track from being judged by a single key press.
|
||||
/// </summary>
|
||||
public bool TryLockTrackForJudge(int trackIndex, string noteId)
|
||||
{
|
||||
if (trackIndex < 0) return false;
|
||||
|
||||
if (!trackNotesBeingJudged.ContainsKey(trackIndex))
|
||||
{
|
||||
trackNotesBeingJudged[trackIndex] = new HashSet<string>();
|
||||
}
|
||||
|
||||
// If there are already notes being judged on this track, reject
|
||||
if (trackNotesBeingJudged[trackIndex].Count > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lock this note
|
||||
trackNotesBeingJudged[trackIndex].Add(noteId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release the lock for a note on a track. Call this after judgment is complete.
|
||||
/// </summary>
|
||||
public void UnlockTrackForJudge(int trackIndex, string noteId)
|
||||
{
|
||||
if (trackIndex < 0) return;
|
||||
|
||||
if (trackNotesBeingJudged.ContainsKey(trackIndex))
|
||||
{
|
||||
trackNotesBeingJudged[trackIndex].Remove(noteId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to mark this track as consumed for the current frame. Returns true if this call
|
||||
/// successfully consumes the track (no other note on the same track has consumed this frame).
|
||||
/// </summary>
|
||||
public bool TryConsumeTrackForFrame(int trackIndex)
|
||||
{
|
||||
if (trackIndex < 0) return false;
|
||||
int currentFrame = Time.frameCount;
|
||||
|
||||
// If the recorded frame is old or invalid, allow consumption
|
||||
if (!trackConsumedFrame.ContainsKey(trackIndex))
|
||||
{
|
||||
trackConsumedFrame[trackIndex] = currentFrame;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we're in a different frame, reset and allow
|
||||
int recordedFrame = trackConsumedFrame[trackIndex];
|
||||
if (recordedFrame != currentFrame)
|
||||
{
|
||||
trackConsumedFrame[trackIndex] = currentFrame;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Same frame, already consumed
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force reset the consumption state (useful for testing or specific scenarios)
|
||||
/// </summary>
|
||||
public void ResetConsumptionState()
|
||||
{
|
||||
trackConsumedFrame.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user