Refactoring the engine 3: animation framework
An animation subsystem is an useful component of a game engine, that can be very handy when writing the game's behavior without having to hand code everything. In this article my engine will get a basic animation framework: not an extremely complex one, but enough to create some nice effects.
Architecture
First of all, the animation framework will be written as an XNA GameComponent and will be completely extensible. Everything depends on this very generic interface:
interface IAnimation {
void Start();
void Update(GameTime gameTime);
event EventHandler Completed;
bool IsRunning { get; }
bool IsCompleted { get; }
TimeSpan Duration {
get;
set;
}
}
We'll have several classes implementing this interface, with different effects and different interpolation methods.
This kind of animation framework is vaguely similar to the one in WPF (I feel ashamed for even comparing them...). In WPF you can instantiate an Animation instance (depending on the data type you are about to animate), hook it up on the right properties, define the parameters (interpolation, duration and so on) and finally run it. In my case there will be no "hooking" onto properties: we'll have a separate class for each animable property of the game, that contains all logic needed to update the animated properties correctly.
The final architecture is going to look like this:

Creating animations and starting them
All animations implementing IAnimation can be run by attaching them to the AnimationComponent. The component will then update all registered and active animations at each frame. The code is really simple:
class AnimationComponent : GameComponent {
C5.IList<IAnimation> _animations = new C5.LinkedList<IAnimation>();
//...
public void AddAnimation(IAnimation animation) {
_animations.Add(animation);
animation.Start();
}
public override void Update(GameTime gameTime) {
foreach (IAnimation a in _animations) {
a.Update(gameTime);
}
base.Update(gameTime);
}
}
Of course there is a bit more code to remove completed animations from the animation queue, but the main parts of the class are really that simple.
All animations will extend the BaseAnimation class, that handles a lot of trivial things like computing the elapsed time from the begin of the animation and raising events (when the animation is completed). The actual work is then done by calling the abstract InternalUpdate(elapsedTime) method.
Interpolation
Once our subclass of BaseAnimation gets called with the amount of elapsed time from the begin of the animation, it must compute the correct effects of the animations. In my everlasting strive for extensibility (!), that logic is enclosed in a particular set of classes, called interpolators, that implement the following interface:
interface IInterpolator {
float ComputeRatio(TimeSpan duration, TimeSpan elapsedTime);
}
Pretty basic: the only method defined above simply computes the right "position" we are in on the whole animation timeline. The timeline - i.e. the animation from one value to another, handled by the underlying IAnimation instance - is linear: this means that if the ComputeRatio() method returns 0.0f the animation will use the initial value, whereas if the value is 1.0f it will use the ending value, while it will scale linearly for all values in between.
This means that the interpolator can actually smooth out the animation without changing how the timeline is handled: by simply returning a non-linear ratio of the animation state (like using the SmoothStep method provided by XNA) the resulting change in the animated value will be smoothed as well.
The basic linear interpolator:
class LinearInterpolator : IInterpolator {
public float ComputeRatio(TimeSpan duration, TimeSpan elapsedTime) {
return MathHelper.Lerp(0.0f, 1.0f, (float)(elapsedTime.TotalSeconds / duration.TotalSeconds));
}
}
The smoothstep interpolator:
class SmoothStepInterpolator : IInterpolator {
public float ComputeRatio(TimeSpan duration, TimeSpan elapsedTime) {
return MathHelper.SmoothStep(0.0f, 1.0f, (float)(elapsedTime.TotalSeconds / duration.TotalSeconds));
}
}
With a little change, we can create an inverse animation (from the final value to the original value):
class SmoothStepInverseInterpolator : IInterpolator {
public float ComputeRatio(TimeSpan duration, TimeSpan elapsedTime) {
return MathHelper.SmoothStep(1.0f, 0.0f, (float)(elapsedTime.TotalSeconds / duration.TotalSeconds));
}
}
And an interesting thing is that we can as well create an interpolator that peaks at 1.0f in the middle of the animation and then goes back to the initial value (this will be used to create a "flash" effect when the player is hit):
class ReverseInterpolator : IInterpolator {
public float ComputeRatio(TimeSpan duration, TimeSpan elapsedTime) {
double halfTime = duration.TotalSeconds / 2.0;
double elaps = elapsedTime.TotalSeconds;
if (elaps >= halfTime)
elaps = duration.TotalSeconds - elaps;
return MathHelper.SmoothStep(0.0f, 1.0f, (float)elaps / (float)halfTime);
}
}
Animating value types
One last problem is: how do we change the values when the InternalUpdate() method is called on out animation class? Generally we'll provide the variables that will be animated to the animation constructor: the instance will keep them and change the values correctly as time passes.
But this doesn't work for value-types. These variables are passed as copies in the constructor call and we can't keep a reference to them, even if we'd simply like to animate a single float value. ![]()
A slightly convoluted solution is to wrap the value inside an object. Looks like a hack, it probably is an ugly hack, but - heh - it works. ![]()
class Holder<T> {
public Holder(T value) {
_value = value;
}
T _value;
public T Value {
get { return _value; }
set { _value = value; }
}
public static implicit operator T(Holder<T> holder) {
return holder.Value;
}
public static implicit operator Holder<T>(T val) {
return new Holder<T>(val);
}
}
This simple wrapper takes a generic T type and wraps it. It also provides an implicit conversion operator that allows us to use the variable as if it was the original type (did I mention that I love C#?
).



