Re-Animating Animations

When the player is hovering over an object, we want it to glow or rise or scale up or whatever. We’ll use the glow from here on but it could be applied to any animation. So if the player’s looking at that object then start the glow fade in animation. If the object is glowing and the player isn’t looking at it anymore, play the glow fade out animation.

If the player’s just passing over this object and not trying to focus it then it’ll play the fad in animation and then when that stops immediately play the fade out.

Well if the player stops looking at the object let’s cut the fade in animation and play the fade out animation. Now all of our object flash which isn’t good either.

So let’s hack our animation to allow us to change the start value of the glow at runtime. Hooray, our objects don’t flash!

But our objects don’t fade out at the same rate.

Since we’re already passing the start value at runtime, we can use this to contract the animation duration.

Now, if the player glances away from an object, we have this problem in reverse. So now we can double the number of lines of code on exactly the same problem.

Let’s also use this same code for a hint in our tutorial. But now our object is fading in twice, once for the hint and one for player’s gaze.

We could just skip the start animation during the tutorial. But we have this system for passing the start value through to the animation.

Now the tutorial needs the glow fade in value, so let’s just make that static. Brilliant.

Aren’t we smart.

No.

Ctrl + A, Delete.

Starting over. We can give our glow some states that we can manipulate. These states manipulate the object’s glow. Intuitively we have four states: Fading in, Fading out, Active, and Inactive. Then every render game loop update we can use these states to drive the amount of glow we have.

We’ll define some values, we’ll have a CurrentTime, a Duration and a CurrentState. The CurrentTime will go between 0 and 1 and the Duration is a constant time in seconds, while the CurrentState stores the state of the animation.

 
enum AnimationState {
    FadeIn,
    Active,
    FadeOut,
    Hidden
}
float CurrentTime;
const float Duration = 5.f;

AnimationState CurrentState;
 

First, let’s translate the render delta from real seconds into our animation time. This is quite simply dividing the render delta by the animation duration. We’ll use this modified delta from now on. Something to note is that this works whether the duration is greater than or less than one. The only case where it doesn’t work is for a instant animation. In this case we can hard code our delta to some constant value if the duration is zero to prevent mathematical errors.

 
void UpdateAnimation(float RenderDeltaTime)
{
    float ModifiedDelta = 2.f;
    if(Duration != 0.f)
    {
        ModifiedDelta = RenderDeltaTime / Duration;
    }
}
 

Next we use this modified delta to increment or decrement the CurrentTime based on the current state. This is the main part of this state based algorithm.

 
void UpdateAnimation(float RenderDeltaTime)
{
    float ModifiedDelta = 2.f;
    if(Duration != 0.f)
    {
        ModifiedDelta = RenderDeltaTime / Duration;
    }

    if(CurrentState == AnimationState::FadeIn)
    {
        CurrentTime += ModifiedDelta;
    }
    else if(CurrentState == AnimationState::FadeOut)
    {
        CurrentTime -= ModifiedDelta;
    }
} 

There are some edge cases, two to be precise, zero and one. We can handle these quite simply by clamping the time and changing the state. After we’ve clamped the CurrentTime, it’s now, proportionally, how far through the animation we are.

 
float UpdateAnimation(float RenderDeltaTime)
{
    float ModifiedDelta = 2.f;
    if(Duration != 0.f)
    {
        ModifiedDelta = RenderDeltaTime / Duration;
    }

    if(CurrentState == AnimationState::FadeIn)
    {
        CurrentTime += ModifiedDelta;
    }
    else if(CurrentState == AnimationState::FadeOut)
    {
        CurrentTime -= ModifiedDelta;
    }

    if(CurrentTime > 1.f)
    {
        CurrentTime = 1.f;
        CurrentState = AnimationState::Active;
    }
    else if(CurrentTime < 0.f)
    {
        CurrentTime = 0.f;
        CurrentState = AnimationState::Hidden;
    }

    return CurrentTime;
} 

Most likely your animations aren’t going to be linear. If you have animation curves, simply plug the number in. If not, there’s a whole bunch of mathematical functions you can apply to get the normal easing values.

Now, when we’re looking at an object, we can set the object’s glow state to FadeIn, if we look away we can set the glow state to FadeOut. Importantly, the state can be set and the object’s glow will sort itself out without any strange behaviour. If we pass over an object quickly, the CurrentTime doesn’t get very high before we start fading out again. In the tutorial example, we set the object’s glow to FadeIn after it’s already Active, the edge case gets it and keeps the CurrentTime at one.