gameplay所有基本功能都装了,加入了全局警告。加入飘字等各种东西。

This commit is contained in:
2026-02-21 00:20:03 +08:00
parent 9f7c1c57b7
commit 14fb281d6f
254 changed files with 102927 additions and 9427 deletions
+66 -35
View File
@@ -1,4 +1,4 @@
using System;
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
@@ -11,6 +11,7 @@ public class NoteSpawner : MonoBehaviour
public NotePool notePool; // Documentation text normalized.
public Transform[] spawnPoints; // Documentation text normalized.
public Transform judgmentLine;
public GameObject[] notePrefabs; // Documentation text normalized.
public TextMeshProUGUI globalGameTime;
@@ -75,6 +76,7 @@ public class NoteSpawner : MonoBehaviour
private Dictionary<int, int> noteIndexToHoldId = new Dictionary<int, int>();
private const string NoteSpeedPrefKey = "noteSpeedMultiplier";
private const float BaseTravelDistance = 10.75f;
private Coroutine spawnCoroutine;
private Coroutine settlementCoroutine;
@@ -211,10 +213,13 @@ public class NoteSpawner : MonoBehaviour
// Cache parameters that don't change within the loop
float sm = Mathf.Clamp(speedMultiplier, SpeedMultiplierMin, SpeedMultiplierMax);
float baseTravelTime = (60f / bpm) * 4f;
float effectiveTravelTime = baseTravelTime / Mathf.Max(0.0001f, sm);
float noteSpeed = CalculateSpeed(effectiveTravelTime);
float baseNoteTravelTime = baseTravelTime / Mathf.Max(0.0001f, sm);
float noteSpeed = CalculateSpeed(baseNoteTravelTime);
float segmentInterval = (60f / bpm) / 4f;
EnsureJudgmentLine();
float[] laneTravelTimes = BuildLaneTravelTimes(noteSpeed);
// iterate with index so we can map hold notes to generated ids
for (int i = 0; i < beatmap.notes.Length; i++)
{
@@ -223,8 +228,9 @@ public class NoteSpawner : MonoBehaviour
yield break;
NoteData note = beatmap.notes[i];
float spawnTime = note.time - effectiveTravelTime;
float travelTime = GetLaneTravelTimeSeconds(laneTravelTimes, note.trackIndex);
float spawnTime = note.time - travelTime;
float delay = spawnTime - (Time.time - startTime) + spawnOffset;
if (delay > 0)
@@ -241,12 +247,12 @@ public class NoteSpawner : MonoBehaviour
if (note.type == "hold")
{
// create the hold note once and record its id mapping
int hid = SpawnHoldNote(note, sm, effectiveTravelTime, noteSpeed, segmentInterval);
int hid = SpawnHoldNote(note, sm, travelTime, noteSpeed, segmentInterval);
noteIndexToHoldId[i] = hid;
}
else
{
SpawnNote(note, sm, effectiveTravelTime, noteSpeed);
SpawnNote(note, sm, travelTime, noteSpeed);
}
}
@@ -268,7 +274,7 @@ public class NoteSpawner : MonoBehaviour
return key;
}
public void SpawnNote(NoteData noteData, float sm, float effectiveTravelTime, float noteSpeed)
public void SpawnNote(NoteData noteData, float sm, float travelTime, float noteSpeed)
{
if (noteData.trackIndex < 0 || noteData.trackIndex >= spawnPoints.Length)
{
@@ -295,20 +301,8 @@ public class NoteSpawner : MonoBehaviour
// Calculate hit time (realtime when note should be judged)
float rawHit = startTime + noteData.time + globalHitDelay;
float realtimeHit = Mathf.Max(0f, rawHit);
// Calculate activation time (when note should start moving)
float activationTime = realtimeHit - effectiveTravelTime;
// Compute early compensation for short notes if enabled
float noteYOffset = 0f;
if (enableYOffsetCompensation)
{
float timeSinceNoteActivation = Time.time - activationTime;
noteYOffset = Mathf.Max(0f, timeSinceNoteActivation * noteSpeed);
}
// Set initial position with offset compensation
Vector3 initialPosition = spawnPoint.position + Vector3.down * noteYOffset;
Vector3 initialPosition = spawnPoint.position;
note.transform.position = initialPosition;
note.transform.rotation = Quaternion.identity;
@@ -323,11 +317,11 @@ public class NoteSpawner : MonoBehaviour
// Configure controller for absolute positioning (replaces relative Translate)
if (noteController != null)
{
noteController.ConfigureAbsolutePositioning(initialPosition, realtimeHit, effectiveTravelTime, noteYOffset);
noteController.ConfigureAbsolutePositioning(initialPosition, realtimeHit, travelTime, 0f);
}
// Schedule calibration for short note to correct any drift
StartCoroutine(CalibrateNoteAfterSpawn(noteController, initialPosition));
StartCoroutine(CalibrateNoteAfterSpawn(noteController));
}
else
{
@@ -336,7 +330,7 @@ public class NoteSpawner : MonoBehaviour
}
// Modified: return generated holdNoteId so callers can map notes to ids
public int SpawnHoldNote(NoteData noteData, float sm, float effectiveTravelTime, float noteSpeed, float segmentInterval)
public int SpawnHoldNote(NoteData noteData, float sm, float travelTime, float noteSpeed, float segmentInterval)
{
if (noteData.trackIndex < 0 || noteData.trackIndex >= spawnPoints.Length)
{
@@ -388,7 +382,7 @@ public class NoteSpawner : MonoBehaviour
if (holdNote != null)
{
// pass realtime hit time (startTime + note.time) and delay 0, include globalHitDelay
holdNote.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, 0f, false, scheduledEndTime, noteData.color, key, "start", judgeConfig, noteData);
holdNote.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, 0f, false, scheduledEndTime, noteData.color, key, "start", travelTime, judgeConfig, noteData);
// compute visual scale for hold segments so pieces visually connect across speedMultiplier changes
float visualScale = 0.9f * sm + 0.1f; // linear fit: f(1)=1, f(2)=1.9
@@ -420,7 +414,7 @@ public class NoteSpawner : MonoBehaviour
if (holdSeg != null)
{
// Documentation text normalized.
holdSeg.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, segmentDelay, false, scheduledEndTime, noteData.color, key, "middle", judgeConfig, noteData);
holdSeg.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, segmentDelay, false, scheduledEndTime, noteData.color, key, "middle", travelTime, judgeConfig, noteData);
// apply same visual scale so middle pieces visually connect
float visualScaleMid = 0.9f * sm + 0.1f;
@@ -452,7 +446,7 @@ public class NoteSpawner : MonoBehaviour
HoldNote holdEnd = endObj.GetComponent<HoldNote>();
if (holdEnd != null)
{
holdEnd.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, endDelay, true, scheduledEndTime, noteData.color, key, "end", judgeConfig, noteData);
holdEnd.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, endDelay, true, scheduledEndTime, noteData.color, key, "end", travelTime, judgeConfig, noteData);
// apply visual scale to end piece as well
float visualScaleEnd = 0.9f * sm + 0.1f;
@@ -472,6 +466,45 @@ public class NoteSpawner : MonoBehaviour
return holdNoteId;
}
private void EnsureJudgmentLine()
{
if (judgmentLine != null) return;
GameObject go = GameObject.FindGameObjectWithTag("JudgmentLine");
if (go != null) judgmentLine = go.transform;
}
private float[] BuildLaneTravelTimes(float noteSpeed)
{
int lanes = spawnPoints != null ? spawnPoints.Length : 0;
float[] times = new float[lanes];
float safeSpeed = Mathf.Max(0.0001f, noteSpeed);
for (int i = 0; i < lanes; i++)
{
float distance = GetLaneDistance(i);
times[i] = distance / safeSpeed;
}
return times;
}
private float GetLaneDistance(int trackIndex)
{
if (spawnPoints == null || trackIndex < 0 || trackIndex >= spawnPoints.Length) return BaseTravelDistance;
Transform sp = spawnPoints[trackIndex];
if (sp == null) return BaseTravelDistance;
if (judgmentLine == null) return BaseTravelDistance;
return Mathf.Abs(sp.position.y - judgmentLine.position.y);
}
private float GetLaneTravelTimeSeconds(float[] laneTravelTimes, int trackIndex)
{
if (laneTravelTimes == null || laneTravelTimes.Length == 0) return 0f;
if (trackIndex < 0 || trackIndex >= laneTravelTimes.Length) return laneTravelTimes[0];
return laneTravelTimes[trackIndex];
}
/// <summary>
/// Documentation text normalized.
[ContextMenu("Force Immediate Settlement")]
@@ -578,7 +611,7 @@ public class NoteSpawner : MonoBehaviour
/// <summary>
/// Documentation text normalized.
private IEnumerator CalibrateNoteAfterSpawn(NoteController noteController, Vector3 expectedSpawnPos)
private IEnumerator CalibrateNoteAfterSpawn(NoteController noteController)
{
if (noteController == null) yield break;
@@ -588,13 +621,11 @@ public class NoteSpawner : MonoBehaviour
yield return GetCalibrateWait();
if (noteObj == null || !noteObj.activeSelf) yield break;
// Check if note position deviates significantly from expected spawn position
float distanceDeviation = Vector3.Distance(noteObj.transform.position, expectedSpawnPos);
Vector3 expected = noteController.GetExpectedPosition(Time.time);
float distanceDeviation = Vector3.Distance(noteObj.transform.position, expected);
if (distanceDeviation > calibrateTolerance)
{
// Snap to expected position while allowing vertical movement
Vector3 correctedPos = expectedSpawnPos;
noteObj.transform.position = correctedPos;
noteObj.transform.position = expected;
if (JudgeManager.IsDebugEnabled) Debug.Log($"[NoteSpawner] Calibrated short note position, deviation was {distanceDeviation:F4}");
}
}
@@ -612,7 +643,7 @@ public class NoteSpawner : MonoBehaviour
private float CalculateSpeed(float noteTravelTime)
{
return 10.75f / noteTravelTime;
return BaseTravelDistance / noteTravelTime;
}
/// <summary>