some updates

This commit is contained in:
2026-07-25 08:31:10 +08:00
parent af8a0b6810
commit 86351dbd2a
225 changed files with 46757 additions and 590 deletions
@@ -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;
}
}