• 'Simple' property animation in Unity

    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 :)

  • Bouncing Ball Physics

    To revive my Blender skills, I've been tinkering with setting up a simple bouncing ball animation. How do you keyframe this properly, without running a physics simulation? There are tons of tutorials on the web on basic bouncing ball demos, but few go into details about what a physically plausible bouncing ball trajectory would look like. As it turns out, with an ideal bouncing ball, there are only a few basic ingredients:

    • The path is obviously a series of parabolas
    • With each bounce, a roughly constant fraction of the energy is lost. The exact value depends on the material of the ball - the magic term is "coefficent of restitution" (COR). The height of each parabolic arc is f * previous_height, where f is in the range (0,1).
    • Assuming no slowdown in the horizontal direction, the distance between touch down positions (resp. duration of a bounce) shortens with the square root of f.

    So far, so good, but is this model realistic? I did a few experiments tracing bouncing ball trajectories from video.

    First, a tennis ball (at 50 fps):

    Doing rough calculations based on the pixel positions of the ball's center, the behavior is close enough to the model, with a COR of roundabout 0.55. Great.

     

    Second, a very squishy rubber ball:

    Surprise: The same calculations show that this ball keeps bouncing a bit higher than expected every time! The COR raises from 0.34 to 0.55 over four bounces. I even repeated the experiment, with similar results. Apparently, a non-constant COR is not unusual at slow speeds, as mentioned e.g. in the Wikipedia article on the subject.

     

  • New demo

    Added a new entry in the Demo section, showing how to do articulated figures in processing. Check it out: Articulated Structures Animation

  • Processing(JS)

    Recently, I dusted off my copy of "The Computational Beauty of Nature" and started rediscovering this still wonderful book. The chapter about IFS fractals inspired me to do some experimenting with animated fractal shapes. An opportune moment to learn more about Processing! After playing with it for a few hours, I have to say, this is a wonderful programming environment for this kind of visual experiments. Virtually no boilerplate code, cumbersome project setup, etc. Just start hacking away on your ideas. Based on Java, it is not a toy language either, so all the usual data structures and OOP constructs are readily available.

    An additional treat: With processing.js, it is quite easy to run a processing application in a browser. See the IFS animation demo in the new demo section on the left (hope to add more to that category soon :) ). Support is not complete, though: I had to rewrite my demo to some extent, because processing.js doesn´t support the PShape class very well yet, which is quite essential to get good performance in a particle system demo... so I reduced the visuals a bit. Still, much easier than having to go through and translate everything to JavaScript myself!