/// /// SURGE FRAMEWORK /// Author: Bob Berkebile /// Email: bobb@pixelplacement.com /// /// Forces a referenced particle system to flow along this spline. To optimize performance limit Max Particles to just what is necessary. Particle speed along the path is determined by Start Lifetime. /// /// using System.Collections; using System.Collections.Generic; using UnityEngine; using Pixelplacement; [ExecuteInEditMode] [RequireComponent (typeof (Spline))] public class SplineControlledParticleSystem : MonoBehaviour { //Public Variables: public float startRadius; public float endRadius; //Private Variables: [SerializeField] ParticleSystem _particleSystem = null; Spline _spline; ParticleSystem.Particle[] _particles; const float _previousDiff = .01f; //Init: void Awake () { _spline = GetComponent (); } //Loops: void LateUpdate () { if (_particleSystem == null) return; if (_particles == null) _particles = new ParticleSystem.Particle[_particleSystem.main.maxParticles]; int aliveParticlesCount = _particleSystem.GetParticles (_particles); for (int i = 0; i < aliveParticlesCount; i++) { //get calculation pieces: float seedMax = Mathf.Pow(10, _particles[i].randomSeed.ToString().Length); float seedAsPercent = _particles[i].randomSeed / seedMax; float travelPercentage = 1 - (_particles[i].remainingLifetime / _particles[i].startLifetime); //bypass issue while running at edit time when particles haven't reached the spline end: if (_spline.GetDirection(travelPercentage, false) == Vector3.zero) continue; //get a direction off our current point on the path - rotating by 1080 results in better distribution since Unity's randomSeed for particles favors lower numbers: Vector3 offshootDirection = Quaternion.AngleAxis(1080 * seedAsPercent, -_spline.GetDirection(travelPercentage, false)) * _spline.Up(travelPercentage); Vector3 previousOffshootDirection = Quaternion.AngleAxis(1080 * seedAsPercent, -_spline.GetDirection(travelPercentage - _previousDiff, false)) * _spline.Up(travelPercentage - _previousDiff, false); //cache our positions: Vector3 position = _spline.GetPosition(travelPercentage, false); //cache a previous position for velocity if possible: Vector3 lastPosition = position; if (travelPercentage - .01f >= 0) lastPosition = _spline.GetPosition(travelPercentage - _previousDiff, false); //decide how far to offshoot from the spline based on where we are between the start and end radius: float offset = Mathf.Lerp(startRadius, endRadius, travelPercentage); float previousOffset = Mathf.Lerp(startRadius, endRadius, travelPercentage - _previousDiff); //place particles depending on simulation space: Vector3 currentPosition = Vector3.zero; Vector3 previousPosition = Vector3.zero; switch (_particleSystem.main.simulationSpace) { case ParticleSystemSimulationSpace.Local: currentPosition = _particleSystem.transform.InverseTransformPoint(position + offshootDirection * offset); previousPosition = _particleSystem.transform.InverseTransformPoint(lastPosition + previousOffshootDirection * previousOffset); break; case ParticleSystemSimulationSpace.World: case ParticleSystemSimulationSpace.Custom: currentPosition = position + offshootDirection * offset; previousPosition = position + previousOffshootDirection * previousOffset; break; } //apply: _particles[i].position = currentPosition; _particles[i].velocity = currentPosition - previousPosition; } //apply the particle changes back to the system: _particleSystem.SetParticles (_particles, _particles.Length); } }