using System; namespace Otter { /// /// Component that will slowly interpolate a value toward a target using a speed and acceleration. /// This component can move the value and does not know about time at all. /// public class Lerper : Component { #region Private Fields float initValue, targetSpeed, speed, distance; bool endPhase = false; #endregion #region Public Fields /// /// The acceleration for moving toward the target. /// public float Acceleration; /// /// The maximum speed for moving toward the target. /// public float MaxSpeed; #endregion #region Public Properties /// /// If the Lerper has completed its movement. /// public bool Completed { get; private set; } /// /// The current target to move toward. /// public float Target { get; private set; } /// /// The current value. /// public float Value { get; private set; } #endregion #region Constructors /// /// Create a new Lerper. /// /// The initial value. /// The acceleration for moving toward the target. /// The max speed for moving toward the target. public Lerper(float value, float accel, float maxSpeed) { initValue = value; Value = value; Acceleration = accel; MaxSpeed = maxSpeed; Completed = false; } #endregion #region Public Methods /// /// Set the target. /// /// public void SetTarget(float value) { if (Target == value) return; Target = value; initValue = Value; endPhase = false; Completed = false; distance = Math.Abs(initValue - Target); } /// /// Force the current value. /// /// public void SetValue(float value) { Value = value; } /// /// Update the Lerper. /// public override void Update() { base.Update(); if (Completed) { Value = Target; return; } var currentDistance = Math.Abs(Target - Value); var stoppingDistance = (MaxSpeed * MaxSpeed) / (2 * Acceleration); var earlyStopDistance = (speed * speed) / (2 * Acceleration); if (!endPhase) { targetSpeed = MaxSpeed * Math.Sign(Target - Value); if (currentDistance <= stoppingDistance) { if (speed == MaxSpeed) { targetSpeed = 0; endPhase = true; } if (speed < MaxSpeed) { if (currentDistance <= earlyStopDistance) { targetSpeed = 0; endPhase = true; } } } } speed = Util.Approach(speed, targetSpeed, Acceleration); if (endPhase) { if (Target > Value) { if (speed < Acceleration) speed = Acceleration; Value = Util.Approach(Value, Target, speed); } if (Target < Value) { if (speed > -Acceleration) speed = -Acceleration; Value = Util.Approach(Value, Target, -speed); } } else { Value += speed; } if (currentDistance <= Acceleration * 5) { speed = 0; Completed = true; } } #endregion #region Operators public static implicit operator float(Lerper lerper) { return lerper.Value; } public static implicit operator int(Lerper lerper) { return (int)lerper.Value; } #endregion } }