Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Scaling Particle Systems at Runtime

Discussion in 'Scripting' started by shawnblais, Jan 27, 2016.

  1. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    I scoured the forums, and the asset store, and there were seemingly no good ways to scale a particle system at runtime, so I created one :)

    I̶t̶ ̶u̶s̶e̶s̶ ̶t̶h̶e̶ ̶S̶e̶r̶i̶a̶l̶i̶z̶e̶d̶O̶b̶j̶e̶c̶t̶ ̶A̶P̶I̶ ̶t̶o̶ ̶a̶c̶c̶e̶s̶s̶ ̶t̶h̶e̶ ̶h̶i̶d̶d̶e̶n̶ ̶p̶r̶o̶p̶e̶r̶t̶i̶e̶s̶ ̶o̶f̶ ̶t̶h̶e̶ ̶S̶h̶u̶r̶i̶k̶e̶n̶ ̶s̶y̶s̶t̶e̶m̶,̶ ̶a̶n̶d̶ ̶I̶ ̶o̶p̶t̶i̶m̶i̶z̶e̶d̶ ̶i̶t̶ ̶t̶o̶ ̶a̶v̶o̶i̶d̶ ̶a̶n̶y̶ ̶g̶a̶r̶b̶a̶g̶e̶ ̶c̶r̶e̶a̶t̶i̶o̶n̶ ̶a̶t̶ ̶a̶l̶l̶.̶ ̶ ̶I̶t̶ ̶a̶l̶s̶o̶ ̶s̶u̶p̶p̶o̶r̶t̶s̶ ̶n̶e̶s̶t̶e̶d̶ ̶p̶a̶r̶t̶i̶c̶l̶e̶ ̶s̶y̶s̶t̶e̶m̶s̶.̶

    [UPDATE] It uses the new particle properties exposed in Unity 5.3 (thanks Karl.Jones!). Currently there's a crash bug in the editor when you try and scale a "Curve", so I've had to comment out a few lines. Once (if?) it's fixed, we can include them again.

    You can check it out here:
    https://gist.github.com/treefortress/e52499b7d6b896669d54

    Usage:
    Code (csharp):
    1.  
    2. ParticleScaler.Scale(myParticles, 2);
    3. //Or you can use an extension:
    4. myParticles.Scale(2)
    5. //You can also scale via LocalTransform
    6. myParticles.ScaleByTransform(2);
    7.  
     
    Last edited: Jan 29, 2016
  2. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,227
    We exposed script access to the modules in 5.3, you dont need to do any of the SerializedObject stuff and as you say it wont work outside of the editor. Also changing the scale of the particle system will work for some elements such as particle size.
     
  3. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Haha, damn, I'm slow... ok great, I guess I can stop learning all about the Reflection API's :) Thanks for the headsup! I'll post an updated version tomorrow.

    As an aside, it would be really cool if you guys could just implement the ParticleSystem.Scale() api natively, it's really just a matter of scaling the 4 modules, Shape, Velocity, ClampVelocity and Force..

    Seems like a feature that is in high demand, there's multiple plugins on the asset store to do it (in the Editor only), and half a dozen open threads asking how to do it, spanning the last 4 yrs.
     
  4. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,227
    People have different ideas how a particle system should scale. Hence why we provide API access so you can do it how you want.

    in 5.3 we also added this:
     
    shawnblais likes this.
  5. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Awesome, you guys rock! Really appreciate you monitoring the forums, and saving me many hours of messing around!
     
    karl_jones likes this.
  6. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    So, I dug into this a bit this morning. Modifying the properties works... but I'm totally confused why.

    This works:
    Code (csharp):
    1.  
    2. var shape = particles.shape;
    3. shape.radius *= scale;
    4. shape.box = shape.box * scale;
    5.  
    But particles.shape, is a ShapeModule, and ShapeModule is a struct. Once we copy it to a var, we should not be affecting the original value of particles.shape.

    I'm not really complaining... but I need to know, what voodoo is this!? :)
     
  7. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,227
    I explain it here http://answers.unity3d.com/questions/1131013/new-particle-system-structs-how-does-this-work.html
     
    shawnblais likes this.
  8. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Ahh, very interesting, a performance optimization. It's great that the engineering team is paying so much attention garbage generation these days. Now if they could just fix co-routines and for-each loops! ;)
     
    karl_jones likes this.
  9. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Well, looks like I've found a bug?

    This is shutting down Unity Editor everytime it runs:
    Code (csharp):
    1. var limitVel = particles.limitVelocityOverLifetime;
    2. limitVel.limit = limitVel.limit;
    This makes it impossible to update the limitVelocityOverLifetime, unless you switch to "Seperate Axis", as assigning to LimitX, LimitY and LimitZ all work fine.
     
    Last edited: Jan 28, 2016
  10. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,227
    Can you file a bug report please. Trying to copy values like that is not going to be good for performance.
     
  11. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,227
    I just tested your sample and it works fine for me. What does your log say?
     
  12. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Looks like it only occurs if you choose "Curve" for the field. And then it seems to happen on any of LimitX, LimitY etc.

    I have filed a bug report, just waiting to get a confirmation email. That code sample is just minimal reproduction code, what I'm really doing is taking the value, modifying some props, and trying to assign the updated values. How else could you update the modules?

    Reproduction steps:
    1. Create a new Particle System
    2. Check "Limit Velocity over Lifetime"
    3. Select "Curve"
    4. Run the code above on that particle system.

    I've attached the error log. Seems like the relevant line is:
    Reading particle curves from script is unsupported unless they are in constant mode
    UnityEngine.LimitVelocityOverLifetimeModule:GetMagnitude(ParticleSystem, MinMaxCurve&)
    UnityEngine.LimitVelocityOverLifetimeModule:get_limit() (at C:\buildslave\unity\build\artifacts\generated\common\modules\ParticleSystem\ParticleSystemBindings.gen.cs:571)
    [C:/buildslave/unity/build/artifacts/generated/common/modules/ParticleSystem/ParticleSystemBindings.gen.cpp line 99]
    (Filename: Assets/_Internal/_Scripts/Bardbarian/Test.cs Line: 11)
    Crash!!!
     

    Attached Files:

  13. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Ok, bug report is here: https://fogbugz.unity3d.com/default.asp?765711_snb8dek7ut8v93bv

    I can't seem to update the details though.

    After messing around some more, I've found that it happens on any of the curve fields. You can get the same behavior using particles.velocityOverLifetime, particles.forceOverLifetime, etc

    Basically just set a module to use curves, try and modify that curve, and the Editor goes boom.
     
    Last edited: Jan 28, 2016
  14. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,227
    Well I think I spotted a clue in the log file ;)
    However the editor should not crash though.
    I think this is due to how we store the curve information. I think it only goes one direction.
     
  15. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,227
    This may work for you

    Code (CSharp):
    1. // Use this for initialization
    2. public ParticleSystem.MinMaxCurve curve;
    3.  
    4. void Update ()
    5. {
    6.     curve.mode = ParticleSystemCurveMode.Curve;
    7.     var particles = GetComponent<ParticleSystem>();
    8.     var limitVel = particles.limitVelocityOverLifetime;
    9.     var v = limitVel.limit;
    10.     v.curveScalar = curve.curveScalar;
    11. }
    12.  
     
    Last edited: Jan 28, 2016
  16. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Lol, yes the error does tell us the issue, but it's still a pretty huge limitation that is not called out anywhere in the docs.

    Not being able to edit curves is unfortunate, as I bought a FX Quest particle pack, with 187 particles, and they all use curves extensively, and typically consist of 3-5 nested particle systems. Re-factoring these to not use curves is not really an option.

    The end goal is simply that we can scale these particles as we need (as the mage levels up, his explosions get bigger). Without having to save off a ton of prefabs for each effect.

    In the editor, we can scale a curve easily using the SerializableObject:
    Code (csharp):
    1. var curve = sObj.FindProperty("VelocityModule.x.minCurve").animationCurveValue;
    2. for (int i = 0; i < curve.keys.Length; i++) { curve.keys[i].value *= scale; }
    Would be nice to be able to do this at runtime as well!
     
  17. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Ahh, thats neat. Doesn't work for our use cases though, as we're not looking to replace curves, simply scale them up and down.
     
  18. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    For what it's worth, I've managed to get pretty good results using the ParticleSystemScalingMode.Local to scale the System.

    It doesn't give us quite the control we'd like, and I had to add in support for gravity, which seems to be neglected, but other than that it works pretty good!

    Code (csharp):
    1.  
    2. static public void LocalScale(ParticleSystem particles, float scale) {
    3.         particles.scalingMode = ParticleSystemScalingMode.Local;
    4.         particles.transform.localScale = particles.transform.localScale * scale;
    5.         particles.gravityModifier *= scale;
    6.         var children = particles.GetComponentsInChildren<ParticleSystem>();
    7.         for (var i = children.Length; i-- > 0;) {
    8.             if (children[i] == particles) { continue; }
    9.             children[i].scalingMode = ParticleSystemScalingMode.Local;
    10.             children[i].transform.localScale = children[i].transform.localScale * scale;
    11.             children[i].gravityModifier *= scale;
    12.         }
    13.     }
    14.  
    I've updated the gist to work with the new properties in 5.3, and to allow for ScaleByTransform:
    https://gist.github.com/treefortress/e52499b7d6b896669d54
     
    Last edited: Jan 29, 2016
  19. Maxii

    Maxii

    Joined:
    May 28, 2012
    Posts:
    45
    I found this really useful. Thanks!

    Just a thought - now that Unity 5.5 has deprecated many of these properties, an update would be very appreciated.