好多新内容
This commit is contained in:
@@ -43,6 +43,9 @@ public class TrackJudgeHitEffectController : MonoBehaviour
|
||||
|
||||
private readonly Queue<GameObject> particlePool = new Queue<GameObject>();
|
||||
private Transform particlePoolRoot;
|
||||
private readonly HashSet<GameObject> rentedParticles = new HashSet<GameObject>();
|
||||
private readonly Dictionary<GameObject, int> particleRentIds = new Dictionary<GameObject, int>();
|
||||
private int particleRentCounter;
|
||||
|
||||
// Per-instance cache: the particle systems and computed lifetime are resolved once
|
||||
// when an instance is created, so the burst hot path never calls
|
||||
@@ -72,6 +75,21 @@ public class TrackJudgeHitEffectController : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
ReturnAllRentedParticles();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
|
||||
ReturnAllRentedParticles();
|
||||
}
|
||||
|
||||
public static void PlayTrackHitFx(int trackIndex)
|
||||
{
|
||||
var controller = Instance != null ? Instance : SceneObjectLookupCache.FindAny<TrackJudgeHitEffectController>();
|
||||
@@ -271,7 +289,8 @@ public class TrackJudgeHitEffectController : MonoBehaviour
|
||||
if (instance == null)
|
||||
return;
|
||||
|
||||
instance.transform.SetParent(parent, false);
|
||||
Transform safeParent = parent != null && parent.gameObject.activeInHierarchy ? parent : transform;
|
||||
instance.transform.SetParent(safeParent, false);
|
||||
instance.transform.localPosition = trackParticleLocalPosition;
|
||||
instance.transform.localRotation = Quaternion.Euler(trackParticleLocalEulerAngles);
|
||||
instance.transform.localScale = trackParticleLocalScale;
|
||||
@@ -280,7 +299,7 @@ public class TrackJudgeHitEffectController : MonoBehaviour
|
||||
Color tint = GetTrackColor(trackIndex);
|
||||
ApplyParticleColor(entry, tint);
|
||||
|
||||
StartCoroutine(RecycleParticleRoutine(instance, entry, Mathf.Max(0.05f, entry.Lifetime)));
|
||||
StartCoroutine(RecycleParticleRoutine(instance, entry, CurrentParticleRentToken(instance), Mathf.Max(0.05f, entry.Lifetime)));
|
||||
}
|
||||
|
||||
// Prewarm the track particle pool during load so the first judge does not pay
|
||||
@@ -354,27 +373,65 @@ public class TrackJudgeHitEffectController : MonoBehaviour
|
||||
{
|
||||
GameObject pooled = particlePool.Dequeue();
|
||||
if (pooled != null && particleCache.TryGetValue(pooled, out entry))
|
||||
{
|
||||
pooled.transform.SetParent(null, false);
|
||||
MarkParticleRented(pooled);
|
||||
return pooled;
|
||||
}
|
||||
}
|
||||
|
||||
GameObject created = CreateParticleInstance();
|
||||
if (created != null && particleCache.TryGetValue(created, out entry))
|
||||
{
|
||||
MarkParticleRented(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
entry = null;
|
||||
return created;
|
||||
}
|
||||
|
||||
private IEnumerator RecycleParticleRoutine(GameObject instance, ParticleCacheEntry entry, float delay)
|
||||
private int MarkParticleRented(GameObject instance)
|
||||
{
|
||||
if (useUnscaledTime)
|
||||
yield return new WaitForSecondsRealtime(delay);
|
||||
else
|
||||
yield return GameplayClock.WaitForSeconds(delay);
|
||||
if (instance == null)
|
||||
return -1;
|
||||
|
||||
rentedParticles.Add(instance);
|
||||
int token = ++particleRentCounter;
|
||||
particleRentIds[instance] = token;
|
||||
return token;
|
||||
}
|
||||
|
||||
private int CurrentParticleRentToken(GameObject instance)
|
||||
{
|
||||
return instance != null && particleRentIds.TryGetValue(instance, out int token) ? token : -1;
|
||||
}
|
||||
|
||||
private IEnumerator RecycleParticleRoutine(GameObject instance, ParticleCacheEntry entry, int rentToken, float delay)
|
||||
{
|
||||
// Short visual FX should always recycle in real time. Tying them to
|
||||
// GameplayClock can leak rented objects while the song clock is paused.
|
||||
yield return new WaitForSecondsRealtime(Mathf.Max(0f, delay));
|
||||
|
||||
if (instance == null)
|
||||
yield break;
|
||||
|
||||
if (CurrentParticleRentToken(instance) != rentToken)
|
||||
yield break;
|
||||
|
||||
ReturnParticleInstance(instance, entry);
|
||||
}
|
||||
|
||||
private void ReturnParticleInstance(GameObject instance, ParticleCacheEntry entry)
|
||||
{
|
||||
if (instance == null)
|
||||
return;
|
||||
|
||||
if (!rentedParticles.Remove(instance))
|
||||
return;
|
||||
|
||||
particleRentIds.Remove(instance);
|
||||
|
||||
if (entry != null && entry.Systems != null)
|
||||
{
|
||||
foreach (var ps in entry.Systems)
|
||||
@@ -391,6 +448,19 @@ public class TrackJudgeHitEffectController : MonoBehaviour
|
||||
particlePool.Enqueue(instance);
|
||||
}
|
||||
|
||||
private void ReturnAllRentedParticles()
|
||||
{
|
||||
var snapshot = new List<GameObject>(rentedParticles);
|
||||
foreach (var instance in snapshot)
|
||||
{
|
||||
if (instance == null)
|
||||
continue;
|
||||
|
||||
particleCache.TryGetValue(instance, out var entry);
|
||||
ReturnParticleInstance(instance, entry);
|
||||
}
|
||||
}
|
||||
|
||||
private Color GetTrackColor(int trackIndex)
|
||||
{
|
||||
if (trackColors != null && trackIndex >= 0 && trackIndex < trackColors.Length)
|
||||
|
||||
Reference in New Issue
Block a user