some updates
This commit is contained in:
@@ -99,14 +99,33 @@ public class groundParticularController : MonoBehaviour
|
||||
private static readonly int _headColorId = Shader.PropertyToID("_HeadColor");
|
||||
private static readonly int _tailColorId = Shader.PropertyToID("_TailColor");
|
||||
private static readonly int _moveDirId = Shader.PropertyToID("_MoveDir");
|
||||
|
||||
|
||||
// 材质均衡分配逻辑
|
||||
private List<MaterialConfig> _cachedEnabledConfigs = new List<MaterialConfig>();
|
||||
private int _materialIndex = 0;
|
||||
|
||||
|
||||
private IObjectPool<GameObject> _pool;
|
||||
private Vector3 _originalStartScale;
|
||||
|
||||
// 【GC 优化】去掉 per-particle 协程,改为中心化移动更新:活跃粒子列表 + 统一 Update 遍历
|
||||
private struct ActiveParticle
|
||||
{
|
||||
public GameObject gameObject;
|
||||
public Vector3 moveDir;
|
||||
public float totalDist;
|
||||
public float traveledDist;
|
||||
public MaterialConfig? config;
|
||||
public Renderer renderer;
|
||||
}
|
||||
private readonly List<ActiveParticle> _activeParticles = new List<ActiveParticle>(64);
|
||||
|
||||
// 【GC 优化】延迟发射队列(替代 per-emit 协程 + WaitForSeconds 分配)
|
||||
private struct DelayedEmit
|
||||
{
|
||||
public float fireTime;
|
||||
}
|
||||
private readonly List<DelayedEmit> _delayedEmits = new List<DelayedEmit>(32);
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (startPoint != null) _originalStartScale = startPoint.localScale;
|
||||
@@ -141,7 +160,11 @@ public class groundParticularController : MonoBehaviour
|
||||
// 持续发射逻辑
|
||||
UpdateContinuousEmit();
|
||||
|
||||
// 【GC 优化】处理延迟发射队列(替代 per-emit 协程)
|
||||
ProcessDelayedEmits();
|
||||
|
||||
// 【GC 优化】统一更新所有活跃粒子(替代 per-particle MoveRoutine 协程)
|
||||
UpdateActiveParticles();
|
||||
|
||||
// 起点缩放抖动
|
||||
if (startPoint != null)
|
||||
@@ -166,15 +189,14 @@ public class groundParticularController : MonoBehaviour
|
||||
if (_emitTimer >= interval)
|
||||
{
|
||||
_emitTimer = 0;
|
||||
|
||||
// 批量生成
|
||||
|
||||
// 批量生成:引入混乱度用延迟队列(无 GC),立即发射直接调 Emit
|
||||
for (int i = 0; i < emitBatchSize; i++)
|
||||
{
|
||||
// 引入混乱度:随机延迟发射。用协程替代 Invoke(nameof)——
|
||||
// 后者每次做字符串反射+分配;协程延迟无字符串开销。
|
||||
if (chaosRandomDelay > 0)
|
||||
{
|
||||
StartCoroutine(DelayedEmitRoutine(Random.Range(0f, chaosRandomDelay)));
|
||||
// 【GC 优化】入队延迟发射,替代 StartCoroutine + WaitForSeconds 分配
|
||||
_delayedEmits.Add(new DelayedEmit { fireTime = Time.time + Random.Range(0f, chaosRandomDelay) });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -184,10 +206,18 @@ public class groundParticularController : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator DelayedEmitRoutine(float delay)
|
||||
// 【GC 优化】处理延迟发射队列(替代 DelayedEmitRoutine 协程)
|
||||
private void ProcessDelayedEmits()
|
||||
{
|
||||
if (delay > 0f) yield return new WaitForSeconds(delay);
|
||||
Emit();
|
||||
float now = Time.time;
|
||||
for (int i = _delayedEmits.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (now >= _delayedEmits[i].fireTime)
|
||||
{
|
||||
Emit();
|
||||
_delayedEmits.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 取粒子缓存的 Renderer;未缓存(异常情况)则补取一次。
|
||||
@@ -230,10 +260,10 @@ public class groundParticularController : MonoBehaviour
|
||||
|
||||
// 1. 获取粒子
|
||||
GameObject particle = _pool.Get();
|
||||
|
||||
|
||||
// 2. 设置层级
|
||||
particle.transform.SetParent(particleParent != null ? particleParent : null);
|
||||
|
||||
|
||||
// 3. 计算缩放
|
||||
float randomScale = Random.Range(minParticleScale, maxParticleScale);
|
||||
float audioBoost = useAudioAnalysis ? (1f + _currentAverageVolume) : 1f;
|
||||
@@ -247,12 +277,13 @@ public class groundParticularController : MonoBehaviour
|
||||
0f,
|
||||
Random.Range(-spawnOffsetY, spawnOffsetY)
|
||||
);
|
||||
particle.transform.position = startPoint.TransformPoint(finalLocalPos);
|
||||
Vector3 spawnWorldPos = startPoint.TransformPoint(finalLocalPos);
|
||||
particle.transform.position = spawnWorldPos;
|
||||
particle.transform.rotation = GetSpawnRotation();
|
||||
|
||||
// 5. 材质均衡选取逻辑
|
||||
MaterialConfig? selectedConfig = GetBalancedMaterialConfig();
|
||||
|
||||
|
||||
// 设置物理层级
|
||||
int layer = LayerMask.NameToLayer(physicsLayerName);
|
||||
if (layer != -1)
|
||||
@@ -284,8 +315,21 @@ public class groundParticularController : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 开始移动
|
||||
StartCoroutine(MoveRoutine(particle, finalLocalPos, selectedConfig));
|
||||
// 6. 【GC 优化】注册到活跃粒子列表(替代 StartCoroutine MoveRoutine)
|
||||
Vector3 currentStartPos = startPoint.position;
|
||||
Vector3 currentEndPos = endPoint.position;
|
||||
Vector3 moveDir = (currentEndPos - currentStartPos).normalized;
|
||||
float totalDist = Vector3.Distance(currentStartPos, currentEndPos);
|
||||
|
||||
_activeParticles.Add(new ActiveParticle
|
||||
{
|
||||
gameObject = particle,
|
||||
moveDir = moveDir,
|
||||
totalDist = totalDist,
|
||||
traveledDist = 0f,
|
||||
config = selectedConfig,
|
||||
renderer = renderer
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -328,72 +372,65 @@ public class groundParticularController : MonoBehaviour
|
||||
return _cachedEnabledConfigs[_materialIndex];
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator MoveRoutine(GameObject particle, Vector3 initialLocalPos, MaterialConfig? config)
|
||||
// 【GC 优化】统一更新所有活跃粒子(替代 per-particle MoveRoutine 协程,消除协程开销)
|
||||
private void UpdateActiveParticles()
|
||||
{
|
||||
if (startPoint == null || endPoint == null) yield break;
|
||||
if (startPoint == null || endPoint == null) return;
|
||||
|
||||
// 【关键修复】在协程开始时,缓存起点的世界坐标
|
||||
Vector3 spawnWorldPos = startPoint.TransformPoint(initialLocalPos);
|
||||
|
||||
Vector3 currentStartPos = startPoint.position;
|
||||
Vector3 currentEndPos = endPoint.position;
|
||||
Vector3 moveDir = (currentEndPos - currentStartPos).normalized;
|
||||
float totalDist = Vector3.Distance(currentStartPos, currentEndPos);
|
||||
float traveledDist = 0f;
|
||||
// 暂停检查:暂停期间不更新粒子
|
||||
if (PauseManager.Instance != null && PauseManager.Instance.IsPaused) return;
|
||||
|
||||
particle.transform.position = spawnWorldPos;
|
||||
|
||||
// 复用共享属性块(不 new,避免每颗粒子 GC)与缓存的 Renderer。
|
||||
MaterialPropertyBlock propBlock = _sharedPropBlock;
|
||||
Renderer renderer = GetCachedRenderer(particle);
|
||||
|
||||
while (particle != null && particle.activeInHierarchy)
|
||||
float currentBPM = GetCurrentBPM();
|
||||
float noteSpawnerSpeedMultiplier = 1.0f;
|
||||
if (beatmapManager != null && beatmapManager.noteSpawner != null)
|
||||
{
|
||||
// 如果游戏暂停,则等待直到取消暂停
|
||||
if (PauseManager.Instance != null && PauseManager.Instance.IsPaused)
|
||||
noteSpawnerSpeedMultiplier = beatmapManager.noteSpawner.EffectiveSpeedMultiplier;
|
||||
currentBPM = beatmapManager.noteSpawner.EffectiveBpm;
|
||||
}
|
||||
|
||||
float effectiveBPM = Mathf.Max(currentBPM, 60f);
|
||||
MaterialPropertyBlock propBlock = _sharedPropBlock;
|
||||
|
||||
// 倒序遍历便于移除
|
||||
for (int i = _activeParticles.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var p = _activeParticles[i];
|
||||
if (p.gameObject == null || !p.gameObject.activeInHierarchy)
|
||||
{
|
||||
yield return new WaitUntil(() => !PauseManager.Instance.IsPaused);
|
||||
_activeParticles.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
float currentBPM = GetCurrentBPM();
|
||||
float noteSpawnerSpeedMultiplier = 1.0f;
|
||||
// 粒子速度须与音符下落同速:跟随 NoteSpawner 的“有效 BPM”(开关决定用谱面 bpm 还是固定参考 bpm)。
|
||||
if (beatmapManager != null && beatmapManager.noteSpawner != null)
|
||||
{
|
||||
noteSpawnerSpeedMultiplier = beatmapManager.noteSpawner.EffectiveSpeedMultiplier;
|
||||
currentBPM = beatmapManager.noteSpawner.EffectiveBpm;
|
||||
}
|
||||
|
||||
float effectiveBPM = Mathf.Max(currentBPM, 60f);
|
||||
float noteSpeed = (totalDist * effectiveBPM * noteSpawnerSpeedMultiplier) / 240f;
|
||||
|
||||
float noteSpeed = (p.totalDist * effectiveBPM * noteSpawnerSpeedMultiplier) / 240f;
|
||||
float step = noteSpeed * speedMultiplier * emissionSpeedMultiplier * perFrameMoveMultiplier * Time.deltaTime;
|
||||
if (step <= 0) step = 0.01f;
|
||||
|
||||
traveledDist += step;
|
||||
particle.transform.position += moveDir * step;
|
||||
p.traveledDist += step;
|
||||
p.gameObject.transform.position += p.moveDir * step;
|
||||
|
||||
// 【动态空间渐变色逻辑】
|
||||
if (config != null && config.Value.useGradientOverride && renderer != null)
|
||||
// 动态空间渐变色
|
||||
if (p.config != null && p.config.Value.useGradientOverride && p.renderer != null)
|
||||
{
|
||||
// 获取渐变的首尾颜色
|
||||
Color headColor = config.Value.colorGradient.Evaluate(1f);
|
||||
Color tailColor = config.Value.colorGradient.Evaluate(0f);
|
||||
|
||||
renderer.GetPropertyBlock(propBlock);
|
||||
// 用缓存的属性 ID(避免每帧字符串查找)
|
||||
Color headColor = p.config.Value.colorGradient.Evaluate(1f);
|
||||
Color tailColor = p.config.Value.colorGradient.Evaluate(0f);
|
||||
|
||||
p.renderer.GetPropertyBlock(propBlock);
|
||||
propBlock.SetColor(_headColorId, headColor);
|
||||
propBlock.SetColor(_tailColorId, tailColor);
|
||||
propBlock.SetVector(_moveDirId, moveDir);
|
||||
renderer.SetPropertyBlock(propBlock);
|
||||
propBlock.SetVector(_moveDirId, p.moveDir);
|
||||
p.renderer.SetPropertyBlock(propBlock);
|
||||
}
|
||||
|
||||
if (traveledDist >= totalDist)
|
||||
// 到达终点:回收
|
||||
if (p.traveledDist >= p.totalDist)
|
||||
{
|
||||
if (particle.activeInHierarchy) _pool.Release(particle);
|
||||
yield break;
|
||||
if (p.gameObject.activeInHierarchy) _pool.Release(p.gameObject);
|
||||
_activeParticles.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
yield return null;
|
||||
|
||||
// 写回更新的 traveledDist(struct 需回写)
|
||||
_activeParticles[i] = p;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user