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(); } } }