优化hierarchyab和consolemethod,然后是粒子追踪

Signed-off-by: TRADER_FOER <lhf190@outlook.com>
This commit is contained in:
2026-07-29 22:27:21 +08:00
parent d662f2fa95
commit ab5465bab0
14 changed files with 21084 additions and 18871 deletions

View File

@@ -3,6 +3,7 @@ namespace Dreamteck.Splines
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Unity.Profiling;
[ExecuteInEditMode]
[AddComponentMenu("Dreamteck/Splines/Users/Particle Controller")]
@@ -21,10 +22,11 @@ namespace Dreamteck.Splines
[SerializeField]
[HideInInspector]
private ParticleSystem _particleSystem;
private ParticleSystemRenderer _renderer;
public enum EmitPoint { Beginning, Ending, Random, Ordered }
public enum MotionType { None, UseParticleSystem, FollowForward, FollowBackward, ByNormal, ByNormalRandomized }
public enum Wrap { Default, Loop }
private ParticleSystemRenderer _renderer;
public enum EmitPoint { Beginning, Ending, Random, Ordered }
public enum MotionType { None, UseParticleSystem, FollowForward, FollowBackward, ByNormal, ByNormalRandomized }
public enum Wrap { Default, Loop }
public enum PathNodeSizeMode { LegacyOffset, ApplyPathNodeSize }
public bool is3D;
public float width = 10f;
@@ -50,8 +52,10 @@ namespace Dreamteck.Splines
public Wrap wrapMode = Wrap.Default;
[HideInInspector]
public float minCycles = 1f;
[HideInInspector]
public float maxCycles = 1f;
[HideInInspector]
public float maxCycles = 1f;
[HideInInspector]
public PathNodeSizeMode pathNodeSizeMode = PathNodeSizeMode.LegacyOffset;
private Dictionary<uint, Particle> _particleDataMap = new Dictionary<uint, Particle>();
private ParticleSystem.Particle[] _particles = new ParticleSystem.Particle[0];
@@ -59,72 +63,80 @@ namespace Dreamteck.Splines
//private Particle[] _controllers = new Particle[0];
private int _particleCount = 0;
private int _birthIndex = 0;
private int _particleDataFrame = 0;
private List<Vector4> _customParticleData = new List<Vector4>();
private readonly List<uint> _keysToRemove = new List<uint>();
private readonly Stack<Particle> _particleDataPool = new Stack<Particle>();
private static readonly ProfilerMarker ParticleControllerLateRunMarker = new ProfilerMarker("ParticleController.LateRun");
protected override void LateRun()
{
if (_particleSystem == null) return;
if (pauseWhenNotVisible)
using (ParticleControllerLateRunMarker.Auto())
{
if (_renderer == null)
if (pauseWhenNotVisible)
{
_renderer = _particleSystem.GetComponent<ParticleSystemRenderer>();
if (_renderer == null)
{
_renderer = _particleSystem.GetComponent<ParticleSystemRenderer>();
}
if (!_renderer.isVisible) return;
}
if (!_renderer.isVisible) return;
int maxParticles = _particleSystem.main.maxParticles;
if (_particles.Length != maxParticles)
{
_particles = new ParticleSystem.Particle[maxParticles];
if (maxParticles > _particleDataMap.Count)
{
_particleDataMap = new Dictionary<uint, Particle>(maxParticles);
}
}
_particleCount = _particleSystem.GetParticles(_particles);
_particleSystem.GetCustomParticleData(_customParticleData, ParticleSystemCustomData.Custom1);
_particleDataFrame++;
bool isLocal = _particleSystem.main.simulationSpace == ParticleSystemSimulationSpace.Local;
Transform particleSystemTransform = _particleSystem.transform;
for (int i = 0; i < _particleCount; i++)
{
uint seed = _particles[i].randomSeed; // 获取粒子的唯一ID
if (isLocal) TransformParticle(ref _particles[i], particleSystemTransform);
// 使用字典来检查粒子是否是新生儿这是100%可靠的
if (!_particleDataMap.TryGetValue(seed, out Particle particleData))
{
particleData = OnParticleBorn(i, seed);
}
particleData.lastSeenFrame = _particleDataFrame;
HandleParticle(i, particleData);
if (isLocal) InverseTransformParticle(ref _particles[i], particleSystemTransform);
}
// 清理字典中已经死亡的粒子数据,防止内存无限增长
if (_particleCount < _particleDataMap.Count)
{
_keysToRemove.Clear();
foreach (var pair in _particleDataMap)
{
if (pair.Value.lastSeenFrame != _particleDataFrame) _keysToRemove.Add(pair.Key);
}
foreach (var key in _keysToRemove)
{
_particleDataPool.Push(_particleDataMap[key]);
_particleDataMap.Remove(key);
}
}
_particleSystem.SetCustomParticleData(_customParticleData, ParticleSystemCustomData.Custom1);
_particleSystem.SetParticles(_particles, _particleCount);
}
int maxParticles = _particleSystem.main.maxParticles;
if (_particles.Length != maxParticles)
{
_particles = new ParticleSystem.Particle[maxParticles];
if (maxParticles > _particleDataMap.Count)
{
_particleDataMap = new Dictionary<uint, Particle>(maxParticles);
}
}
_particleCount = _particleSystem.GetParticles(_particles);
_particleSystem.GetCustomParticleData(_customParticleData, ParticleSystemCustomData.Custom1);
HashSet<uint> activeSeeds = new HashSet<uint>();
bool isLocal = _particleSystem.main.simulationSpace == ParticleSystemSimulationSpace.Local;
Transform particleSystemTransform = _particleSystem.transform;
for (int i = 0; i < _particleCount; i++)
{
uint seed = _particles[i].randomSeed; // 获取粒子的唯一ID
activeSeeds.Add(seed); // 记录存活的粒子
if (isLocal) TransformParticle(ref _particles[i], particleSystemTransform);
// 使用字典来检查粒子是否是新生儿这是100%可靠的
if (!_particleDataMap.ContainsKey(seed))
{
OnParticleBorn(i, seed);
}
HandleParticle(i, seed);
if (isLocal) InverseTransformParticle(ref _particles[i], particleSystemTransform);
}
// 清理字典中已经死亡的粒子数据,防止内存无限增长
if (activeSeeds.Count < _particleDataMap.Count)
{
List<uint> keysToRemove = new List<uint>();
foreach (var key in _particleDataMap.Keys)
{
if (!activeSeeds.Contains(key)) keysToRemove.Add(key);
}
foreach (var key in keysToRemove)
{
_particleDataMap.Remove(key);
}
}
_particleSystem.SetCustomParticleData(_customParticleData, ParticleSystemCustomData.Custom1);
_particleSystem.SetParticles(_particles, _particleCount);
}
void TransformParticle(ref ParticleSystem.Particle particle, Transform trs)
@@ -152,23 +164,21 @@ namespace Dreamteck.Splines
if (_particleSystem == null) _particleSystem = GetComponent<ParticleSystem>();
}
void HandleParticle(int index, uint seed)
void HandleParticle(int index, Particle particleData)
{
if (!_particleDataMap.TryGetValue(seed, out Particle particleData)) return; // 安全检查
float lifePercent = _particles[index].remainingLifetime / _particles[index].startLifetime;
if (motionType == MotionType.FollowBackward || motionType == MotionType.FollowForward || motionType == MotionType.None)
{
Evaluate(particleData.GetSplinePercent(wrapMode, _particles[index], motionType), ref evalResult);
Vector3 resultRight = evalResult.right;
if (!is3D)
{
_particles[index].position = evalResult.position + extendDirection * particleData.initialOffset;
}
else
{
_particles[index].position = evalResult.position + particleData.threeDOffset;
}
Evaluate(particleData.GetSplinePercent(wrapMode, _particles[index], motionType), ref evalResult);
if (!is3D)
{
Vector3 initialOffset = extendDirection * particleData.initialOffset;
_particles[index].position = evalResult.position + GetInitialOffset(evalResult, initialOffset);
}
else
{
_particles[index].position = evalResult.position + GetInitialOffset(evalResult, particleData.threeDOffset);
}
if (apply3DRotation)
{
@@ -189,16 +199,37 @@ namespace Dreamteck.Splines
finalOffset += particleData.startOffset;
}
}
_particles[index].position += resultRight * (finalOffset.x * evalResult.size)
+ evalResult.up * (finalOffset.y * evalResult.size);
if (finalOffset != Vector2.zero)
{
_particles[index].position += GetSamplePlaneOffset(evalResult, finalOffset);
}
_particles[index].velocity = evalResult.forward;
_particles[index].startColor = particleData.startColor * evalResult.color;
}
}
private void OnParticleBorn(int index, uint seed)
private static Vector3 GetSampleSpaceOffset(SplineSample sample, Vector3 offset)
{
return sample.right * (offset.x * sample.size)
+ sample.up * (offset.y * sample.size)
+ sample.forward * (offset.z * sample.size);
}
private Vector3 GetInitialOffset(SplineSample sample, Vector3 offset)
{
return pathNodeSizeMode == PathNodeSizeMode.ApplyPathNodeSize
? GetSampleSpaceOffset(sample, offset)
: offset;
}
private static Vector3 GetSamplePlaneOffset(SplineSample sample, Vector2 offset)
{
Particle newParticleData = new Particle();
return sample.right * (offset.x * sample.size) + sample.up * (offset.y * sample.size);
}
private Particle OnParticleBorn(int index, uint seed)
{
Particle newParticleData = _particleDataPool.Count > 0 ? _particleDataPool.Pop() : new Particle();
Vector4 custom = _customParticleData[index];
custom.w = 1;
@@ -234,11 +265,13 @@ namespace Dreamteck.Splines
if (!is3D)
{
newParticleData.initialOffset = Random.Range(-width, width);
newParticleData.threeDOffset = Vector3.zero;
}
else
{
newParticleData.initialOffset = 0f;
newParticleData.threeDOffset = new Vector3(
Random.Range(-width, width) * extendDirection.x,
Random.Range(-width, width) * extendDirection.x,
Random.Range(-width, width) * extendDirection.y,
Random.Range(-width, width) * extendDirection.z);
}
@@ -249,10 +282,8 @@ namespace Dreamteck.Splines
if (!(motionType == MotionType.FollowForward || motionType == MotionType.FollowBackward))
{
Vector3 right = Vector3.Cross(evalResult.forward, evalResult.up);
_particles[index].position = evalResult.position +
right * newParticleData.startOffset.x * evalResult.size * scale.x +
evalResult.up * newParticleData.startOffset.y * evalResult.size * scale.y;
Vector2 scaledStartOffset = new Vector2(newParticleData.startOffset.x * scale.x, newParticleData.startOffset.y * scale.y);
_particles[index].position = evalResult.position + GetSamplePlaneOffset(evalResult, scaledStartOffset);
}
float forceX = _particleSystem.forceOverLifetime.x.constantMax;
@@ -284,6 +315,7 @@ namespace Dreamteck.Splines
_particles[index].velocity = normal * startSpeed + new Vector3(forceX, forceY, forceZ) * time;
}
//HandleParticle(index);
return newParticleData;
}
public class Particle
@@ -295,6 +327,7 @@ namespace Dreamteck.Splines
internal float cycleSpeed = 0f;
internal Color startColor = Color.white;
internal double startPercent = 0.0;
internal int lastSeenFrame = 0;
internal double GetSplinePercent(Wrap wrap, ParticleSystem.Particle particle, MotionType motionType)
{