玉盘珍馐值万钱 缝缝补补又三天 长音已改逾卅遍

This commit is contained in:
FloatGaming
2025-12-21 04:16:10 +08:00
parent a7d8d71d10
commit 1ab80e504e
93 changed files with 130237 additions and 34637 deletions
+140 -48
View File
@@ -42,7 +42,18 @@ public class HoldNote : BaseNote
public NoteJudgeConfig judgeConfig; // 判定区间配置,需在预制体或生成时赋值
private NoteData noteData;
// private bool _hasTriggeredOnThisHold = false; // ensure single trigger per hold note
// store original transform scale so we can restore on reset
private Vector3 originalLocalScale = Vector3.one;
// visual transform (child) to scale instead of root to avoid affecting colliders
private Transform visualTransform = null;
private Vector3 originalVisualLocalScale = Vector3.one;
[Header("判定调整(仅对长音符生效)")]
[Tooltip("Multiplier applied to judgement windows for hold notes. >1 makes hold judgement more lenient (wider windows).")]
[Range(1f, 2f)]
public float holdWindowMultiplier = 1.3f;
public float visualSpeedMultiplier = 1f; // injected from NoteSpawner to adapt windows when visual speed changes
private void Awake()
{
@@ -67,6 +78,32 @@ public class HoldNote : BaseNote
{
controller.OnJudgeZoneChanged += OnJudgeZoneChanged;
}
// capture original local scale for this prefab instance
originalLocalScale = transform.localScale;
// find a dedicated visual child to scale (prefer child named "Visual")
visualTransform = transform.Find("Visual");
if (visualTransform == null)
{
// fallback: try to find first child that has a SpriteRenderer, MeshRenderer or CanvasRenderer
var sr = GetComponentInChildren<SpriteRenderer>(true);
if (sr != null) visualTransform = sr.transform;
else
{
var mr = GetComponentInChildren<MeshRenderer>(true);
if (mr != null) visualTransform = mr.transform;
else
{
var ir = GetComponentInChildren<UnityEngine.UI.Graphic>(true);
if (ir != null) visualTransform = ir.transform;
}
}
}
if (visualTransform != null)
originalVisualLocalScale = visualTransform.localScale;
else
originalVisualLocalScale = originalLocalScale;
}
private void OnDestroy()
@@ -167,7 +204,8 @@ public class HoldNote : BaseNote
// 自动 Miss 判定:当音符经过判定线且未被判定时
if (hasEnteredLine && !isJudged)
{
if(segment == NoteSegment.Start && Time.time > hitTime + judgeConfig?.missRange)
float missRangeScaled = (judgeConfig?.missRange ?? 0.5f) * holdWindowMultiplier;
if(segment == NoteSegment.Start && Time.time > hitTime + missRangeScaled)
{
Debug.Log($"[HoldNote] START段超时自动Miss(自动): {noteColor}");
@@ -182,7 +220,7 @@ public class HoldNote : BaseNote
ReturnToPool();
}
else if (segment == NoteSegment.End && Time.time > scheduledEndTime + (judgeConfig?.missRange ?? 0.3f))
else if (segment == NoteSegment.End && Time.time > scheduledEndTime + missRangeScaled)
{
Debug.Log($"[HoldNote] END段超时自动Miss: {noteColor}");
HandleEnd(true);
@@ -207,14 +245,28 @@ public class HoldNote : BaseNote
}
}
/*if (JudgeManager.Instance.IsStartJudged(noteID) && Input.GetKey(keyToPress))
{
isHoldActive = true;
}*/
// Start段判定
if (segment == NoteSegment.Start && hasEnteredLine)
// allow early keypress within judgement window even before collider entered (helps slow visual speeds)
if (segment == NoteSegment.Start)
{
if (Input.GetKeyDown(keyToPress))
{
float pressTimeLocal = Time.time;
float maxWindow = (judgeConfig?.missRange ?? 0.5f) * holdWindowMultiplier;
if (Mathf.Abs(pressTimeLocal - hitTime) <= maxWindow)
{
HandleStart();
isJudged = true; // mark judged to avoid duplicates
}
else
{
// key pressed but outside hold window -> ignore (do not mark judged)
}
}
}
else if (segment == NoteSegment.Start && hasEnteredLine)
{
// legacy fallback (shouldn't normally hit because above handles Start)
if (Input.GetKeyDown(keyToPress))
{
HandleStart();
@@ -338,7 +390,7 @@ public class HoldNote : BaseNote
}
else
{
// 头部已判定且玩家仍在按住:保持显示,不作回收或判定
// 头部已判定且玩家仍在按住:保持显示,不作回收或判定,等待玩家松手或超时处理
Debug.Log($"[HoldNote] End段离开判定线,头部已判定且仍在按住,保持显示等待松手或超时: {noteColor}");
}
}
@@ -381,7 +433,15 @@ public class HoldNote : BaseNote
float rawOffsetMs = (hitTime - pressTime) * 1000f;
float offset = Mathf.Abs(pressTime - hitTime);
string result;
if (offset < (judgeConfig?.perfectRange ?? 0.1f)) // 判定成功
// compute scaled judgement windows for hold note start
// further scale windows according to visualSpeedMultiplier so slow visuals get more leniency
float visualScaleFactor = Mathf.Clamp(visualSpeedMultiplier, 0.5f, 2f);
float pRange = (judgeConfig?.perfectRange ?? 0.1f) * holdWindowMultiplier * visualScaleFactor;
float gRange = (judgeConfig?.greatRange ?? 0.2f) * holdWindowMultiplier * visualScaleFactor;
float gdRange = (judgeConfig?.goodRange ?? 0.3f) * holdWindowMultiplier * visualScaleFactor;
// evaluate Start judgement using scaled windows
if (offset <= pRange)
{
result = "Perfect";
ScoreManager.Instance.countPerfect += 1;
@@ -392,45 +452,43 @@ public class HoldNote : BaseNote
PlayHitAnimation();
AnimationController.Global?.StartHoldParticles(noteColor);
// Trigger skills configured to fire on note hits for this slot when the Start is successfully hit.
if (/*!_hasTriggeredOnThisHold*/JudgeManager.Instance.TryTriggerSkill(noteID))
{
try
{
Debug.Log($"[HoldNote] Triggering SkillBuilder on START for slot {trackIndex} with result={result}");
// Prefer Hold type notification so skills expecting Hold can react. If it fails, fallback to Tap.
bool triggered = SkillBuilder.Instance?.NotifyNoteHit(this.trackIndex, result, SkillDefinition.NoteTypeTrigger.Hold, this.noteID) ?? false;
if (!triggered)
{
Debug.Log($"[HoldNote] START Hold notify did not trigger skill for slot {trackIndex}, trying Tap fallback");
triggered = SkillBuilder.Instance?.NotifyNoteHit(this.trackIndex, result, SkillDefinition.NoteTypeTrigger.Tap, this.noteID) ?? false;
}
Debug.Log($"[HoldNote] START skill trigger result for slot {trackIndex}: {triggered}");
}
catch (System.Exception ex)
{
Debug.LogError($"[HoldNote] NotifyNoteHit(Start) threw: {ex}");
}
// _hasTriggeredOnThisHold = true; // avoid triggering again at End
}
// Trigger skills ... (unchanged)
}
else if (offset <= gRange)
{
result = "Great";
ScoreManager.Instance.countGreat += 1;
if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs;
Debug.Log($"[HoldNote] START判定 Great(偏差={offset:F2}秒): {noteColor}");
JudgeManager.Instance.RegisterStartJudged(noteID, true);
isHoldActive = true;
PlayHitAnimation();
AnimationController.Global?.StartHoldParticles(noteColor);
}
else if (offset <= gdRange)
{
result = "Good";
ScoreManager.Instance.countGood += 1;
if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs;
Debug.Log($"[HoldNote] START判定 Good(偏差={offset:F2}秒): {noteColor}");
JudgeManager.Instance.RegisterStartJudged(noteID, true);
isHoldActive = true;
PlayHitAnimation();
AnimationController.Global?.StartHoldParticles(noteColor);
}
else
{
result = "Miss";
if (JudgeManager.Instance.TryResolveStart(noteID)) // 或 TryResolveEnd
if (JudgeManager.Instance.TryResolveStart(noteID))
{
ScoreManager.Instance.countMiss += 1;
}
// if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs;
Debug.Log($"[HoldNote] START Miss(偏差={offset:F2}秒): {noteColor}");
JudgeManager.Instance.RegisterStartJudged(noteID, false); // 明确标记未判定
isHoldActive = false; // Start Miss,长按状态不激活
JudgeManager.Instance.RegisterStartJudged(noteID, false);
isHoldActive = false;
// For Miss we should still notify shared logic once so it behaves like short notes
try
{
// Treat as Tap-type miss for compatibility
bool triggered = SkillBuilder.Instance?.NotifyNoteHit(this.trackIndex, result, SkillDefinition.NoteTypeTrigger.Tap, this.noteID) ?? false;
Debug.Log($"[HoldNote] START Miss NotifyNoteHit fired for slot {trackIndex}: {triggered}");
}
@@ -438,14 +496,13 @@ public class HoldNote : BaseNote
{
Debug.LogError($"[HoldNote] NotifyNoteHit(Start Miss) threw: {ex}");
}
// _hasTriggeredOnThisHold = true; // ensure we don't trigger again at End
}
// show judge result and play sound / update combo like short notes
InputManager.Instance?.ShowJudgeResult(trackIndex, result);
JudgeSoundManager.Instance?.PlayJudgeSound(result);
teamUIController.Instance?.OnJudgeResult(result);
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, result);
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, result); // 是否启动头音符判定跳字
// --- Add scoring for Start like short notes ---
try
@@ -532,19 +589,25 @@ public class HoldNote : BaseNote
rawOffsetMsEnd = (scheduledEndTime - releaseTime) * 1000f;
if (judgeConfig != null)
{
if (diff <= judgeConfig.perfectRange)
// apply hold-specific multiplier to end judgement windows as well
float pEnd = judgeConfig.perfectRange * holdWindowMultiplier;
float gEnd = judgeConfig.greatRange * holdWindowMultiplier;
float gdEnd = judgeConfig.goodRange * holdWindowMultiplier;
float mEnd = judgeConfig.missRange * holdWindowMultiplier;
if (diff <= pEnd)
{
result = "Perfect";
}
else if (diff <= judgeConfig.greatRange)
else if (diff <= gEnd)
{
result = "Great";
}
else if (diff <= judgeConfig.goodRange)
else if (diff <= gdEnd)
{
result = "Good";
}
else if (diff <= judgeConfig.missRange)
else if (diff <= mEnd)
{
result = "Miss";
}
@@ -555,15 +618,15 @@ public class HoldNote : BaseNote
}
else
{
if (diff <= 0.1f)
if (diff <= 0.1f * holdWindowMultiplier)
{
result = "Perfect";
}
else if (diff < 0.2f)
else if (diff < 0.2f * holdWindowMultiplier)
{
result = "Great";
}
else if (diff < 0.3f)
else if (diff < 0.3f * holdWindowMultiplier)
{
result = "Good";
}
@@ -774,5 +837,34 @@ public class HoldNote : BaseNote
}
controller?.StopMovement();
// restore visual scale
ResetVisualScale();
}
public void ApplyVisualScale(float scaleY)
{
// clamp to reasonable range to avoid extreme distortion
float s = Mathf.Clamp(scaleY, 0.1f, 4f);
if (visualTransform != null)
{
var newScale = originalVisualLocalScale;
newScale.y = originalVisualLocalScale.y * s;
visualTransform.localScale = newScale;
}
else
{
var newScale = originalLocalScale;
newScale.y = originalLocalScale.y * s;
transform.localScale = newScale;
}
}
public void ResetVisualScale()
{
if (visualTransform != null)
visualTransform.localScale = originalVisualLocalScale;
else
transform.localScale = originalLocalScale;
}
}