using System; namespace Interpolation { public class Interpolator { // allocate a pool of interpolators. the parameters: // 1) we have a maximum number of interpolators at 1000. // 2) we don't allocate any instances up front (i.e. we'll create instances as we need them) // 3) our validation delegate to tell the pool when an object is considered valid // 4) a custom allocation delegate. we will just let the pool create the interpolators using // their parameterless constructor. this works even though the Interpolator constructor is // private. private static readonly Pool interpolators = new Pool(1000, 0, i => i.valid, null) { // an initialize delegate to set the values we know will always // be constant when we want a new Interpolator out of the pool Initialize = i => { i.valid = true; i.timer = 0f; }, // a deinitialize delegate to make sure we release any references Deinitialize = i => { i.valueChanged = null; i.completedOrStopped = null; i.Tag = null; } }; #if DEBUG // we use these to detect changes in allocation and active count for printing debug information private static int lastAllocationCount; private static int lastActiveCount; #endif private bool valid; private Action valueChanged; private Action completedOrStopped; private float timer; /// /// Gets the starting value of the interpolator. /// public float Start { get; private set; } /// /// Gets the ending value of the interpolator. /// public float End { get; private set; } /// /// Gets the current value of the interpolator. /// public float Value { get; private set; } /// /// Gets the rate of change of the interpolator in units per second. /// public float Speed { get; private set; } /// /// Gets the amount of time the interpolator will spend to complete. /// public float Length { get; private set; } /// /// Gets or sets an extra object to associate with the Interpolator. /// public object Tag { get; set; } private Interpolator() { } /// /// Stops the interpolator. /// public void Stop() { valid = false; if (completedOrStopped != null) completedOrStopped(this); } /// /// Creates a new Interpolator that takes a given amount of time to interpolate. /// /// The starting value. /// The ending value. /// The amount of time (in seconds) to take interpolating. /// An action to invoke when the interpolator's value changes. /// An action to invoke when the interpolator finishes interpolating or is stopped. /// A new Interpolator instance. public static Interpolator CreateWithTime( float start, float end, float time, Action valueChanged, Action completedOrStopped) { return create(start, end, time, (end - start) / time, valueChanged, completedOrStopped); } /// /// Creates a new Interpolator that interpolates at a given speed. /// /// The starting value. /// The ending value. /// The rate of change at which to interpolate in units per second. /// An action to invoke when the interpolator's value changes. /// An action to invoke when the interpolator finishes interpolating or is stopped. /// A new Interpolator instance. public static Interpolator CreateWithSpeed( float start, float end, float speed, Action valueChanged, Action completedOrStopped) { return create(start, end, Math.Abs(end - start) / speed, speed, valueChanged, completedOrStopped); } // a private method that our public Create_ methods will call private static Interpolator create( float start, float end, float length, float speed, Action valueChanged, Action completedOrStopped) { Interpolator interpolator = interpolators.New(); if (interpolator == null) throw new InvalidOperationException("Too many interpolators active. Interpolator count capped at 1000."); interpolator.Value = interpolator.Start = start; interpolator.End = end; interpolator.Length = length; interpolator.Speed = speed; interpolator.valueChanged = valueChanged; interpolator.completedOrStopped = completedOrStopped; return interpolator; } /// /// Updates all active interpolators. /// /// The current time step. public static void Update(float dt) { #if DEBUG // for a debug feature of the demo, we write out the number of active // interpolators so we can make sure the pool is working properly if (lastAllocationCount != interpolators.AllocationSize) { lastAllocationCount = interpolators.AllocationSize; Console.WriteLine("Interpolators Allocated: {0}", lastAllocationCount); } if (lastActiveCount != interpolators.ValidCount) { lastActiveCount = interpolators.ValidCount; Console.WriteLine("Interpolators Active: {0}", lastActiveCount); } #endif for (int k = 0; k < interpolators.ValidCount; k++) { Interpolator i = interpolators[k]; // if the interpolator is already invalid, skip over it if (!i.valid) continue; // increase the timer and value i.timer += dt; i.Value += i.Speed * dt; // see if the interpolator is done if (i.timer >= i.Length) i.valid = false; // if the interpolator is no longer valid make sure the value is the desired end value if (!i.valid) i.Value = i.End; // then fire off the value changed action if (i.valueChanged != null) i.valueChanged(i); // if the interpolator is done, fire off the completed action if (!i.valid && i.completedOrStopped != null) i.completedOrStopped(i); } // clean up the pool interpolators.CleanUp(); } } }