I tried implementing this behavior using normal steering arrival behavior, but it was too finicky and the effect wasn't right, so I am now trying to use a decaying sine or cosine wave to encode the enemy's distance to their destination.
In particular, I am trying to create a parametrized decaying sine or cosine wave so that arriving enemies overshoot their destination slightly before returning to it, without additional oscillation.
Ideally, I'd like for the first peak of the sine or cosine wave to be the full amplitude and for it to take one additional period for the wave to decay sufficiently close to 0 (say, within some epsilon).
The ideal curve looks something like:
I have looked at some articles on decaying trig waves (for example, https://en.wikipedia.org/wiki/Damping and https://www.calculushowto.com/damped-sine-wave/) and know that there's an infinite number of ways to specify decay in a parametrized curve.
For example, the equation for exponentially decaying cosine wave is the following:
Code: Select all
y(t) = amplitude * e^(-lambda * t) * cos(frequency * t + initial angle offset)
I can't seem to fathom how you'd specify that it'd decay over exactly one additional period, however.
It would also be nice to be able to specify the amplitude of the decayed peak, so that you can granularly control the overshoot distance proportional to the full amplitude. It would also be nice to be able to specify that the resolution of the overshoot happens faster than the initial period (for example takes 2 * initial period, rather than the full period).
Does anyone have any tips or tricks for implementing this kind of curve in order to approximate overshoot movement?
Are there other ways to implement overshoot and return that you've found look appealing for enemy arrival? For example, does anyone know how it was done in popular shmups by Cave or Raizing?
I know that certain Tween libraries like http://dotween.demigiant.com/documentation.php have ease types that represent overshoot, but I'd like to implement it myself for more control & flexibility.
I tried the following, gradually increasing amplitude by a certain step once the curve reaches 0 (which happens at PI/2), as well as decreasing speed once the curve reaches the nadir of -1 (which happens at PI). But the results weren't very good. The overshoot does resolve, but not in a smooth way. Rather, the enemy quickly stops when it reaches its final position.
Code: Select all
private IEnumerator MoveAlongCurve()
{
// walk through curve using parametrized form, w/ parameter t
while (t < Mathf.PI/2)
{
t += Time.deltaTime * speed;
pos_on_curve.x = origin.x;
pos_on_curve.y = origin.y + y_func(t * 2 * Mathf.PI) * amplitude;
transform.position = pos_on_curve;
yield return new WaitForEndOfFrame(); // wait for end of frame
}
while (t >= Mathf.PI / 2)
{
if (t >= Mathf.PI)
{
speed = 0.5f;
}
amplitude -= 0.25f;
amplitude = Mathf.Clamp(amplitude, 0, float.PositiveInfinity);
t += Time.deltaTime * speed;
pos_on_curve.x = origin.x;
pos_on_curve.y = origin.y + Mathf.Cos(t * 2 * Mathf.PI) * amplitude;
transform.position = pos_on_curve;
yield return new WaitForEndOfFrame(); // wait for end of frame
}
}