8
\$\begingroup\$

Good day, I'm attempting to randomly generate a space scene (quite large actually) in Unity through C# code mainly. So far, I've gotten stunning results up to what image #2 will show you. However, now I'm looking to expand the look with more random particles. So, I've made a texture atlas of 8 in a vertical row, which works on the random setting if you have an emitter and the particle system is called with PS.Play();

Assigning different materials would only work on a per particle system basis(which would be new texture PER cloud, so to speak) which is not what I want. So, my question:

How can I use the Shuriken Particle System's Texture Sheet Animation functionality through code, without having an "active" particle system? More precisely, I wish to use the 'Random Row' feature of it to assign random textures to each particle.

Since I can't share my code (due to policies), here is a bit more info on how I do it:

  1. I create an array with coordinates for each cloud.
  2. Assign a particle system to that cloud.
  3. Randomly generate all the particles for the cloud with relative offsets.
  4. Use PS.setParticles(particlesArray);

Using a Texture Atlas consisting of the letters A to H to test, only B showing on all particles. Text particles from A to H, only B showing

Using a Texture Atlas consisting of the fog particles to test, only 2nd showing on all particles. Fog particles from 1 to 8, only 2 showing

Thank you @DMGregory for the help, I've tested it multiple times and it works perfectly now, I've also went ahead and tweaked a few of my settings just to get a more "clear" space:

enter image description here

\$\endgroup\$
5
  • \$\begingroup\$ Are you setting the particle's random seed or lifetime parameters when you initialize their positions? \$\endgroup\$
    – DMGregory
    Nov 8, 2016 at 15:57
  • \$\begingroup\$ As the system isn't played out, lifetime is irrelevant, although I have played around with the setting just now as you've mentioned it to see if it would perhaps have any effect, but still no change. Also setting the particle's random seed has no effect, I tried generating a random one per particle, before and after setting their positions. \$\endgroup\$
    – Hurly
    Nov 8, 2016 at 16:03
  • \$\begingroup\$ Lifetime may be more relevant than you realize - there's a method to my madness (I've done this before). ;) If you use texture sheet animation over time, and then pre-bake the lifetime to a random percentage of its startlifetime, you can use that to randomize the particle's fixed image, even though the particle system isn't playing. \$\endgroup\$
    – DMGregory
    Nov 8, 2016 at 16:07
  • \$\begingroup\$ Will this affect particles added after the system is created? At the moment it goes from Spawner -> StarGenerator -> Collection -> Starfield -> ParticleSystem -> Stars :? Also, how would I go about pre-baking the lifetime with my current system as shown above? Everything is generated through code from the Spawner. The Starfield is a prefab that gets instantiated with a pre-configured particle system on it. \$\endgroup\$
    – Hurly
    Nov 8, 2016 at 16:17
  • \$\begingroup\$ I didn't understand anything. But +1 for the visual effect. \$\endgroup\$ Nov 11, 2016 at 3:59

1 Answer 1

4
\$\begingroup\$

In my tests, it's sufficient to set a random seed on each particle as we place it into our particle buffer.

That seed is then used to calculate all the runtime randomized properties on the particle (including texture sheet random row / random start frame, but not initial properties like StartColour which are already stored separately)

Here's an example:

using Particle = UnityEngine.ParticleSystem.Particle;

[RequireComponent(typeof(ParticleSystem))]
public class ParticleCloudRandomizer : MonoBehaviour {

    public float radius = 10f;
    ParticleSystem _system;

    void Start () {
        _system = GetComponent<ParticleSystem>();

        // Create a buffer large enough for all our particles.
        Particle[] particles = new Particle[_system.maxParticles];

        // Note the using statement above - I'm using "Particle" to mean
        // UnityEngine.ParticleSystem.Particle to save typing. ;)

        // Prep a template particle with any parameters we want constant.
        Particle template = new Particle();
        template.startLifetime = _system.startLifetime;
        template.lifetime = _system.startLifetime;
        template.startSize = _system.startSize;
        template.startColor = _system.startColor;

        // Fill our buffer with randomized copies of this particle.
        for(int i = 0; i < particles.Length; i++)
        {
            template.position = Random.insideUnitSphere * radius;

            // This is just a naive way to roughly fill the uint range.
            // I don't guarantee it gives good randomness properties,
            // but on visual inspection it looks "good enough"  
            template.randomSeed = unchecked((uint)Random.Range(int.MinValue, int.MaxValue));

            // In the past I've also used this trick of setting the lifetime
            // to a random fraction of its initial value, then setting
            // "Texture Sheet Animation -> Frame Over Time" to "Curve"
            // template.lifetime = Random.value * template.startLifetime;

            // Save this randomized particle into our buffer:
            particles[i] = template;
        }

        // Pass our filled particle buffer to the ParticleSystem.
        _system.SetParticles(particles, particles.Length);
    }
}

Here's what it looks like in my setup:

(The particle system is not playing - it's just displaying the buffer of particles we gave it at Start)

Demonstration of this script being used with a particle system

If you want to add or overwrite more particles later, just do the same operation above, setting a random seed on each before it's added to the buffer and passed to the ParticleSystem with SetParticles.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .