- Home /
Manually and efficiently placing thousands of particles
I have a Shuriken system based on this answer to another question about manually producing particles. However, it's very costly to run Emit() and Play() after placing each particle. I've written an alternate method that assigns the various positions to the particle themselves (rather than singly via emitparams) and then calls Emit() and Play(). It does perform much faster, but all the particles end up at 0,0,0 instead of their respective positions. I've logged out their positions to make sure they are correct and they are, but I'm not sure why this method is not working. How do I set the positions of individual particles correctly in this more efficient method?
I am rendering many of these pools and around 5-6k particles at a time, so I need a more performant method than one at a time via emitparams.
Below in the code you'll see: - The old, inefficient but working method in the script below is CreateParticle() - The new, more performant method that doesn't yet render the positions correctly is CreateParticles()
using UnityEngine;
[RequireComponent(typeof(ParticleSystem))]
public class ParticlePool : MonoBehaviour
{
int _lastParticleIndex = 0; // keeps track of our oldest particle (for deletion)
ParticleSystem _particleSystem; // our object's particle system
ParticleSystem.Particle[] _particles; // our reusable array of particles
ParticleSystem.EmitParams _emitParams; // reusable emitparams
int _maxParticles = 0; // total number of particles in our scene before re-using
private float _maxValue;
private bool _primed;
private void Awake()
{
if (_primed == false)
{
Initialize(); // initialize all of our member variables
_primed = true;
}
}
public void ResetParticles() // used to erase all previous particles written to the system
{
for (int i = 0; i < _particles.Length; i++)
{
_particles[i].remainingLifetime = -1;
_particles[i].startLifetime = 1;
}
_lastParticleIndex = 0; // keep track of oldest particle - now, of course, reset to the beginning
// have to re-write
_particleSystem.SetParticles(_particles, _particles.Length); // write those pesky particles back into our ParticleSystem
}
public void CreateParticles(Vector3[] particlePositions, float[] particleTransparencies, Color32 color)
{
for (int i = 0; i < particlePositions.Length; i++)
{
if (i > _maxParticles)
break;
color.a = (byte) (particleTransparencies[i] * 255);
var particle = _particles[i];
particle.position = particlePositions[i];
particle.startLifetime = _maxValue;
particle.startColor = color;
_particles[i] = particle;
}
_particleSystem.SetParticles(_particles, _particles.Length);
_particleSystem.Emit(_particles.Length);
_particleSystem.Play();
}
public void CreateParticle(Vector3 position, Color32 color)
{
// if we're at our particle count limit, kill our oldest particle.
if (_lastParticleIndex >= _maxParticles)
{
// set lifetime to -1 to kill the particle
_particles[_lastParticleIndex].remainingLifetime = -1;
// we need to reset start lifetime to a normal value, too or the particle will still have infinite lifetime
_particles[_lastParticleIndex].startLifetime = 1;
_lastParticleIndex++; // keep track of oldest particle
if (_lastParticleIndex >= _maxParticles) _lastParticleIndex = 0;
// have to re-write
_particleSystem.SetParticles(_particles, _particles.Length); // write those pesky particles back into our ParticleSystem
}
// set up params for this particle, you can use whatever you want (see unity docs for EmitParams for what's available)
_emitParams.position = position;
_emitParams.startColor = color;
_emitParams.startLifetime = _maxValue; // float.MaxValue makes the particle's lifetime infinite
_particleSystem.Emit(_emitParams, 1);
_particleSystem.Play();
}
void Initialize()
{
if (_particleSystem == null)
{
_particleSystem = GetComponent<ParticleSystem>();
_particleSystem.transform.parent = gameObject.transform;
}
_maxParticles = _particleSystem.main.maxParticles;
_maxValue = float.MaxValue;
if (_particles == null || _particles.Length < _particleSystem.main.maxParticles)
_particles = new ParticleSystem.Particle[_particleSystem.main.maxParticles];
}
}