Formula behind SmoothDamp
This question is somewhat unorthodox, I know, but please let me ask it anyway:
What's the formula (or method used) behind Mathf.SmoothDamp()?
I'd totally understand when you guys tell me that it is top secret. The thing is that I need to have something similar outside of Unity and just can't get behind that. I'm not lazy or anything, I'm not asking for finished and working code. I'd only like to get a hint into a useful direction.
@Whimsical ... How about a little something for the effort?
Answer by Magnus Wolffelt · Aug 11, 2010 at 08:48 PM
Good question.
While I don't know the exact formula inside that function, you can achieve similar results by a partial Lerp (implement your own if outside unity) every timestep, and save the value of the Lerp:
myValue = Mathf.Lerp(myValue, targetValue, 0.1f);
You should be aware however, that this is not timestep size independent, so if you change the timestep size, you will get different behaviour. Putting the delta time in the t parameter kind of reduces this change, but it will still change the behaviour if you change timestep size.
There is a correct (and slightly complex) way to implement this kind of smoothing to get the same behaviour independent of step size, but I don't have it at hand, and my math skills aren't that great.. :) I don't know if Unity implements this correctly or not. It is somewhat counter-intuitive that putting deltatime in the t parameter doesn't solve it, so people usually miss this.
This is a nice start, thanks! But unfortunately this will work only when not changing the "direction" of the target. An example: If your target value is lower than the current value it will gradually dampen towards the target just fine, but when you now change the target abruptly to something larger than the current value, this function will produce a "hard turn" towards the new higher target. What I mean is that your function will work perfectly fine for dampening a value towards a target, but is completely missing the spring-like behavior. I guess that's what the velocity param is there for.
Ah, yes.. good point. Then what you want is basically a PD controller with feed-forward velocity and a velocity clamp, I think.
Wow, this PID controller thing looks complicated. I give it a shot and as soon as I can confirm that this is what I need I'll set you the tick for a right answer. Thanks for now! :)
Hm. I gave the PID controller a shot and it didn't work out. I still think that UT uses another kind of procedure. And I still want to know what they use, because the motion that this thing produces is, well, perfect and feels best of everything I tested recently.
Answer by koirat · Aug 30, 2012 at 06:39 PM
In Unity 3.5 MonoDevelop you can jump into definition of Methods. For example SmoothDump:
using System;
public static float SmoothDamp (float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed, float deltaTime)
{
smoothTime = Mathf.Max (0.0001f, smoothTime);
float num = 2f / smoothTime;
float num2 = num * deltaTime;
float num3 = 1f / (1f + num2 + 0.48f * num2 * num2 + 0.235f * num2 * num2 * num2);
float num4 = current - target;
float num5 = target;
float num6 = maxSpeed * smoothTime;
num4 = Mathf.Clamp (num4, -num6, num6);
target = current - num4;
float num7 = (currentVelocity + num * num4) * deltaTime;
currentVelocity = (currentVelocity - num * num7) * num3;
float num8 = target + (num4 + num7) * num3;
if (num5 - current > 0f == num8 > num5)
{
num8 = num5;
currentVelocity = (num8 - num5) / deltaTime;
}
return num8;
}
The recently released Unity source code also contains a comment which references the original source for these equations as // Based on Game Program$$anonymous$$g Gems 4 Chapter 1.10
so you can look up the explanation in that textbook.
Link to source code: https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/$$anonymous$$ath/$$anonymous$$athf.cs
Answer by Cyb3rManiak · Aug 11, 2010 at 10:11 PM
If you want something simpler try checking out the "Weighted Average" part in this very useful page: http://sol.gfxile.net/interpolation/index.html
I don't imagine this is what Unity uses, but I think it may get the job done in many cases.
Answer by Wolfram · Aug 12, 2010 at 10:42 AM
You could add another level of smoothing to Magnus Wolffelt's suggestion:
Directly setting the target value: no smoothing, D0 discontinuity (=jump in position f(x)):
myValue=targetValue;
Using simple smoothing: D1 discontinuity (=jump in velocity f'(x), suddenly changing direction):
myValue = Mathf.Lerp(myValue, targetValue, 0.1f);
Using two-level smoothing: D2 discontinuity (=jump in acceleration f''(x) (which is not noticeable, since nature does the same all the time), resulting in linearly changing velocity and therefore smoothly changing position):
myIntermediate = Mathf.Lerp(myIntermediate, targetValue, 0.1f);
myValue = Mathf.Lerp(myValue, myIntermediate, 0.1f);
The two-level smoothing will also result automatically in a higher-order ease-in and ease-out, due to the acceleration effect.
This is somehow related to Bzier curves, maybe you can also get some ideas from there.