Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How to set an Animation Curve to linear through scripting?

Discussion in 'Scripting' started by bugzilla, Sep 19, 2012.

  1. bugzilla

    bugzilla

    Joined:
    Dec 9, 2008
    Posts:
    196
    I am writing an animation program. In my script when the user taps a button it sets the current keyframe in an AnimationCurve to have a Linear tangent. I have tried several techniques but I do not know the math to set the inTangent and outTangent of the Keyframe to linear. My original idea was to set the inTangent and outTangent of the Keyframe and it's surrounding keys to 0 but this didn't work. Any ideas? Here's what I have scripted so far.

    Code (csharp):
    1. function SetLinear(currenttime:float){
    2.     for(var ti=0;ti<xtranscurve.keys.length;++ti){
    3.     var tx:Keyframe=xtranscurve.keys[ti];
    4.     if(tx.time==currenttime){
    5.         var originalvalue=xtranscurve.keys[ti].value;
    6.         var originaltime=xtranscurve.keys[ti].time;
    7.        
    8.         // Previous Key
    9.         if(ti>0){
    10.         var beforekey:Keyframe=xtranscurve[ti-1];
    11.         beforekey.outTangent=0;
    12.         xtranscurve.MoveKey(ti-1,beforekey);
    13.         }
    14.         // Current Key
    15.         var modkey:Keyframe=xtranscurve[ti];
    16.         modkey.inTangent=0;
    17.         modkey.outTangent=0;
    18.         modkey.tangentMode=2;
    19.         xtranscurve.MoveKey(ti,modkey);
    20.         // Next Key
    21.         if(ti<xtranscurve.keys.length-1){
    22.         var nextkey:Keyframe=xtranscurve[ti+1];
    23.         nextkey.inTangent=0;
    24.         xtranscurve.MoveKey(ti+1,nextkey);
    25.         }
    26.        
    27.        
    28.     }  
    29. }
     
  2. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I really need this for our artists as well. We want to be able to far quicker set keys as desired - adding keyboard shortcuts would be our best preferred way to deal with it, without really clumsy right clicking programmer-designed creative tool (always a deplorable concept).

    do get with the times, chaps!
     
  3. bugzilla

    bugzilla

    Joined:
    Dec 9, 2008
    Posts:
    196
    Let me clarify the code I wrote. The above function does indeed change the shape of the curve. I just do not know the math to consistently make a linear curve between two keyframes. I want the same behavior you can get in the AnimationCurve editor in Unity when you right click on a keyframe and select Linear from the popup menu.
     
  4. bugzilla

    bugzilla

    Joined:
    Dec 9, 2008
    Posts:
    196
    Any ideas anyone? This concept already exists in the Editor, but I need it to work diring runtime....it is such a general concept that I think the whole Unity community would benefit from knowing....
     
  5. Loius

    Loius

    Joined:
    Aug 16, 2012
    Posts:
    546
    Disclaimer: I suck at math. A lot. But I'm decent with logic. I might have the functions or order of variables wrong below >_<

    So, it looks like the Tangent is given in a rotational format -

    That is, a tangent of zero means a horizontal tangent; tangent of 90 is vertical. (dunno if up or down).

    So, for a sequence of linear (point-to-point) tangents, you want each inTangent to point directly at the previous point, and each outTangent to point at the next point.

    You know the width and height of the triangle (time between keyframes, and magnitude (vertical distance between them) ). And you want the angle from left point to right point. That's... um... *SohCahToa* ... let's see...

    tan( angle ) = opposite over adjacent
    sooooo
    angle = cotangent( opposite over adjacent )
    or arctangent, i don't see a cotan function in Mathf and like I said I'm terrible at math.
    The outTangent angle's opposite is probably height (assuming zero degrees is horizontal)

    , so

    Code (csharp):
    1. key.outTangent = Mathf.Atan( height / width ) * radToDegree;
    atan returns radians, so radToDegree should be ( 180 / 3.1415 ).

    So that should point the first outTangent directly at the keyframe x+1. The inTangent of keyframe x+1 might be (-outTangent) or (360-outTangent), depending on how stuff is set up.
     
  6. bugzilla

    bugzilla

    Joined:
    Dec 9, 2008
    Posts:
    196
    Thanks for the input Louis but I could not get it to work. Since the Unity AnimationCurve Editor allows you to easily set the curve type of a Keyframe I don't see why that's not included in the Scripting engine.

    Any ideas out there? Sounds like the kind of thing the whole Unity community could benefit from.
     
  7. eagle555

    eagle555

    Joined:
    Aug 28, 2011
    Posts:
    2,705
    Hello,

    You can do it this way:
    PHP:
    function set_curve_linear(curveAnimationCurve): AnimationCurve
    {
        var 
    curve3AnimationCurve = new AnimationCurve();
        for (var 
    count_keyint 0;count_key curve.keys.Length;++count_key)
        {
            var 
    intangentfloat 0;
            var 
    outtangentfloat 0;
            var 
    intangent_setboolean false;
            var 
    outtangent_setboolean false;
            var 
    point1Vector2;
            var 
    point2Vector2;
            var 
    deltapointVector2;
            var 
    keyKeyframe curve[count_key];
            
            if (
    count_key == 0){intangent 0;intangent_set true;}
            if (
    count_key == curve.keys.Length -1){outtangent 0;outtangent_set true;}
            
            if (!
    intangent_set)
            {
                
    point1.curve.keys[count_key-1].time;
                
    point1.curve.keys[count_key-1].value;
                
    point2.curve.keys[count_key].time;
                
    point2.curve.keys[count_key].value;
                    
                
    deltapoint point2-point1;
                
                
    intangent deltapoint.y/deltapoint.x;
            }
            if (!
    outtangent_set)
            {
                
    point1.curve.keys[count_key].time;
                
    point1.curve.keys[count_key].value;
                
    point2.curve.keys[count_key+1].time;
                
    point2.curve.keys[count_key+1].value;
                    
                
    deltapoint point2-point1;
                    
                
    outtangent deltapoint.y/deltapoint.x;
            }
                    
            
    key.inTangent intangent;
            
    key.outTangent outtangent;
            
    curve3.AddKey(key);
        }
        return 
    curve3;
    }
     
    Westland, mariosge, Laiken and 2 others like this.
  8. bugzilla

    bugzilla

    Joined:
    Dec 9, 2008
    Posts:
    196
    Thanks, eagle. I'll try that out.
     
  9. metroidsnes

    metroidsnes

    Joined:
    Jan 5, 2014
    Posts:
    67
    The code above works just fine, here's a C# version:
    Code (CSharp):
    1.  public static void SetCurveLinear(AnimationCurve curve) {
    2.             for (int i = 0; i < curve.keys.Length; ++i) {
    3.                 float intangent = 0;
    4.                 float outtangent = 0;
    5.                 bool intangent_set = false;
    6.                 bool outtangent_set = false;
    7.                 Vector2 point1;
    8.                 Vector2 point2;
    9.                 Vector2 deltapoint;
    10.                 Keyframe key = curve[i];
    11.  
    12.                 if (i == 0) {
    13.                     intangent = 0; intangent_set = true;
    14.                 }
    15.  
    16.                 if (i == curve.keys.Length - 1) {
    17.                     outtangent = 0; outtangent_set = true;
    18.                 }
    19.  
    20.                 if (!intangent_set) {
    21.                     point1.x = curve.keys[i - 1].time;
    22.                     point1.y = curve.keys[i - 1].value;
    23.                     point2.x = curve.keys[i].time;
    24.                     point2.y = curve.keys[i].value;
    25.  
    26.                     deltapoint = point2 - point1;
    27.  
    28.                     intangent = deltapoint.y / deltapoint.x;
    29.                 }
    30.                 if (!outtangent_set) {
    31.                     point1.x = curve.keys[i].time;
    32.                     point1.y = curve.keys[i].value;
    33.                     point2.x = curve.keys[i + 1].time;
    34.                     point2.y = curve.keys[i + 1].value;
    35.  
    36.                     deltapoint = point2 - point1;
    37.  
    38.                     outtangent = deltapoint.y / deltapoint.x;
    39.                 }
    40.  
    41.                 key.inTangent = intangent;
    42.                 key.outTangent = outtangent;
    43.                 curve.MoveKey(i, key);
    44.             }
    45.         }
     
    arivd, Westland, Laiken and 3 others like this.
  10. jwyoo_stone

    jwyoo_stone

    Joined:
    Jul 15, 2012
    Posts:
    12
    only need do like this:
    Code (CSharp):
    1.  
    2. for (int i = 0; i < curve.keys.Length; ++i) {
    3.     Keyframe k = curve.keys[i];
    4.     k.tangetMode = 21;
    5.     curve.MoveKey(i,k);
    6. }
     
  11. deab

    deab

    Joined:
    Aug 11, 2013
    Posts:
    93
    Spot on, thanks metroidsnes and eagle555.
     
  12. aubrey_unity438

    aubrey_unity438

    Joined:
    May 11, 2018
    Posts:
    1
  13. AlexTuduran

    AlexTuduran

    Joined:
    Nov 26, 2016
    Posts:
    27
    That's the right way to do it. It's elegant and clear.

    Code (CSharp):
    1. for (int i = 0; i < curve.length; i++) {
    2.     AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Linear);
    3.     AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Linear);
    4. }
     
    vincurekf and samana1407 like this.
  14. EnduvoJD

    EnduvoJD

    Joined:
    Aug 12, 2016
    Posts:
    49
    This would only work in the Unity Editor, not in a build.
     
  15. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,624
    The elegant and clean way would be for Unity to have a bunch of shortcuts and a ton of quality of life improvements for their basic animation tools already, but they are working on their v2 instead of doing that, and v2 will probably release with a bunch of quality of life stuff missing, which they will not implement since they will be working on v3, which will release with a bunch of quality of life stuff missing, which they will not implement since they will be working on v4, which will release with a bunch of quality of life stuff missing, which they will not implement since we will all be sucked into the cosmic singularity by then and will only exist as vapours.
     
    ul_antnasce, Ruslank100 and halley like this.