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 🙂