编队系统大更新 基本快搞好了 准备做敌人和分数
This commit is contained in:
@@ -21,22 +21,39 @@ public class GameManager : MonoBehaviour
|
||||
[Header("Debug / Test")]
|
||||
public Text banflagTextUI;
|
||||
|
||||
private bool pauseSubscribed = false;
|
||||
|
||||
private void SubscribeToPauseManager()
|
||||
{
|
||||
if (pauseSubscribed) return;
|
||||
// prefer the singleton, but try to find in scene if null
|
||||
var pm = PauseManager.Instance ?? FindObjectOfType<PauseManager>();
|
||||
if (pm != null)
|
||||
{
|
||||
pm.OnPauseStateChanged += HandlePauseStateChanged;
|
||||
pauseSubscribed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribeFromPauseManager()
|
||||
{
|
||||
if (!pauseSubscribed) return;
|
||||
var pm = PauseManager.Instance ?? FindObjectOfType<PauseManager>();
|
||||
if (pm != null)
|
||||
{
|
||||
pm.OnPauseStateChanged -= HandlePauseStateChanged;
|
||||
}
|
||||
pauseSubscribed = false;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// 订阅暂停管理器的事件
|
||||
if (PauseManager.Instance != null)
|
||||
{
|
||||
PauseManager.Instance.OnPauseStateChanged += HandlePauseStateChanged;
|
||||
}
|
||||
SubscribeToPauseManager();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
// 取消订阅暂停管理器的事件
|
||||
if (PauseManager.Instance != null)
|
||||
{
|
||||
PauseManager.Instance.OnPauseStateChanged -= HandlePauseStateChanged;
|
||||
}
|
||||
UnsubscribeFromPauseManager();
|
||||
}
|
||||
|
||||
private void HandlePauseStateChanged(bool isPaused)
|
||||
@@ -282,7 +299,8 @@ public class GameManager : MonoBehaviour
|
||||
}
|
||||
|
||||
// Pause the system until Space pressed
|
||||
// Use PauseManager instead of directly setting Time.timeScale
|
||||
// Ensure we are subscribed before invoking pause so we track audio state
|
||||
SubscribeToPauseManager();
|
||||
PauseManager.Instance?.Pause(true);
|
||||
UpdateStatusOnConsole("Paused: press Space to start playback");
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public enum NoteSegment { None, Start, Middle, End }
|
||||
@@ -40,6 +41,8 @@ public class HoldNote : BaseNote
|
||||
[Header("判定区间配置")]
|
||||
public NoteJudgeConfig judgeConfig; // 判定区间配置,需在预制体或生成时赋值
|
||||
|
||||
private bool _hasTriggeredOnThisHold = false; // ensure single trigger per hold note
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
controller = GetComponent<HoldNoteController>();
|
||||
@@ -92,6 +95,8 @@ public class HoldNote : BaseNote
|
||||
{
|
||||
this.id = id;
|
||||
this.trackIndex = trackIndex;
|
||||
// also set BaseNote.TrackIndex so other systems using TrackIndex property work consistently
|
||||
this.TrackIndex = trackIndex;
|
||||
this.speed = speed;
|
||||
this.startTime = time;
|
||||
this.delay = delay;
|
||||
@@ -346,6 +351,28 @@ public class HoldNote : BaseNote
|
||||
JudgeManager.Instance.RegisterStartJudged(noteID, true);
|
||||
isHoldActive = true; // 成功判定Start,长按状态激活
|
||||
PlayHitAnimation();
|
||||
|
||||
// Trigger skills configured to fire on note hits for this slot when the Start is successfully hit.
|
||||
if (!_hasTriggeredOnThisHold)
|
||||
{
|
||||
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) ?? 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) ?? 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
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -353,8 +380,26 @@ public class HoldNote : BaseNote
|
||||
Debug.Log($"[HoldNote] START Miss(偏差={offset:F2}秒): {noteColor}");
|
||||
JudgeManager.Instance.RegisterStartJudged(noteID, false); // 明确标记未判定
|
||||
isHoldActive = false; // Start Miss,长按状态不激活
|
||||
|
||||
// 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) ?? false;
|
||||
Debug.Log($"[HoldNote] START Miss NotifyNoteHit fired for slot {trackIndex}: {triggered}");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
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);
|
||||
|
||||
StartCoroutine(DelayedReturn());
|
||||
}
|
||||
|
||||
@@ -442,6 +487,88 @@ public class HoldNote : BaseNote
|
||||
}
|
||||
InputManager.Instance?.ShowJudgeResult(trackIndex, result);
|
||||
teamUIController.Instance?.OnJudgeResult(result); // 新增:更新combo计数
|
||||
// Notify SkillBuilder so Hold notes can trigger skills configured for Hold
|
||||
if (!_hasTriggeredOnThisHold)
|
||||
{
|
||||
var sb = SkillBuilder.Instance;
|
||||
if (sb == null)
|
||||
{
|
||||
Debug.LogWarning($"[HoldNote] SkillBuilder.Instance is null when trying to notify for track {TrackIndex}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var so = sb.GetAllyHeroSOBySlot(TrackIndex);
|
||||
var def = so?.GetPrimarySkill();
|
||||
Debug.Log($"[HoldNote] About to NotifyNoteHit: TrackIndex={TrackIndex} trackIndexField={trackIndex} so={(so!=null?so.name:"null")} skill={(def!=null?def.skillId:"null")} trigger={(def!=null?def.triggerCondition.ToString():"-")}");
|
||||
}
|
||||
|
||||
bool triggeredHold = false;
|
||||
bool triggeredTap = false;
|
||||
|
||||
// Build candidate slot indices to try: prefer instance field, then BaseNote.TrackIndex, then try to match UI slots
|
||||
var candidates = new List<int>();
|
||||
candidates.Add(this.trackIndex);
|
||||
if (!candidates.Contains(this.TrackIndex)) candidates.Add(this.TrackIndex);
|
||||
// try to resolve by matching this object to UI slots
|
||||
var ui = teamUIController.Instance;
|
||||
if (ui != null)
|
||||
{
|
||||
for (int i = 0; i < ui.allySlotIds.Count; i++)
|
||||
{
|
||||
var slotObj = ui.GetAllyObjectBySlot(i);
|
||||
if (slotObj == null) continue;
|
||||
if (slotObj == this.gameObject || this.gameObject.transform.IsChildOf(slotObj.transform))
|
||||
{
|
||||
if (!candidates.Contains(i)) candidates.Add(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"[HoldNote] NotifyNoteHit candidate slots: {string.Join(",", candidates)}");
|
||||
|
||||
// Try each candidate: prefer Hold notification. Stop on first successful trigger.
|
||||
foreach (var slot in candidates)
|
||||
{
|
||||
try
|
||||
{
|
||||
triggeredHold = SkillBuilder.Instance?.NotifyNoteHit(slot, result, SkillDefinition.NoteTypeTrigger.Hold) ?? false;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"[HoldNote] NotifyNoteHit(Hold) threw for slot {slot}: {ex}");
|
||||
}
|
||||
if (triggeredHold)
|
||||
{
|
||||
Debug.Log($"[HoldNote] NotifyNoteHit succeeded (Hold) for slot {slot}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no candidate triggered via Hold, try Tap fallback on same candidate list
|
||||
if (!triggeredHold)
|
||||
{
|
||||
foreach (var slot in candidates)
|
||||
{
|
||||
try
|
||||
{
|
||||
triggeredTap = SkillBuilder.Instance?.NotifyNoteHit(slot, result, SkillDefinition.NoteTypeTrigger.Tap) ?? false;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"[HoldNote] NotifyNoteHit(Tap) threw for slot {slot}: {ex}");
|
||||
}
|
||||
if (triggeredTap)
|
||||
{
|
||||
Debug.Log($"[HoldNote] NotifyNoteHit succeeded (Tap fallback) for slot {slot}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_hasTriggeredOnThisHold = true;
|
||||
Debug.Log($"[HoldNote] Hold end NotifyNoteHit called: TrackIndex={TrackIndex} trackIndexField={trackIndex} result={result} triggeredHold={triggeredHold} triggeredTap={triggeredTap}");
|
||||
}
|
||||
Debug.Log($"[HoldNote] END判定结果: {noteColor} {result} (release={releaseTime:F2}, target={scheduledEndTime:F2})");
|
||||
ReturnToPool();
|
||||
}
|
||||
@@ -507,6 +634,7 @@ public class HoldNote : BaseNote
|
||||
isJudged = false;
|
||||
isHoldActive = false; // 重置长按标记
|
||||
hasBeenHeldFromStart = false; // 重置
|
||||
_hasTriggeredOnThisHold = false; // 重置
|
||||
|
||||
if (autoReturnCoroutine != null)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Centralized judge sound player. Assign four AudioSources (one per judgement) in the Inspector.
|
||||
/// Designed to be low-latency: each judgement has a dedicated AudioSource and PlayOneShot is used.
|
||||
/// Place this component on a persistent gameobject in the scene (or your audio container).
|
||||
/// </summary>
|
||||
public class JudgeSoundManager : MonoBehaviour
|
||||
{
|
||||
public static JudgeSoundManager Instance { get; private set; }
|
||||
|
||||
[Header("AudioSources (assign dedicated AudioSource for each judgement)")]
|
||||
public AudioSource perfectSource;
|
||||
public AudioSource greatSource;
|
||||
public AudioSource goodSource;
|
||||
public AudioSource missSource;
|
||||
|
||||
[Header("Enable/Disable per-judgement playback")]
|
||||
[Tooltip("Toggle whether Perfect sounds are played")]
|
||||
public bool enablePerfect = true;
|
||||
[Tooltip("Toggle whether Great sounds are played")]
|
||||
public bool enableGreat = true;
|
||||
[Tooltip("Toggle whether Good sounds are played")]
|
||||
public bool enableGood = true;
|
||||
[Tooltip("Toggle whether Miss sounds are played")]
|
||||
public bool enableMiss = true;
|
||||
|
||||
[Header("Fallback clips (optional, used if the assigned AudioSource has no clip)")]
|
||||
public AudioClip perfectClip;
|
||||
public AudioClip greatClip;
|
||||
public AudioClip goodClip;
|
||||
public AudioClip missClip;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
// Do not automatically destroy; leave lifecycle to scene management
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure AudioSources are configured for low latency
|
||||
ConfigureSource(perfectSource);
|
||||
ConfigureSource(greatSource);
|
||||
ConfigureSource(goodSource);
|
||||
ConfigureSource(missSource);
|
||||
}
|
||||
|
||||
private void ConfigureSource(AudioSource src)
|
||||
{
|
||||
if (src == null) return;
|
||||
src.playOnAwake = false;
|
||||
// keep 3D off for UI/flat sounds
|
||||
src.spatialBlend = 0f;
|
||||
// ensure not looping
|
||||
src.loop = false;
|
||||
}
|
||||
|
||||
public enum JudgeResult { Perfect, Great, Good, Miss }
|
||||
|
||||
public void PlayJudgeSound(string result)
|
||||
{
|
||||
if (string.IsNullOrEmpty(result)) return;
|
||||
switch (result)
|
||||
{
|
||||
case "Perfect": PlayJudgeSound(JudgeResult.Perfect); break;
|
||||
case "Great": PlayJudgeSound(JudgeResult.Great); break;
|
||||
case "Good": PlayJudgeSound(JudgeResult.Good); break;
|
||||
case "Miss": PlayJudgeSound(JudgeResult.Miss); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayJudgeSound(JudgeResult r)
|
||||
{
|
||||
// honor inspector toggles to allow disabling specific judgement sounds
|
||||
switch (r)
|
||||
{
|
||||
case JudgeResult.Perfect:
|
||||
if (!enablePerfect) return;
|
||||
PlayOnSource(perfectSource, perfectClip);
|
||||
break;
|
||||
case JudgeResult.Great:
|
||||
if (!enableGreat) return;
|
||||
PlayOnSource(greatSource, greatClip);
|
||||
break;
|
||||
case JudgeResult.Good:
|
||||
if (!enableGood) return;
|
||||
PlayOnSource(goodSource, goodClip);
|
||||
break;
|
||||
case JudgeResult.Miss:
|
||||
if (!enableMiss) return;
|
||||
PlayOnSource(missSource, missClip);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayOnSource(AudioSource src, AudioClip fallbackClip)
|
||||
{
|
||||
if (src != null)
|
||||
{
|
||||
// Prefer PlayOneShot so sources can overlap and we don't need to manage clip state
|
||||
if (src.clip != null)
|
||||
{
|
||||
src.PlayOneShot(src.clip);
|
||||
}
|
||||
else if (fallbackClip != null)
|
||||
{
|
||||
src.PlayOneShot(fallbackClip);
|
||||
}
|
||||
}
|
||||
else if (fallbackClip != null)
|
||||
{
|
||||
// no dedicated source provided: play via a temporary one-shot using AudioSource.PlayClipAtPoint at camera position
|
||||
AudioSource.PlayClipAtPoint(fallbackClip, Camera.main != null ? Camera.main.transform.position : Vector3.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7692a74982af114ca6d79307fc43676
|
||||
@@ -76,22 +76,18 @@ public class Note : BaseNote
|
||||
{
|
||||
Debug.Log($"{keyToPress}: Miss");
|
||||
judgeResult = "Miss";
|
||||
InputManager.Instance?.ShowJudgeResult(TrackIndex, judgeResult);
|
||||
JudgeMiss();
|
||||
return;
|
||||
// Do not return here - let shared post-judge logic run so NotifyNoteHit is invoked for Miss as well
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"{keyToPress}: Miss");
|
||||
judgeResult = "Miss";
|
||||
InputManager.Instance?.ShowJudgeResult(TrackIndex, judgeResult);
|
||||
JudgeMiss();
|
||||
return;
|
||||
// Do not return here - let shared post-judge logic run so NotifyNoteHit is invoked for Miss as well
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 兼容旧逻辑
|
||||
// fallback timing
|
||||
if (timeDifference <= 0.08f)
|
||||
{
|
||||
Debug.Log($"{keyToPress}: Perfect");
|
||||
@@ -106,17 +102,18 @@ public class Note : BaseNote
|
||||
{
|
||||
Debug.Log($"{keyToPress}: Miss");
|
||||
judgeResult = "Miss";
|
||||
InputManager.Instance?.ShowJudgeResult(TrackIndex, judgeResult);
|
||||
JudgeMiss();
|
||||
return;
|
||||
// Do not return here - let shared post-judge logic run so NotifyNoteHit is invoked for Miss as well
|
||||
}
|
||||
}
|
||||
|
||||
// 显示判定文本
|
||||
// 显示判定信息
|
||||
if (!string.IsNullOrEmpty(judgeResult))
|
||||
{
|
||||
InputManager.Instance?.ShowJudgeResult(TrackIndex, judgeResult);
|
||||
teamUIController.Instance?.OnJudgeResult(judgeResult); // 新增:更新combo计数
|
||||
JudgeSoundManager.Instance?.PlayJudgeSound(judgeResult);
|
||||
teamUIController.Instance?.OnJudgeResult(judgeResult); // 更新combo计数
|
||||
// Notify SkillBuilder about this note hit so OnNoteHit skills may trigger (tap notes)
|
||||
try { SkillBuilder.Instance?.NotifyNoteHit(TrackIndex, judgeResult, SkillDefinition.NoteTypeTrigger.Tap); } catch { }
|
||||
}
|
||||
Judge();
|
||||
}
|
||||
@@ -152,7 +149,10 @@ public class Note : BaseNote
|
||||
Debug.Log($"{keyToPress} Miss");
|
||||
// Ensure UI shows Miss and combo is updated when a note auto-misses
|
||||
InputManager.Instance?.ShowJudgeResult(TrackIndex, "Miss");
|
||||
JudgeSoundManager.Instance?.PlayJudgeSound("Miss");
|
||||
teamUIController.Instance?.OnJudgeResult("Miss");
|
||||
// Notify SkillBuilder about Miss so OnNoteHit skills configured for Miss can trigger
|
||||
try { SkillBuilder.Instance?.NotifyNoteHit(TrackIndex, "Miss", SkillDefinition.NoteTypeTrigger.Tap); } catch { }
|
||||
// InputManager.Instance?.ShowJudgeResult(TrackIndex, "Miss"); // 已在HandlePress中调用 for manual presses
|
||||
ReturnToPool();
|
||||
}
|
||||
@@ -188,6 +188,7 @@ public class Note : BaseNote
|
||||
{
|
||||
// Show Miss and update combo
|
||||
InputManager.Instance?.ShowJudgeResult(TrackIndex, "Miss");
|
||||
JudgeSoundManager.Instance?.PlayJudgeSound("Miss");
|
||||
teamUIController.Instance?.OnJudgeResult("Miss");
|
||||
JudgeMiss();
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ using UnityEngine;
|
||||
public class NoteJudgeConfig : ScriptableObject
|
||||
{
|
||||
[Header("判定区间(单位:秒)")]
|
||||
[Tooltip("Perfect 判定最大时间偏差")]
|
||||
[Tooltip("Perfect")]
|
||||
public float perfectRange = 0.05f;
|
||||
[Tooltip("Great 判定最大时间偏差")]
|
||||
[Tooltip("Great")]
|
||||
public float greatRange = 0.1f;
|
||||
[Tooltip("Good 判定最大时间偏差")]
|
||||
[Tooltip("Good")]
|
||||
public float goodRange = 0.2f;
|
||||
[Tooltip("Miss 判定最大时间偏差")]
|
||||
[Tooltip("Miss")]
|
||||
public float missRange = 0.3f;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ public class NoteSpawner : MonoBehaviour
|
||||
public float spawnOffset = 0f; // 生成音符时的时间偏移量
|
||||
public float bpm;
|
||||
|
||||
[Header("Global timing adjustments")]
|
||||
[Tooltip("Global additional realtime offset (seconds) added to hit times for all notes. Use to test input latency or adjust judgement timing. Default 0. Can be negative to make notes arrive earlier.")]
|
||||
public float globalHitDelay = 0f;
|
||||
|
||||
public NoteJudgeConfig judgeConfig; // 判定区间配置,需在 Inspector 赋值
|
||||
|
||||
private Beatmap beatmap;
|
||||
@@ -108,8 +112,9 @@ public class NoteSpawner : MonoBehaviour
|
||||
if (noteScript != null)
|
||||
{
|
||||
float noteSpawnTime = Time.time;
|
||||
// pass realtime hit time to Note.Setup
|
||||
float realtimeHit = startTime + noteData.time;
|
||||
// pass realtime hit time to Note.Setup, include globalHitDelay
|
||||
float rawHit = startTime + noteData.time + globalHitDelay;
|
||||
float realtimeHit = Mathf.Max(0f, rawHit); // clamp to non-negative
|
||||
noteScript.Setup(key, noteData.trackIndex, CalculateSpeed(), realtimeHit, noteData.color, judgeConfig);
|
||||
if (GameConfig.verboseLogs) Debug.Log($"到达时间:{noteSpawnTime + (60f / bpm) * 4}");
|
||||
}
|
||||
@@ -152,7 +157,8 @@ public class NoteSpawner : MonoBehaviour
|
||||
|
||||
Transform spawnPoint = spawnPoints[noteData.trackIndex];
|
||||
// scheduledEndTime 使用谱面时间(note.time + length),但转换为实时
|
||||
float scheduledEndTime = startTime + (noteData.time + noteData.length);
|
||||
float rawScheduledEnd = startTime + (noteData.time + noteData.length) + globalHitDelay;
|
||||
float scheduledEndTime = Mathf.Max(0f, rawScheduledEnd); // clamp to non-negative
|
||||
|
||||
// 生成唯一 id(改为自增计数器,避免随机冲突)
|
||||
int holdNoteId = ++holdNoteIdCounter;
|
||||
@@ -170,8 +176,10 @@ public class NoteSpawner : MonoBehaviour
|
||||
HoldNote holdNote = startObj.GetComponent<HoldNote>();
|
||||
if (holdNote != null)
|
||||
{
|
||||
// pass realtime hit time (startTime + note.time) and delay 0
|
||||
holdNote.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), startTime + noteData.time, 0f, false, scheduledEndTime, noteData.color, key, "start", judgeConfig);
|
||||
// pass realtime hit time (startTime + note.time) and delay 0, include globalHitDelay
|
||||
float rawStartHit = startTime + noteData.time + globalHitDelay;
|
||||
float startHit = Mathf.Max(0f, rawStartHit);
|
||||
holdNote.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), startHit, 0f, false, scheduledEndTime, noteData.color, key, "start", judgeConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -198,7 +206,9 @@ public class NoteSpawner : MonoBehaviour
|
||||
if (holdSeg != null)
|
||||
{
|
||||
// 中段都标记为 middle,pass realtime base time and segmentDelay
|
||||
holdSeg.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), startTime + noteData.time, segmentDelay, false, scheduledEndTime, noteData.color, key, "middle", judgeConfig);
|
||||
float rawBase = startTime + noteData.time + globalHitDelay;
|
||||
float baseHit = Mathf.Max(0f, rawBase);
|
||||
holdSeg.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), baseHit, segmentDelay, false, scheduledEndTime, noteData.color, key, "middle", judgeConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -226,7 +236,9 @@ public class NoteSpawner : MonoBehaviour
|
||||
{
|
||||
// 将 delay 设置为整个 hold 长度 (或 segmentCount * actualSegmentInterval),确保尾部在最后中段之后
|
||||
float endDelay = segmentCount * actualSegmentInterval; // 通常等于 noteData.length
|
||||
holdEnd.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), startTime + noteData.time, endDelay, true, scheduledEndTime, noteData.color, key, "end", judgeConfig);
|
||||
float rawBase = startTime + noteData.time + globalHitDelay;
|
||||
float baseHit = Mathf.Max(0f, rawBase);
|
||||
holdEnd.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), baseHit, endDelay, true, scheduledEndTime, noteData.color, key, "end", judgeConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user