using System;
namespace Utility
{
///
/// A delegate used by Interpolators to scale their progress and generate their current value.
///
/// The current progress of the Interpolator in the range [0, 1].
/// A value representing the scaled progress used to generate the Interpolator's Value.
public delegate float InterpolatorScaleDelegate(float progress);
public sealed class Interpolator
{
private static readonly Pool interpolators = new Pool(10, i => i.valid)
{
// Initialize is invoked whenever we get an instance through New()
Initialize = i =>
{
i.valid = true;
i.progress = 0f;
},
// Deinitialize is invoked whenever an object is reclaimed during CleanUp()
Deinitialize = i =>
{
i.Tag = null;
i.scale = null;
i.step = null;
i.completed = null;
}
};
private bool valid;
private float progress;
private float start;
private float end;
private float range;
private float speed;
private float value;
private InterpolatorScaleDelegate scale;
private Action step;
private Action completed;
///
/// Gets the interpolator's progress in the range of [0, 1].
///
public float Progress { get { return progress; } }
///
/// Gets the interpolator's starting value.
///
public float Start { get { return start; } }
///
/// Gets the interpolator's ending value.
///
public float End { get { return end; } }
///
/// Gets the interpolator's current value.
///
public float Value { get { return value; } }
///
/// Gets or sets some extra data to the timer.
///
public object Tag { get; set; }
///
/// Creates a new Interpolator.
///
/// The starting value.
/// The ending value.
/// The length of time, in seconds, to perform the interpolation.
/// An optional callback to invoke when the Interpolator is updated.
/// An optional callback to invoke when the Interpolator completes.
/// The Interpolator instance.
public static Interpolator Create(
float start,
float end,
float length,
Action step,
Action completed)
{
return Create(start, end, length, InterpolatorScales.Linear, step, completed);
}
///
/// Creates a new Interpolator.
///
/// The starting value.
/// The ending value.
/// The length of time, in seconds, to perform the interpolation.
/// A method to perform
/// An optional callback to invoke when the Interpolator is updated.
/// An optional callback to invoke when the Interpolator completes.
/// The Interpolator instance.
public static Interpolator Create(
float start,
float end,
float length,
InterpolatorScaleDelegate scale,
Action step,
Action completed)
{
if (length <= 0f)
throw new ArgumentException("length must be greater than zero");
if (scale == null)
throw new ArgumentNullException("scale");
Interpolator i = interpolators.New();
i.start = start;
i.end = end;
i.range = end - start;
i.step = step;
i.completed = completed;
i.scale = scale;
i.speed = 1f / length;
return i;
}
///
/// Stops the Interpolator.
///
public void Stop()
{
valid = false;
}
///
/// Updates all the Interpolators.
///
/// The elapsed time (in seconds) to advance the timers. Generally you want to pass in (float)gameTime.ElapsedGameTime.TotalSeconds from your main Game class.
public static void Update(float dt)
{
for (int i = 0; i < interpolators.ValidCount; i++)
{
Interpolator p = interpolators[i];
// if Stop was called, the interpolator may already be invalid, so we
// make sure to skip those interpolators.
if (!p.valid)
continue;
// update the progress, clamping at 1f
p.progress = Math.Min(p.progress + p.speed * dt, 1f);
// get the scaled progress and use that to generate the value
float scaledProgress = p.scale(p.progress);
p.value = p.start + p.range * scaledProgress;
// invoke the step callback
if (p.step != null)
p.step(p);
// if the progress is 1...
if (p.progress == 1f)
{
// the interpolator is done
p.valid = false;
// invoke the completed callback
if (p.completed != null)
p.completed(p);
}
}
// clean up the invalid interpolators
interpolators.CleanUp();
}
}
}