1
\$\begingroup\$

My question is partly a continuation of this question. So, why I can't change just one GradientColorKey this way (without replacing the whole Gradient instance):

ColorOverLifetimeModule colorOverLifetime = _particle.colorOverLifetime; 
MinMaxGradient color = colorOverLifetime.color; 
color.gradient.colorKeys[1] = new GradientColorKey(new Color(1, 0, 0), 0.5f); 
colorOverLifetime.color = color;

colorOverLifetime.color is MinMaxGradient struct, so I copy its value in a variable color.

The colorOverLifetime.color.gradient property is Gradient class and there is no need to copy it.

The colorKeys property is just an array of GradientColorKey struct. So, I assign a new GradientColorKey value to the array element that already exists.

Why shouldn't this work?

\$\endgroup\$
1
\$\begingroup\$

Well, while I was writing this question, I think I found the answer along the way. If you look into the implementation of the ColorOverLifetimeModule struct color property it should be clear why my code doesn't work:

  public ParticleSystem.MinMaxGradient color
  {
    get
    {
      ParticleSystem.MinMaxGradient gradient = new ParticleSystem.MinMaxGradient();
      ParticleSystem.ColorOverLifetimeModule.GetColor(this.m_ParticleSystem, ref gradient);
      return gradient;
    }
    set
    {
      ParticleSystem.ColorOverLifetimeModule.SetColor(this.m_ParticleSystem, ref value);
    }
  }

in the getter they return a reference to a copy of the MinMaxGradient instance.

So, I tried to fix my code this way:

ColorOverLifetimeModule colorOverLifetime = _particle.colorOverLifetime;
MinMaxGradient color = colorOverLifetime.color;
var gradient = color.gradient;
gradient.colorKeys[1] = new GradientColorKey(new Color(1, 0, 0), 0.5f);
color.gradient = gradient;
colorOverLifetime.color = color;

but this still didn't work. I noticed that colorKeys[1] hadn't changed after the assignment. I could find only this piece of implementation of the Gradient class colorKeys[] property:

extern public GradientColorKey[] colorKeys
{
    [FreeFunction("Gradient_Bindings::GetColorKeys", IsThreadSafe = true, HasExplicitThis = true)] get;
    [FreeFunction("Gradient_Bindings::SetColorKeys", IsThreadSafe = true, HasExplicitThis = true)] set;
}

so I can't say what's going on in there. Likely, they use the same pattern - return a copy of the array.

If so, it should work this way:

ColorOverLifetimeModule colorOverLifetime = _particle.colorOverLifetime;
MinMaxGradient color = colorOverLifetime.color;
var keys = color.gradient.colorKeys;
keys[1] = new GradientColorKey(new Color(1f, 0f, 0f, 1f), middleColor.time);
color.gradient.colorKeys = keys;
colorOverLifetime.color = color;

and it really does!

Summary: we don't need to explicitly recreate the Gradient, but we need to copy all these properties to variables and then copy them back.

P.S. To me this looks like a very unintuitive approach that makes C# developers stumble over such things and spend extra time trying to figure out what's wrong (thanks we've got decompiled Unity sources, otherwise we could only guess). I think methods might be better candidates for such tricks - at least developers won't have certain expectations as they have about properties.

\$\endgroup\$

You must log in to answer this question.

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