Search Unity

SmoothDamp problem?

Discussion in 'Scripting' started by SpookyCat, Aug 5, 2011.

  1. SpookyCat

    SpookyCat

    Joined:
    Jan 25, 2010
    Posts:
    3,764
    Hi All
    I am using Mathf.SmoothDamp to control a camera via mouse movement, everything works great and the camera moves around nicely and if I let go of the camera control key the camera comes to a smooth stop, but only if the camera was moving in a positive direction when the key was released, if it was moving in a negative direction, ie currentVelocity param of SmootDamp is < 0.0f then the camera just stops suddenly. Am I using SmoothDamp incorrectly?

    Code (csharp):
    1. x = Mathf.SmoothDampAngle(x, nx, ref vx, delay);
    In the line above if vx is +ve when the value of nx is not updated the x value will smoothly move towards nx as expected, if the value of vx is -ve when then the value of x will snap instantly to the value in nx.

    Any ideas?
    Chris
     
  2. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    Could you post the rest of the code for this? I understand your troubles, but need to be able to test it.
     
  3. SpookyCat

    SpookyCat

    Joined:
    Jan 25, 2010
    Posts:
    3,764
    Thanks for the interest :) A very simple class that shows the behaviour is below, put it on a camera and when you hold the left mouse button and drag you will see if you drag to the right the camera does a nice damp move but drag to the left and it stops suddenly.
    Code (csharp):
    1. public class MyCamera : MonoBehaviour
    2. {
    3.     float nx = 0.0f;
    4.     float ny = 0.0f;
    5.     float x = 0.0f;
    6.     float y = 0.0f;
    7.     float vx = 0.0f;
    8.     float vy = 0.0f;
    9.  
    10.     void LateUpdate()
    11.     {
    12.         if ( Input.GetMouseButton(0) )
    13.         {
    14.             float mx = Input.GetAxis("Mouse X");
    15.             float my = Input.GetAxis("Mouse Y");
    16.  
    17.             nx = x - (my * 50.0f);
    18.             ny = y + (mx * 50.0f);
    19.             x = Mathf.SmoothDampAngle(x, nx, ref vx, 1.0f);
    20.             y = Mathf.SmoothDampAngle(y, ny, ref vy, 1.0f);
    21.  
    22.             transform.rotation = Quaternion.Euler(x, y, 0.0f);
    23.         }
    24.     }
    25. }
    26.  
     
  4. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    I am sure this is not what you want. The best I can see is that you are trying to mimic a Mouse Orbit and damping it.

    To get SmoothDampAngle to work at all, I had to revert back to the documentation:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class MyCamera : MonoBehaviour
    6. {
    7.     public Transform target;
    8.     public float smooth = 0.3F;
    9.     public float distance = 5.0F;
    10.     private float xVelocity = 0.0F;
    11.     private float yVelocity = 0.0F;
    12.    
    13.     void Start(){
    14.         Vector3 position = target.position;
    15.         position += Quaternion.Euler(0, 0, 0) * new Vector3(0, 0, -distance);
    16.         transform.position = position;
    17.     }
    18.    
    19.     void Update() {
    20.         if(Input.GetMouseButton(0)){
    21.             float xAngle = Mathf.SmoothDampAngle(transform.eulerAngles.x, target.eulerAngles.x, ref xVelocity, smooth);
    22.             float yAngle = Mathf.SmoothDampAngle(transform.eulerAngles.y, target.eulerAngles.y, ref yVelocity, smooth);
    23.             Vector3 position = target.position;
    24.             position += Quaternion.Euler(xAngle, yAngle, 0) * new Vector3(0, 0, -distance);
    25.             transform.position = position;
    26.             transform.LookAt(target);
    27.         } else {
    28.             xVelocity = 0.0F;
    29.             yVelocity = 0.0F;
    30.         }
    31.     }
    32. }
    33.  
    This by its self does not move the camera, but more centers it behind an object. This peice of code I did for someone on the forum here and it will rotate and object.
    Code (csharp):
    1.  
    2. var timeScale=1;
    3.  
    4. function Update () {
    5.     LookAround();
    6.    
    7.     timeScale=1;
    8.     if(Input.GetKey("space")) timeScale=0.2;
    9.     Time.timeScale=Mathf.Lerp(Time.timeScale, timeScale, 4 * Time.deltaTime);
    10. }
    11.  
    12.  
    13. function LookAround() {
    14.     var x = Input.GetAxis ("Mouse X");
    15.     var y = -Input.GetAxis ("Mouse Y");
    16.    
    17.     transform.Rotate (y * 4, x * 4, 0);
    18.     //x=transform.localEulerAngles.y;
    19.     //if(x>180) x-=360;
    20.     //x=Mathf.Clamp(x, -80, 80);
    21.     //transform.localEulerAngles.y=x;
    22.     transform.localEulerAngles.z=0;
    23. }
    24.  
    Now, having a look at the concept from a MouseOrbit standpoint... I believe changes to it will help alot more than working on it in this state:
    Code (csharp):
    1.  
    2. var target : Transform;
    3. var distance = 10.0;
    4.  
    5. var useMouse=true;
    6. var damping=4.0;
    7.  
    8. var xSpeed = 250.0;
    9. var ySpeed = 120.0;
    10.  
    11. var yMinLimit = -20;
    12. var yMaxLimit = 80;
    13.  
    14. private var x = 0.0;
    15. private var y = 0.0;
    16. private var targetPosition : Vector3=Vector3.zero;
    17.  
    18. function Start () {
    19.     var angles = transform.eulerAngles;
    20.     x = angles.y;
    21.     y = angles.x;
    22.  
    23.     // Make the rigid body not change rotation
    24.     if (rigidbody)
    25.         rigidbody.freezeRotation = true;
    26.     if(!target)targetPosition=transform.position;
    27. }
    28.  
    29. function LateUpdate () {
    30.     var targ=targetPosition;
    31.     if (target) targ=target.position;
    32.    
    33.     if(Input.GetMouseButton(0) || !useMouse){
    34.         x += Input.GetAxis("Mouse X") * xSpeed * 0.02;
    35.         y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02;
    36.        
    37.         y = ClampAngle(y, yMinLimit, yMaxLimit);
    38.     }
    39.    
    40.     var rotation = Quaternion.Euler(y, x, 0);
    41.     rotation=Quaternion.Slerp(transform.rotation, rotation, damping * Time.deltaTime);
    42.     var position = rotation * Vector3(0.0, 0.0, -distance) + targ;
    43.    
    44.     transform.rotation = rotation;
    45.     transform.position = position;
    46. }
    47.  
    48. static function ClampAngle (angle : float, min : float, max : float) {
    49.     if (angle < -360)
    50.         angle += 360;
    51.     if (angle > 360)
    52.         angle -= 360;
    53.     return Mathf.Clamp (angle, min, max);
    54. }
    55.  
    Hope it helps
     
  5. SpookyCat

    SpookyCat

    Joined:
    Jan 25, 2010
    Posts:
    3,764
    Many thanks BigMisterB for the comprehensive and detailed reply, really is very kind of you to take the time to put such a full answer together. Your solution looks pretty much like what I have for the rest of my orbit code and it all works very nicely, but I am just at a loss still as to why SmoothDamp behaves oddly. Just wondering if a bug report is needed but it seems that it cant be a bug as surely someone else would have spotted the behavior in the years gone by, so I am left wondering if SmoothDamp just isnt designed o work with negative velocities.

    Thanks again for your time :)
    Chris
     
  6. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    I would not believe that it is a bug though. There are a couple of things that you are not doing in your script. Like clearing vx and vy. When you stop, you need to release all of the momentum.

    I also do not believe it is a bug since I actually got the SmoothDampAngle to work properly, I was very unhappy with the results though.
     
  7. Groucho

    Groucho

    Joined:
    Dec 24, 2009
    Posts:
    73
    Chris-

    I'm seeing a problem with SmoothDamp too. I've recently upgraded to 3.4, but I'm not sure if this is the problem yet. I use SmoothDamp to control the camera rotation in a racing game I'm working on for iOS. If I shake the device from side to side (causing my inputX variable to go from positive to negative quickly), SmoothDamp returns NaN the camera dies. I didn't notice this problem with Unity 3.3.

    Here's the code I use to set the camera's rotation.
    Code (csharp):
    1.  
    2. //this is JavaScript code
    3. //cameraRotation is the Z rotation of the camera
    4. //inputX is the raw accelerometer input value
    5. //cameraBankScale  inputMaxX are scale factors for the inputX variable
    6. //cameraRotationDamping is the time delay for SmoothDamp
    7.     cameraRotation = Mathf.SmoothDamp(cameraRotation, inputX * cameraBankScale/inputMaxX, inputRotationVelocity, cameraRotationDamping);
    8.     Camera.main.transform.localEulerAngles.z = cameraRotation;
     
  8. Groucho

    Groucho

    Joined:
    Dec 24, 2009
    Posts:
    73
    I found my NaN problem and it wasn't SmoothDamp. My inputX variable above wasn't actually the raw accelerometer input. Instead I was doing a Mathf.Asin on it to return a rotation angle of the phone. I didn't bound the inputs of the Asin input to +/-1. When the accelerometer input was greater to 1 or less than -1, Asin returns a NaN (since arcsin of anything out of those bounds isn't a real number).

    I didn't run into this problem in Unity3.3 and it happens all the time now, so it may be possible that the new UnityScript compiler doesn't check for an invalid input range on Asin. However, it's more likely that I just wasn't looking for it. Either way, lesson learned -> don't feed a function an input value that doesn't make sense!

    Have fun,
     
  9. owen_proto

    owen_proto

    Joined:
    Mar 18, 2018
    Posts:
    118
    Sorry for digging up an old thread, but everywhere I've searched and found this issue it has been left unresolved.

    I'm having the same problem as SpookyCat where a SmoothDamp or SmoothDampAngle in a negative direction does not result in a smooth stop with mouse axis/raw inputs.
     
  10. PanicEnsues

    PanicEnsues

    Joined:
    Jul 17, 2014
    Posts:
    187
  11. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    SmoothDampe Angle worked fine in my old project last year, when I implemented the same code again, recently the negative angle are not damping at all
     
  12. PanicEnsues

    PanicEnsues

    Joined:
    Jul 17, 2014
    Posts:
    187
    I wound up rolling my own copy of the SmoothDamp/SmoothDampAngle code to disable the "feature" that causes the problem:

    Code (CSharp):
    1.     #region smoothDamp
    2.     //Copied directly from Unity source, but removes their "Prevent Overshooting" code,
    3.     //which only works on negative velocities (snaps them to an instant halt, which I don't want)
    4.     public static float SmoothDampAngle(float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed)
    5.     {
    6.         float deltaTime = Time.deltaTime;
    7.         return SmoothDampAngle(current, target, ref currentVelocity, smoothTime, maxSpeed, deltaTime);
    8.     }
    9.  
    10.     // Gradually changes an angle given in degrees towards a desired goal angle over time.
    11.     public static float SmoothDampAngle(float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed, float deltaTime)
    12.     {
    13.         target = current + Mathf.DeltaAngle(current, target);
    14.         return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, deltaTime);
    15.     }
    16.  
    17.     // Gradually changes a value towards a desired goal over time.
    18.     public static float SmoothDamp(float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed, float deltaTime)
    19.     {
    20.         // Based on Game Programming Gems 4 Chapter 1.10
    21.         smoothTime = Mathf.Max(0.0001F, smoothTime);
    22.         float omega = 2F / smoothTime;
    23.  
    24.         float x = omega * deltaTime;
    25.         float exp = 1F / (1F + x + 0.48F * x * x + 0.235F * x * x * x);
    26.         float change = current - target;
    27.         //float originalTo = target;
    28.  
    29.         // Clamp maximum speed
    30.         float maxChange = maxSpeed * smoothTime;
    31.         change = Mathf.Clamp(change, -maxChange, maxChange);
    32.         target = current - change;
    33.  
    34.         float temp = (currentVelocity + omega * change) * deltaTime;
    35.         currentVelocity = (currentVelocity - omega * temp) * exp;
    36.  
    37.         float output = target + (change + temp) * exp;
    38.  
    39.         //// Prevent overshooting
    40.         //if (originalTo - current > 0.0F == output > originalTo)
    41.         //{
    42.         //    output = originalTo;
    43.         //    currentVelocity = (output - originalTo) / deltaTime;
    44.         //}
    45.  
    46.         return output;
    47.     }
    48.     #endregion
     
  13. IAmJustADog

    IAmJustADog

    Joined:
    Aug 9, 2013
    Posts:
    8
    Instead of removing the overshoot protection, I modified it. For me, this code works fine with negative velocity:

    Code (CSharp):
    1.  
    2. public static float SmoothDamp(float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed, float deltaTime)
    3. {
    4.    // Based on Game Programming Gems 4 Chapter 1.10
    5.    smoothTime = Mathf.Max(0.0001F, smoothTime);
    6.    float omega = 2f / smoothTime;
    7.    float x = omega * deltaTime;
    8.    float exp = 1F / (1F + x + 0.48F * x * x + 0.235F * x * x * x);
    9.    float change = current - target;
    10.    // Clamp maximum speed
    11.    float maxChange = maxSpeed * smoothTime;
    12.    change = Mathf.Clamp(change, -maxChange, maxChange);
    13.    float temp = (currentVelocity + omega * change) * deltaTime;
    14.    currentVelocity = (currentVelocity - omega * temp) * exp;
    15.    float output = current - change + (change + temp) * exp;
    16.    
    17.    // Prevent overshooting
    18.    if ((target - current) * (output - target) > Zero)
    19.    //if (target - current > 0.0F == output > target) <- old
    20.    {
    21.       output = target;
    22.       currentVelocity = (output - target) / deltaTime;
    23.    }
    24.    return output;
    25. }
    26.  
     
    Timurio, jiraphatK and PanicEnsues like this.