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

ParticleSystem.rateOverTimeMultiplier and rateOverDistanceMultiplier not working as expected

Discussion in 'Scripting' started by memoid, Mar 12, 2017.

  1. memoid

    memoid

    Joined:
    Aug 30, 2014
    Posts:
    10
    I have a bunch of particle systems in my scene and I'd like to collectively control their emission rate and rateOverTimeMultiplier and rateOverDistanceMultiplier looks perfect
    https://docs.unity3d.com/ScriptRefe...missionModule-rateOverDistanceMultiplier.html
    https://docs.unity3d.com/ScriptReference/ParticleSystem.EmissionModule-rateOverTimeMultiplier.html

    However it doesn't seem to be multiplier, it just sets the emission rate directly. Am I misunderstanding it?

    This is the behaviour that I would like, and this works:

    Code (csharp):
    1.  
    2.     public ParticleSystem[] particleSystems;
    3.     public int[] emissionRates = { 11, 12, 13, 14 };
    4.     public float emissionRateMult = 2.5f;
    5.  
    6.     void Update()
    7.     {
    8.         for (int i = 0; i < particleSystems.Length; i++)
    9.         {
    10.             ParticleSystem ps = particleSystems[i];
    11.             ParticleSystem.EmissionModule emission = ps.emission;
    12.             emission.rateOverDistance = emissionRates[i] * emissionRateMult;
    13.         }
    14.     }
    15.  
    But of course this means I need to enter all of the emission rates in here, if I can avoid that it would be great. So I tried the below, but this just sets all of the actual rateOverDistance to 2.5!

    Code (csharp):
    1.  
    2.     for (int i = 0; i < particleSystems.Length; i++)
    3.     {
    4.         ParticleSystem ps = particleSystems[i];
    5.         ParticleSystem.EmissionModule emission = ps.emission;
    6.         emission.rateOverDistanceMultiplier = emissionRateMult;
    7.     }
    8.  


    Am I misunderstanding what this is supposed to do? Or is this a bug?

    Unity 5.5.2f1 64bit on Windows 10 Pro btw.
     
  2. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,891
    Same issue here. As Memo says, I would expect it to modulate the actual rate - not override it.
     
  3. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,306
    Oh goodie, not just me. Maybe update the docs?
     
    arkano22 likes this.
  4. OhiraKyou

    OhiraKyou

    Joined:
    Mar 27, 2012
    Posts:
    259
    I just ran into this issue again. I have some particle systems that use a curve and some that use a constant. I want to set a multiplier based on the player's distance to the particle system, regardless of how the emission is calculated. But, none of the "multiplier" values are actually multiplying anything. Can we get an explanation?
     
    Lipoly likes this.
  5. Lipoly

    Lipoly

    Joined:
    Feb 11, 2014
    Posts:
    42
    To add further info to the confusing state of how this works, not only does it not seem to actually multiply anything, it only appears to affect the latter value when using "Random between two constants".
     
  6. barbelot

    barbelot

    Joined:
    Jul 18, 2017
    Posts:
    38
    Same problem here, an emission rate multiplier sounded perfect for what I wanted but using it it seems to only affect the emission rate in certain configurations as Lipoly said, and when it does it just set the rate, not multiply it...
     
  7. VirtusH

    VirtusH

    Joined:
    Aug 18, 2015
    Posts:
    95
    This is still a problem in 2018.3 it seems?
     
  8. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    467
    I'm also experiencing this. Seems confusing to me that setting the multiplier also sets the base rate.
     
  9. Robdon

    Robdon

    Joined:
    Jan 10, 2014
    Posts:
    141
    I also just fell into this. Would have been perfect if it worked, but as others say, it just seems to override the rates, rather than being a multiplier.

    Guess it is a bug that just hasn't been fixed.
     
  10. PanicEnsues

    PanicEnsues

    Joined:
    Jul 17, 2014
    Posts:
    187
    Just in case anyone else runs across this thread while trying to figure out why emission multipliers aren't multipliers...

    I tried submitting a bug for this, but was told that:

    "emission.rateOverTimeMultiplier is made for Particle System Curve mode (check screenshot). It multiplies every point of the Curve by that Multiplier. Let's say that Curve initially has values 0-1. If the Curve Multiplier is set to 100, the values in your custom Curve will now become 0-100 (according to every individual point on the Curve).

    If you do not intend to use Curve mode, just stick to adjusting emission.RateOverTime. If you would have Curve mode selected and change emission.rateOverTime in the Script, Curve mode would automatically be changed to Constant.
    "

    Which unfortunately means that unless you're using curve mode (which I almost never do) you'll have to script your own multiplier system where you store the initial value.
     
  11. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,298
    Just stumbled across this (again). Here is a little static class to manage this for you. Though I would not really recommend using it too much because it introduces a hidden state where there should be none.

    Code (CSharp):
    1. public static class ParticleRateUtils
    2. {
    3.     /// <summary>
    4.     /// Format:
    5.     ///  key = InstanceID of the ParticleSystem
    6.     ///  value = (int)mode, (float)const or constMin, (float)constMax
    7.     /// </summary>
    8.     public static Dictionary<int, float[]> EmissionRateMultiplierMemory;
    9.  
    10.     /// <summary>
    11.     /// This memorizes the particle system values based on their InstanceID.
    12.     /// Be sure to clear the cache if you unload a scene or it may contain wrong values.
    13.     ///
    14.     /// The whole particle rater over time multiplier is a mess, see:
    15.     /// https://forum.unity.com/threads/particlesystem-rateovertimemultiplier-and-rateoverdistancemultiplier-not-working-as-expected.460672/
    16.     /// </summary>
    17.     /// <param name="ps"></param>
    18.     /// <param name="multiplier"></param>
    19.     public static void SetEmissionRateOverTimeMultiplier(ParticleSystem ps, float multiplier)
    20.     {
    21.         if (ps == null)
    22.             return;
    23.  
    24.         var emission = ps.emission;
    25.         if (emission.rateOverTime.mode == ParticleSystemCurveMode.Curve || emission.rateOverTime.mode == ParticleSystemCurveMode.TwoCurves)
    26.         {
    27.             emission.rateOverTimeMultiplier = multiplier;
    28.         }
    29.         else
    30.         {
    31.             if (EmissionRateMultiplierMemory == null)
    32.             {
    33.                 EmissionRateMultiplierMemory = new Dictionary<int, float[]>();
    34.             }
    35.  
    36.             var id = ps.GetInstanceID();
    37.             bool memoryExists = EmissionRateMultiplierMemory.ContainsKey(id);
    38.  
    39.             // remove from memory if mode has changed
    40.             if (memoryExists && (int)EmissionRateMultiplierMemory[id][0] != (int)emission.rateOverTime.mode)
    41.             {
    42.                 EmissionRateMultiplierMemory.Remove(id);
    43.                 memoryExists = false;
    44.             }
    45.  
    46.             // create memory and set new values
    47.             if (emission.rateOverTime.mode == ParticleSystemCurveMode.Constant)
    48.             {
    49.                 if (memoryExists == false)
    50.                 {
    51.                     EmissionRateMultiplierMemory[id] = new float[] {
    52.                         (float)emission.rateOverTime.mode,
    53.                         emission.rateOverTime.constant,
    54.                         0f
    55.                     };
    56.                 }
    57.  
    58.                 // finally we can do this
    59.                 var rate = emission.rateOverTime;
    60.                 rate.constant = EmissionRateMultiplierMemory[id][1] * multiplier;
    61.                 emission.rateOverTime = rate;
    62.             }
    63.             else if (emission.rateOverTime.mode == ParticleSystemCurveMode.TwoConstants)
    64.             {
    65.                 if (memoryExists == false)
    66.                 {
    67.                     EmissionRateMultiplierMemory[id] = new float[] {
    68.                         (float)emission.rateOverTime.mode,
    69.                         emission.rateOverTime.constantMin,
    70.                         emission.rateOverTime.constantMax
    71.                     };
    72.                 }
    73.  
    74.                 // finally we can do this
    75.                 var rate = emission.rateOverTime;
    76.                 rate.constantMin = EmissionRateMultiplierMemory[id][1] * multiplier;
    77.                 rate.constantMax = EmissionRateMultiplierMemory[id][2] * multiplier;
    78.                 emission.rateOverTime = rate;
    79.             }
    80.         }
    81.     }
    82.  
    83.     /// <summary>
    84.     /// Removes the cached values for the given particle system.
    85.     /// </summary>
    86.     /// <param name="ps"></param>
    87.     public static void ClearEmissionRateOverTimeMultiplierCacheFor(ParticleSystem ps)
    88.     {
    89.         if (ps == null)
    90.             return;
    91.  
    92.         if (EmissionRateMultiplierMemory == null)
    93.             return;
    94.  
    95.         EmissionRateMultiplierMemory.Remove(ps.GetInstanceID());
    96.     }
    97.  
    98.     /// <summary>
    99.     /// Clear all memories memories. Don't do this if you still have some changes cached.
    100.     /// </summary>
    101.     public static void ClearEmissionRateOverTimeMultiplierCache()
    102.     {
    103.         if (EmissionRateMultiplierMemory == null)
    104.             return;
    105.  
    106.         EmissionRateMultiplierMemory.Clear();
    107.     }
    108. }
     
    PanicEnsues likes this.
  12. milesizzo

    milesizzo

    Joined:
    Dec 2, 2017
    Posts:
    1
    Just stumbled across this too, and I solved it in a similar way to _geo__. I wrote this class to deal with the problem:
    Code (CSharp):
    1. public class ParticleEmissionMultiplier
    2. {
    3.     private readonly ParticleSystem.MinMaxCurve initialRateOverTime;
    4.     private readonly ParticleSystem.MinMaxCurve initialRateOverDistance;
    5.     private ParticleSystem.EmissionModule emission;
    6.  
    7.     public ParticleEmissionMultiplier(ParticleSystem.EmissionModule emission)
    8.     {
    9.         this.initialRateOverDistance = emission.rateOverDistance;
    10.         this.initialRateOverTime = emission.rateOverTime;
    11.         this.emission = emission;
    12.     }
    13.  
    14.     private static ParticleSystem.MinMaxCurve MultiplyConstant(ParticleSystem.MinMaxCurve minMaxCurve, float multiplier)
    15.     {
    16.         switch (minMaxCurve.mode)
    17.         {
    18.             case ParticleSystemCurveMode.Constant:
    19.                 return minMaxCurve.constant * multiplier;
    20.             case ParticleSystemCurveMode.TwoConstants:
    21.                 return new ParticleSystem.MinMaxCurve(minMaxCurve.constantMin * multiplier, minMaxCurve.constantMax * multiplier);
    22.             default:
    23.                 return minMaxCurve;
    24.         }
    25.     }
    26.  
    27.     public float RateOverTimeMultiplier
    28.     {
    29.         set
    30.         {
    31.             switch (this.initialRateOverTime.mode)
    32.             {
    33.                 case ParticleSystemCurveMode.Curve:
    34.                 case ParticleSystemCurveMode.TwoCurves:
    35.                     this.emission.rateOverTimeMultiplier = value;
    36.                     break;
    37.                 default:
    38.                     this.emission.rateOverTime = MultiplyConstant(this.initialRateOverTime, value);
    39.                     break;
    40.             }
    41.         }
    42.     }
    43.  
    44.     public float RateOverDistanceMultiplier
    45.     {
    46.         set
    47.         {
    48.             switch (this.initialRateOverDistance.mode)
    49.             {
    50.                 case ParticleSystemCurveMode.Curve:
    51.                 case ParticleSystemCurveMode.TwoCurves:
    52.                     this.emission.rateOverDistanceMultiplier = value;
    53.                     break;
    54.                 default:
    55.                     this.emission.rateOverDistance = MultiplyConstant(this.initialRateOverDistance, value);
    56.                     break;
    57.             }
    58.         }
    59.     }
    60. }
    An example of usage:
    Code (CSharp):
    1. public class MultiplierTest : MonoBehaviour
    2. {
    3.     [SerializeField]
    4.     private ParticleSystem ps;
    5.  
    6.     [SerializeField]
    7.     private float amount = 1f;
    8.  
    9.     private ParticleEmissionMultiplier multiplier;
    10.  
    11.     private void Awake()
    12.     {
    13.         this.multiplier = new ParticleEmissionMultiplier(this.ps.emission);
    14.     }
    15.  
    16.     private void Update()
    17.     {
    18.         this.multiplier.RateOverDistanceMultiplier = this.multiplier.RateOverTimeMultiplier = this.amount;
    19.     }
    20. }
     
    _geo__ and AlejMC like this.
  13. AlejMC

    AlejMC

    Joined:
    Oct 15, 2013
    Posts:
    149
    Thanks a lot for this example, will be using it this way then, judging by how recent it is your test.
    The documentation on https://docs.unity3d.com/ScriptRefe...missionModule-rateOverDistanceMultiplier.html seemed to imply that it would work as a catch it all utility.
     
  14. anthonov

    anthonov

    Joined:
    Sep 24, 2015
    Posts:
    160
    I tested the "rateOverTimeMultiplier" with a curve mode and it doesn't work. It simply replace the curve rate by the multiply value.
    I still haven't found a way to use this multiplier. This looks very faulty.
     
  15. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,298
    Turns out "startSize" also shares this lovely behaviour. Here is the class to deal with that (based on @milesizzo idea).
    Code (CSharp):
    1.    public class ParticleStartSizeMultiplier
    2.     {
    3.         private readonly ParticleSystem.MinMaxCurve initialStartSize;
    4.         private readonly ParticleSystem.MinMaxCurve initialStartSizeX;
    5.         private readonly ParticleSystem.MinMaxCurve initialStartSizeY;
    6.         private readonly ParticleSystem.MinMaxCurve initialStartSizeZ;
    7.         private ParticleSystem.MainModule main;
    8.  
    9.         public ParticleStartSizeMultiplier(ParticleSystem.MainModule main)
    10.         {
    11.             this.initialStartSize = main.startSize;
    12.             this.initialStartSizeX = main.startSizeX;
    13.             this.initialStartSizeY = main.startSizeY;
    14.             this.initialStartSizeZ = main.startSizeZ;
    15.             this.main = main;
    16.         }
    17.  
    18.         private static ParticleSystem.MinMaxCurve MultiplyConstant(ParticleSystem.MinMaxCurve minMaxCurve, float multiplier)
    19.         {
    20.             switch (minMaxCurve.mode)
    21.             {
    22.                 case ParticleSystemCurveMode.Constant:
    23.                     return minMaxCurve.constant * multiplier;
    24.                 case ParticleSystemCurveMode.TwoConstants:
    25.                     return new ParticleSystem.MinMaxCurve(minMaxCurve.constantMin * multiplier, minMaxCurve.constantMax * multiplier);
    26.                 default:
    27.                     return minMaxCurve;
    28.             }
    29.         }
    30.  
    31.         /// <summary>
    32.         /// Sets StartSizeMultiplier or StartSize3DMultiplier depending on which is used.
    33.         /// </summary>
    34.         public float StartSizeMultipliers
    35.         {
    36.             set
    37.             {
    38.                 if (main.startSize3D)
    39.                 {
    40.                     StartSize3DMultiplier = value;
    41.                 }
    42.                 else
    43.                 {
    44.                     StartSizeMultiplier = value;
    45.                 }
    46.             }
    47.         }
    48.  
    49.         public float StartSizeMultiplier
    50.         {
    51.             set
    52.             {
    53.                 if (!main.startSize3D)
    54.                 {
    55.                     switch (this.initialStartSize.mode)
    56.                     {
    57.                         case ParticleSystemCurveMode.Curve:
    58.                         case ParticleSystemCurveMode.TwoCurves:
    59.                             this.main.startSizeMultiplier = value;
    60.                             break;
    61.                         default:
    62.                             this.main.startSize = MultiplyConstant(this.initialStartSize, value);
    63.                             break;
    64.                     }
    65.                 }
    66.                 else
    67.                 {
    68.                     Debug.LogWarning("You are trying to set StartSizeMultiplier on a ParticleSystem which is using startSize3D. Use StartSize3DMultiplier instead.");
    69.                 }
    70.             }
    71.         }
    72.  
    73.         public float StartSize3DMultiplier
    74.         {
    75.             set
    76.             {
    77.                 if (main.startSize3D)
    78.                 {
    79.                     StartSizeXMultiplier = value;
    80.                     StartSizeYMultiplier = value;
    81.                     StartSizeZMultiplier = value;
    82.                 }
    83.                 else
    84.                 {
    85.                     Debug.LogWarning("You are trying to set StartSize3DMultiplier on a ParticleSystem which is NOT using startSize3D. Use StartSizeMultiplier instead.");
    86.                 }
    87.             }
    88.         }
    89.  
    90.         public float StartSizeXMultiplier
    91.         {
    92.             set
    93.             {
    94.                 if (main.startSize3D)
    95.                 {
    96.                     switch (this.initialStartSizeX.mode)
    97.                     {
    98.                         case ParticleSystemCurveMode.Curve:
    99.                         case ParticleSystemCurveMode.TwoCurves:
    100.                             this.main.startSizeXMultiplier = value;
    101.                             break;
    102.                         default:
    103.                             this.main.startSizeX = MultiplyConstant(this.initialStartSizeX, value);
    104.                             break;
    105.                     }
    106.                 }
    107.                 else
    108.                 {
    109.                     Debug.LogWarning("You are trying to set StartSizeXMultiplier on a ParticleSystem which is NOT using startSize3D. Use StartSizeMultiplier instead.");
    110.                 }
    111.             }
    112.         }
    113.  
    114.         public float StartSizeYMultiplier
    115.         {
    116.             set
    117.             {
    118.                 if (main.startSize3D)
    119.                 {
    120.                     switch (this.initialStartSizeY.mode)
    121.                     {
    122.                         case ParticleSystemCurveMode.Curve:
    123.                         case ParticleSystemCurveMode.TwoCurves:
    124.                             this.main.startSizeYMultiplier = value;
    125.                             break;
    126.                         default:
    127.                             this.main.startSizeY = MultiplyConstant(this.initialStartSizeY, value);
    128.                             break;
    129.                     }
    130.                 }
    131.                 else
    132.                 {
    133.                     Debug.LogWarning("You are trying to set StartSizeYMultiplier on a ParticleSystem which is NOT using startSize3D. Use StartSizeMultiplier instead.");
    134.                 }
    135.             }
    136.         }
    137.  
    138.         public float StartSizeZMultiplier
    139.         {
    140.             set
    141.             {
    142.                 if (main.startSize3D)
    143.                 {
    144.                     switch (this.initialStartSizeZ.mode)
    145.                     {
    146.                         case ParticleSystemCurveMode.Curve:
    147.                         case ParticleSystemCurveMode.TwoCurves:
    148.                             this.main.startSizeZMultiplier = value;
    149.                             break;
    150.                         default:
    151.                             this.main.startSizeZ = MultiplyConstant(this.initialStartSizeZ, value);
    152.                             break;
    153.                     }
    154.                 }
    155.                 else
    156.                 {
    157.                     Debug.LogWarning("You are trying to set StartSizeZMultiplier on a ParticleSystem which is NOT using startSize3D. Use StartSizeMultiplier instead.");
    158.                 }
    159.             }
    160.         }
    161.     }
     
  16. zhaozony

    zhaozony

    Joined:
    Jan 7, 2020
    Posts:
    29
    *Error:
    Do not create your own module instances, get them from a ParticleSystem instance*
    NullReferenceException: Do not create your own module instances, get them from a ParticleSystem instance
    UnityEngine.ParticleSystem+EmissionModule.set_rateOverTime (UnityEngine.ParticleSystem+MinMaxCurve value) (at <9d9556280fbc46eaacd51cbce266662a>:0)
    animals.scripts.util.gizmo_editor_stuff.ParticleEmissionMultiplier.set_RateOverTimeMultiplier (System.Single value) (at Assets/animals/scripts/util/gizmo_editor_stuff/ParticleEmissionMultiplier.cs:42)
     
  17. quizcanners

    quizcanners

    Joined:
    Feb 6, 2015
    Posts:
    109
    Unity 2022.3.9f1 - still happening.
    I know they have moved on and now working on VFXGraph. Understandable.
    Whoever designed the old ParticleSystem probably hates people.