收拾烂摊子

增加编队初步,改动了判定系统,编队UI组装好
This commit is contained in:
FloatGaming
2025-09-28 00:23:05 +08:00
parent 1d818fa4eb
commit 98510eef0f
53 changed files with 10492 additions and 60 deletions
+67 -19
View File
@@ -37,10 +37,30 @@ public class HoldNote : BaseNote
// 新增变量
private bool hasBeenHeldFromStart = false; // 进入判定区时是否已按住 (用于辅助判断,但不再直接阻断判定)
[Header("判定区间配置")]
public NoteJudgeConfig judgeConfig; // 判定区间配置,需在预制体或生成时赋值
private void Awake()
{
controller = GetComponent<HoldNoteController>();
anim = GetComponent<AnimationController>();
if (controller != null)
{
controller.OnJudgeZoneChanged += OnJudgeZoneChanged;
}
}
private void OnDestroy()
{
if (controller != null)
{
controller.OnJudgeZoneChanged -= OnJudgeZoneChanged;
}
}
private void OnJudgeZoneChanged(bool inZone)
{
hasEnteredLine = inZone;
}
private void OnEnable()
@@ -68,7 +88,7 @@ public class HoldNote : BaseNote
}
public void Setup(int id, int trackIndex, float speed, float time, float delay,
bool isEnd, float scheduledEnd, string color, KeyCode key, string type)
bool isEnd, float scheduledEnd, string color, KeyCode key, string type, NoteJudgeConfig judgeConfig)
{
this.id = id;
this.trackIndex = trackIndex;
@@ -80,6 +100,7 @@ public class HoldNote : BaseNote
this.color = color;
this.key = key;
this.type = type;
this.judgeConfig = judgeConfig;
this.noteID = id.ToString();
this.keyToPress = key;
@@ -90,6 +111,12 @@ public class HoldNote : BaseNote
this.hasEnteredLine = false;
this.hasReleased = false;
// 判定区间配置检查
if (judgeConfig == null)
Debug.LogError($"HoldNoteJudgeConfig is null! 判定区间配置未传入!track={trackIndex}");
else
Debug.Log($"HoldNoteJudgeConfig: perfect={judgeConfig.perfectRange}, great={judgeConfig.greatRange}, good={judgeConfig.goodRange}, miss={judgeConfig.missRange}");
// 在 Setup 时注册初始状态
JudgeManager.Instance?.RegisterNoteReleased(noteID, false);
if (segment == NoteSegment.Start)
@@ -304,9 +331,10 @@ public class HoldNote : BaseNote
{
float pressTime = Time.time;
float offset = Mathf.Abs(pressTime - hitTime);
string result;
if (offset < 0.1f) // 判定成功
{
result = "Perfect";
Debug.Log($"[HoldNote] START判定成功(偏差={offset:F2}秒): {noteColor}");
JudgeManager.Instance.RegisterStartJudged(noteID, true);
isHoldActive = true; // 成功判定Start,长按状态激活
@@ -314,11 +342,12 @@ public class HoldNote : BaseNote
}
else
{
result = "Miss";
Debug.Log($"[HoldNote] START Miss(偏差={offset:F2}秒): {noteColor}");
JudgeManager.Instance.RegisterStartJudged(noteID, false); // 明确标记未判定
isHoldActive = false; // Start Miss,长按状态不激活
}
InputManager.Instance?.ShowJudgeResult(trackIndex, result);
// Start段判定后立即回收,因为它的视觉部分可能只需要短暂显示
StartCoroutine(DelayedReturn());
}
@@ -332,46 +361,65 @@ public class HoldNote : BaseNote
}
}
// 重载 HandleEnd 方法,增加一个强制Miss的参数
private void HandleEnd(bool forceMiss = false)
{
releaseTime = Time.time;
hasReleased = true; // 标记此音符已被释放
JudgeManager.Instance.RegisterNoteReleased(noteID, true); // 在 JudgeManager 中注册释放
hasReleased = true;
JudgeManager.Instance.RegisterNoteReleased(noteID, true);
string result;
// 如果强制Miss,或者头部未判定,或者长按已经中断,则直接判定为Miss
if (forceMiss || !JudgeManager.Instance.IsStartJudged(noteID) || !isHoldActive)
// 只要头部判定通过,松手时机在判定区间内就给出对应判定
if (forceMiss || !JudgeManager.Instance.IsStartJudged(noteID))
{
result = "Miss";
Debug.LogWarning($"[HoldNote] END判定失败({noteColor}):{(forceMiss ? "Miss" : (!JudgeManager.Instance.IsStartJudged(noteID) ? "" : ""))}");
Debug.LogWarning($"[HoldNote] END判定失败({noteColor}):{(forceMiss ? "Miss" : "")}");
}
else if (judgeConfig != null)
{
float diff = Mathf.Abs(releaseTime - scheduledEndTime);
if (diff <= judgeConfig.perfectRange)
{
result = "Perfect";
}
else if (diff <= judgeConfig.greatRange)
{
result = "Great";
}
else if (diff <= judgeConfig.goodRange)
{
result = "Good";
}
else if (diff <= judgeConfig.missRange)
{
result = "Miss";
}
else
{
result = "Miss";
}
}
else
{
float diff = Mathf.Abs(releaseTime - scheduledEndTime);
// End段的判定规则
if (diff <= 0.1f) // 更严格的完美判定窗口
if (diff <= 0.1f)
{
result = "Perfect";
}
else if (diff < 0.2f) // 适当放宽的Great判定
else if (diff < 0.2f)
{
result = "Great";
}
else if (diff < 0.3f) // 再次放宽的Good判定
else if (diff < 0.3f)
{
result = "Good";
}
else
{
result = "Bad"; // 更大的偏差可能视为Bad或Miss
result = "Bad";
}
}
InputManager.Instance?.ShowJudgeResult(trackIndex, result);
Debug.Log($"[HoldNote] END判定结果: {noteColor} {result} (release={releaseTime:F2}, target={scheduledEndTime:F2})");
ReturnToPool(); // 判定后回收
ReturnToPool();
}
private void OnDisable()
@@ -1,11 +1,33 @@
using UnityEngine;
using TMPro;
using System;
public class InputManager : MonoBehaviour
{
public static InputManager Instance { get; private set; }
public static event Action<KeyCode> OnKeyPressed;
public static event Action<KeyCode> OnKeyReleased;
[Header("轨道判定显示TMP对象(请在Inspector中连接)")]
public TextMeshProUGUI[] trackJudgeTexts = new TextMeshProUGUI[5];
[Header("是否显示判定文字")]
public bool showJudgeText = true;
// 判定结果对应颜色
public Color perfectColor = Color.yellow;
public Color greatColor = Color.green;
public Color goodColor = Color.cyan;
public Color missColor = Color.red;
private void Awake()
{
if (Instance == null)
Instance = this;
else
Destroy(gameObject);
}
private void Update()
{
foreach (string color in new string[] { "red", "green", "yellow", "purple", "blue" })
@@ -23,4 +45,34 @@ public class InputManager : MonoBehaviour
OnKeyReleased?.Invoke(key);
}
}
/// <summary>
/// 更新指定轨道的判定文本和颜色
/// </summary>
public void ShowJudgeResult(int trackIndex, string result)
{
if (!showJudgeText) return;
if (trackJudgeTexts == null || trackIndex < 0 || trackIndex >= trackJudgeTexts.Length) return;
var textObj = trackJudgeTexts[trackIndex];
if (textObj == null) return;
textObj.text = result;
switch (result)
{
case "Perfect":
textObj.color = perfectColor;
break;
case "Great":
textObj.color = greatColor;
break;
case "Good":
textObj.color = goodColor;
break;
case "Miss":
textObj.color = missColor;
break;
default:
textObj.color = Color.white;
break;
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d2b29530f1a693242b59ebe1fb54f8d2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 088ed1ed6b6731f43ad3166781e3e931, type: 3}
m_Name: NoteJudgeConfig
m_EditorClassIdentifier:
perfectRange: 0.05
greatRange: 0.1
goodRange: 0.2
missRange: 0.3
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f870aa419242e72438b57ac9659f61e9
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+73 -15
View File
@@ -8,13 +8,16 @@ public class Note : BaseNote
private NoteController controller;
private bool isJudged = false;
[Header("判定区间配置")]
public NoteJudgeConfig judgeConfig; // 判定区间配置,需在预制体或生成时赋值
private void Awake()
{
anim = GetComponent<AnimationController>();
controller = GetComponent<NoteController>();
}
public void Setup(KeyCode key, int trackIndex, float speed, float hitTime, string color)
public void Setup(KeyCode key, int trackIndex, float speed, float hitTime, string color, NoteJudgeConfig judgeConfig)
{
keyToPress = key;
noteColor = color;
@@ -22,6 +25,7 @@ public class Note : BaseNote
Speed = speed;
this.hitTime = hitTime;
isJudged = false;
this.judgeConfig = judgeConfig;
if (controller != null)
{
@@ -29,6 +33,12 @@ public class Note : BaseNote
}
InputManager.OnKeyPressed += HandlePress;
// 判定区间配置检查
if (judgeConfig == null)
Debug.LogError($"NoteJudgeConfig is null! 判定区间配置未传入!track={trackIndex}");
else
Debug.Log($"NoteJudgeConfig: perfect={judgeConfig.perfectRange}, great={judgeConfig.greatRange}, good={judgeConfig.goodRange}, miss={judgeConfig.missRange}");
}
private void OnDestroy()
@@ -38,27 +48,75 @@ public class Note : BaseNote
private void HandlePress(KeyCode key)
{
// 确保只有匹配的按键触发,并且音符处于判定区
// 只判定正确轨道且在判定区
if (isJudged || key != keyToPress || (controller != null && !controller.IsInJudgeZone()))
return;
float timeDifference = Mathf.Abs(Time.time - hitTime);
string judgeResult = null;
if (timeDifference <= 0.08f)
if (judgeConfig != null)
{
Debug.Log($"短音符 {keyToPress}: Perfect");
}
else if (timeDifference <= 0.15f)
{
Debug.Log($"短音符 {keyToPress}: Great");
if (timeDifference <= judgeConfig.perfectRange)
{
Debug.Log($"{keyToPress}: Perfect");
judgeResult = "Perfect";
}
else if (timeDifference <= judgeConfig.greatRange)
{
Debug.Log($"{keyToPress}: Great");
judgeResult = "Great";
}
else if (timeDifference <= judgeConfig.goodRange)
{
Debug.Log($"{keyToPress}: Good");
judgeResult = "Good";
}
else if (timeDifference <= judgeConfig.missRange)
{
Debug.Log($"{keyToPress}: Miss");
judgeResult = "Miss";
InputManager.Instance?.ShowJudgeResult(TrackIndex, judgeResult);
JudgeMiss();
return;
}
else
{
Debug.Log($"{keyToPress}: Miss");
judgeResult = "Miss";
InputManager.Instance?.ShowJudgeResult(TrackIndex, judgeResult);
JudgeMiss();
return;
}
}
else
{
Debug.Log($"短音符 {keyToPress} Miss");
JudgeMiss();
return;
// 兼容旧逻辑
if (timeDifference <= 0.08f)
{
Debug.Log($"{keyToPress}: Perfect");
judgeResult = "Perfect";
}
else if (timeDifference <= 0.15f)
{
Debug.Log($"{keyToPress}: Great");
judgeResult = "Great";
}
else
{
Debug.Log($"{keyToPress}: Miss");
judgeResult = "Miss";
InputManager.Instance?.ShowJudgeResult(TrackIndex, judgeResult);
JudgeMiss();
return;
}
}
// 显示判定文本
if (!string.IsNullOrEmpty(judgeResult))
{
InputManager.Instance?.ShowJudgeResult(TrackIndex, judgeResult);
}
Judge();
}
@@ -81,7 +139,6 @@ public class Note : BaseNote
ReturnToPool();
if (anim !=null)
{
Debug.Log("执行");
anim.PlayDestroyAnimation(noteColor);
}
@@ -91,7 +148,8 @@ public class Note : BaseNote
{
if (isJudged) return;
isJudged = true;
Debug.Log($"短音符 {keyToPress} Miss");
Debug.Log($"{keyToPress} Miss");
// InputManager.Instance?.ShowJudgeResult(TrackIndex, "Miss"); // 已在HandlePress中调用
ReturnToPool();
}
@@ -117,11 +175,11 @@ public class Note : BaseNote
}
/// <summary>
/// 由 Controller 调用,通知 Note 是否判定区域内
/// 由 Controller 通知 Note 是否进入判定区
/// </summary>
public void SetJudgeZone(bool inZone)
{
// 如果音符离开判定区域且还未判定则判 Miss
// 离开判定区未判定则判 Miss
if (!inZone && !isJudged)
{
JudgeMiss();
@@ -0,0 +1,18 @@
using UnityEngine;
/// <summary>
/// 音符判定区间配置,支持在 Inspector 中调整
/// </summary>
[CreateAssetMenu(fileName = "NoteJudgeConfig", menuName = "Game/NoteJudgeConfig")]
public class NoteJudgeConfig : ScriptableObject
{
[Header("判定区间(单位:秒)")]
[Tooltip("Perfect 判定最大时间偏差")]
public float perfectRange = 0.05f;
[Tooltip("Great 判定最大时间偏差")]
public float greatRange = 0.1f;
[Tooltip("Good 判定最大时间偏差")]
public float goodRange = 0.2f;
[Tooltip("Miss 判定最大时间偏差")]
public float missRange = 0.3f;
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 088ed1ed6b6731f43ad3166781e3e931
@@ -18,6 +18,8 @@ public class NoteSpawner : MonoBehaviour
public float spawnOffset = 0f; // 生成音符时的时间偏移量
public float bpm;
public NoteJudgeConfig judgeConfig; // 判定区间配置,需在 Inspector 赋值
private Beatmap beatmap;
private float startTime; // 存储歌曲开始时间
private bool isSpawning = false;
@@ -106,7 +108,7 @@ public class NoteSpawner : MonoBehaviour
if (noteScript != null)
{
float noteSpawnTime = Time.time;
noteScript.Setup(key, noteData.trackIndex, CalculateSpeed(), noteData.time, noteData.color);
noteScript.Setup(key, noteData.trackIndex, CalculateSpeed(), noteData.time, noteData.color, judgeConfig);
Debug.Log($"到达时间:{noteSpawnTime + (60f / bpm) * 4}");
}
else
@@ -142,6 +144,7 @@ public class NoteSpawner : MonoBehaviour
float segmentInterval = (60f / bpm) / 4f;
int segmentCount = Mathf.CeilToInt(noteData.length / segmentInterval);
Transform spawnPoint = spawnPoints[noteData.trackIndex];
// 修正:scheduledEndTime 应为游戏时间轴上的绝对时间(noteData.time + noteData.length
float scheduledEndTime = noteData.time + noteData.length;
// 生成唯一 id
@@ -160,7 +163,7 @@ public class NoteSpawner : MonoBehaviour
HoldNote holdNote = startObj.GetComponent<HoldNote>();
if (holdNote != null)
{
holdNote.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), noteData.time, 0f, false, scheduledEndTime, noteData.color, key, "start");
holdNote.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), noteData.time, 0f, false, scheduledEndTime, noteData.color, key, "start", judgeConfig);
}
else
{
@@ -188,7 +191,7 @@ public class NoteSpawner : MonoBehaviour
HoldNote holdSeg = segObj.GetComponent<HoldNote>();
if (holdSeg != null)
{
holdSeg.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), noteData.time, segmentDelay, isEndSegment, scheduledEndTime, noteData.color, key, partType);
holdSeg.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), noteData.time, segmentDelay, isEndSegment, scheduledEndTime, noteData.color, key, partType, judgeConfig);
}
else
{