Recently, I've spent a few hours with Unity's new animation system, Mecanim. I wasn't working on any complex animations, but only wanted to implement fading of a few elements in my scene. This turned out to be a real desaster. Why?

  • For simply fading a property in an out, I needed two animation clips and four states, with pretty sensitive transition setup (I wanted the fading to be interruptible, etc.)
  • The animations cannot go from the current property value to the target at a certain speed. They will always animate from start to end.
  • Adding a delay would require either to modify the animation clip or do even more complex state setup.

So, after fiddling with this for a while, I came to the conclusion that this kind of animation is better done in code (which seems to be what people in the forums think, too). I've set up a simple class for fading a property value, added the logic to my MonoBehaviour script, and had it working the way I wanted within 20 minutes. Go figure.

For anyone interested, here is the code. It could probably be improved in various ways, but it's doing what I want for my project. Feel free to play with it.

ValueFader.cs:

public struct FadeParameters
{
    public float initialValue;
    public float maxValue;
    public float minValue;
    public float fadeInDuration;
    public float fadeOutDuration;
    public float fadeInDelay; // applies only when going from Idle to FadingIn (not e.g. FadingOut -> FadingIn)
}

public class ValueFader
{
    private enum FadeState
    {
        FadingIn,
        FadingOut,
        Idle
    }

    private float _remainingDelay;
    private float _currentValue;
    private FadeParameters _fadeParameters;
    private FadeState _currentFadeState = FadeState.Idle;
    private float _fadeInIncrement;
    private float _fadeOutIncrement;
    public float currentValue { get { return _currentValue; } }

    public ValueFader(FadeParameters fp)
    {
        _fadeParameters = fp;
        _fadeInIncrement = (_fadeParameters.maxValue - _fadeParameters.minValue) / _fadeParameters.fadeInDuration;
        _fadeOutIncrement = (_fadeParameters.maxValue - _fadeParameters.minValue) / _fadeParameters.fadeOutDuration;
        _currentValue = fp.initialValue;
        _remainingDelay = fp.fadeInDelay;
    }

    public void FadeIn()
    {
        if (_currentValue < _fadeParameters.maxValue)
        {
            _currentFadeState = FadeState.FadingIn;
        }
        else
        {
            SetIdle();
        }
    }

    public void FadeOut()
    {
        if (_currentValue > _fadeParameters.minValue)
        {
            _currentFadeState = FadeState.FadingOut;
        }
        else
        {
            SetIdle(); // might have been in FadingIn, during delay
        }
    }

    // Returns true, if currentValue was changed by this method
    public bool Update(float deltaTime)
    {
        if (_currentFadeState == FadeState.Idle)
        {
            return false;
        }

        bool retVal = false;

        if (_currentFadeState == FadeState.FadingIn)
        {
            if (_remainingDelay <= 0.0f)
            {
                _currentValue += _fadeInIncrement * deltaTime;
                if (_currentValue >= _fadeParameters.maxValue)
                {
                    _currentValue = _fadeParameters.maxValue;
                    _currentFadeState = FadeState.Idle;
                }

                retVal = true;
            }
            else
            {
                _remainingDelay -= deltaTime;
            }
        }
        else //if (_currentFadeState == FadeState.FadingOut)
        {
            _currentValue -= _fadeOutIncrement * deltaTime;
            if (_currentValue <= _fadeParameters.minValue)
            {
                _currentValue = _fadeParameters.minValue;
                SetIdle();
            }

            retVal = true;
        }

        return retVal;
    }

    private void SetIdle()
    {
        _currentFadeState = FadeState.Idle;
        _remainingDelay = _fadeParameters.fadeInDelay;
    }
}

And here is how you would use it, from within a MonoBehavior script.

Set up the animation parameters like this, and store the ValueFader in a class member:

FadeParameters imageFP;
imageFP.fadeInDuration = 1.0f;
imageFP.fadeOutDuration = 0.7f;
imageFP.initialValue = 0.0f;
imageFP.maxValue = 0.1f;
imageFP.minValue = 0.0f;
imageFP.fadeInDelay = 1.0f;

 _imageFader = new ValueFader(imageFP);

 

How you control fading values in or out, depends entirely on your logic. You might want to do it based on some event:

void OnSomethingHappened()

{

   ...

     if (startFadingIn)
   {
        _imageFader.FadeIn();
   }
   else if (startFadingOut)
   {
        _imageFader.FadeOut();
   }

}

You get the idea... In Update(), apply the animated value to a material color, or whatever you want to animate:

void Update()
{
    if (_imageFader.Update(Time.deltaTime))
    {
        _imageMaterial.SetColor("_Color", new Color(1.0f, 1.0f, 1.0f, _imageFader.currentValue));
    }

    ...

}

 And that's all :)