玉盘珍馐值万钱 缝缝补补又三天 长音已改逾卅遍
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user