Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
4
Question by testure · Sep 05, 2012 at 03:49 AM · animationanimationcurvetweentangents

Undocumented property: Keyframe.tangentMode

So yeah.. basically I'm trying to create an AnimationCurve dynamically by generating keyframes and then adding them to an AnimationCurve object. The problem is, Unity is adding them all with flat tangents- so a nice sine wave ends up looking like a jittery pile of garbage :P

In the AnimationCurve editor, you can right click on a node and set the tangent mode to Linear, Constant, Free, etc. Now- the unity docs make no mention of Keyframe.tangentMode, and through my own tests I'm unable to get it to do anything. Is this just a stubbed out property that isn't hooked up or something? Is somebody aware of another way to set a tangent to linear without having to open the animation curve editor?

For reference, here's Unity's Keyframe struct. Assignment of tangentMode always ends up with the same result- it never seems to change from zero

 namespace UnityEngine
 {
     public struct Keyframe
     {
         public Keyframe( float time, float value );
         public Keyframe( float time, float value, float inTangent, float outTangent );
 
         public float inTangent { get; set; }
         public float outTangent { get; set; }
         public int tangentMode { get; set; }
         public float time { get; set; }
         public float value { get; set; }
     }
 }

I threw together this test script if anybody wants to see the behavior I'm talking about. Just throw this on any gameobject and look at the animationcurve- it's a basic quadratic slope, but you can see how the node tangents are causing it to have bumps at every node. If you select all of those nodes and right click->set both nodes to linear, you get the appropriate quadratic slope. I'm looking for a way in code to convert these nodes to linear. Thanks!

 using UnityEngine;
 using System.Collections;
 
 [ExecuteInEditMode]
 public class curvetest : MonoBehaviour
 {
     public AnimationCurve testCurve;
 
     void OnEnable()
     {
         if ( testCurve.keys.Length > 0 )
             return;
 
         testCurve = new AnimationCurve();

         for ( float i = 0f; i < 1f; i += .1f )
         {
             float v = i;
             v /= .5f;

             if ( i < 1 )
             {
                 testCurve.AddKey( new Keyframe( i, 1f / 2 * v * v ) );
             }
             else
             {
                 v--;
                 testCurve.AddKey( new Keyframe( i, -1f / 2 * ( v * ( v - 2 ) - 1 ) ) );
             }
         }
     }
 
 }
 
Comment
Add comment · Show 3
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image testure · Sep 05, 2012 at 06:00 AM 0
Share

Some aditional information: - if I add some debug output while making changes to the node from the animation curve editor, I get the following results for tangent$$anonymous$$ode: "Free" is 1, "Linear" is 21, and "Constant" is 31. attempting to actually set tangent$$anonymous$$ode to one of these values via code seems to have no effect.

Debug.Log( i + "] Before: " + testCurve.keys[ i ].tangent$$anonymous$$ode );

testCurve.keys[ i ].tangent$$anonymous$$ode = 21;

Debug.Log( i + "] After: " + testCurve.keys[ i ].tangent$$anonymous$$ode );

result is 0 before and 0 after.

avatar image testure · Sep 05, 2012 at 06:52 AM 0
Share

Some more info.. starting to dig into uncharted territory here it seems. Using reflection I discovered that there's actually an internal Tangent$$anonymous$$odes enum:

 internal enum Tangent$$anonymous$$ode
 {
     Editable,
     Smooth,
     Linear,
     Stepped
 }


I can only assume that UT haven't deemed all of this ready for prime-time yet? For now I'm trying to dig through reflector to find the UnityEditor call that sets the tangent mode- looks like it's

 UnityEditor.Curve$$anonymous$$enu$$anonymous$$anager.SetBoth(Tangent$$anonymous$$ode, List<$$anonymous$$eyIdentifier>) : Void
 Depends On
     mscorlib
     UnityEditor
     UnityEngine
 Used By
     UnityEditor.Curve$$anonymous$$enu$$anonymous$$anager.SetEditable(Object) : Void
     UnityEditor.Curve$$anonymous$$enu$$anonymous$$anager.SetFlat(Object) : Void
     UnityEditor.Curve$$anonymous$$enu$$anonymous$$anager.SetSmooth(Object) : Void
avatar image testure · Sep 05, 2012 at 09:01 AM 0
Share

Alright, after much screwing around with this I'm calling it a bust. So far as I can tell, the setter for tangent$$anonymous$$ode isn't actually hooked up to SetTangent$$anonymous$$ey$$anonymous$$ode, which lives in CurveUtility (internal unityeditor class). I was able to derive how the tangent mode is being calculated though- it appears to be a bitmask based on the left/right hand tangency of any given node.

the left side is calculated with:

     m_Tangent$$anonymous$$ode &= -7;
     m_Tangent$$anonymous$$ode |= ( ( int ) mode ) << 1;           

and the right side is calculated with:

     m_Tangent$$anonymous$$ode &= -25;
     m_Tangent$$anonymous$$ode |= ( ( int ) mode ) << 3;

where mode is a Tangent$$anonymous$$ode (enum posted up above)

Long story short, the editor seems to be calling SetTangent$$anonymous$$ey$$anonymous$$ode to update the tangents, but the setter for the $$anonymous$$eyframe struct isn't. Seems like a bug to me.. And due to boxing/unboxing, using reflection to set the private member variable manually isn't an option in this case.

Oh well... guess I'll have to wait for official support for this :\

3 Replies

· Add your reply
  • Sort: 
avatar image
7
Best Answer

Answer by testure · Sep 05, 2012 at 07:35 PM

Okay, figured out a way to solve this issue. Though bear in mind that this relies on an undocumented private member of struct, and hidden functionality of an internal class, so use at your own risk. Personally, I'm developing a tool that will be in development for quite a while, so I can just make note of it and replace the functionality with the proper method when UT officially supports it. I'm only posting it here because there is ZERO information about this on the internet, and thus no solutions- so I've done all the exhausting research/testing necessary so the next guy doesn't have to :P

Anyway, here it is:

 using UnityEngine;
 using System;
 using System.Collections;
 using System.Reflection;
 
 /// <summary>
 /// Static utility class to work around lack of support for Keyframe.tangentMode
 /// This utility class mimics the functionality that happens behind the scenes in UnityEditor when you manipulate an AnimationCurve. All of this information
 /// was discovered via .net reflection, and thus relies on reflection to work
 /// --testure 09/05/2012
 /// </summary>
 public static class AnimationCurveUtility : System.Object
 {
     public enum TangentMode
     {
         Editable,
         Smooth,
         Linear,
         Stepped
     }
 
     public enum TangentDirection
     {
         Left,
         Right
     }
 
     public static void SetLinear( ref AnimationCurve curve )
     {
         Type t = typeof( UnityEngine.Keyframe );
         FieldInfo field = t.GetField( "m_TangentMode", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance );
 
         for ( int i = 0; i < curve.length; i++ )
         {
             object boxed = curve.keys[ i ]; // getting around the fact that Keyframe is a struct by pre-boxing
             field.SetValue( boxed, GetNewTangentKeyMode( ( int ) field.GetValue( boxed ), TangentDirection.Left, TangentMode.Linear ) );
             field.SetValue( boxed, GetNewTangentKeyMode( ( int ) field.GetValue( boxed ), TangentDirection.Right, TangentMode.Linear ) );
             curve.MoveKey( i, ( Keyframe ) boxed );
             curve.SmoothTangents( i, 0f );
         }
     }
 
     public static int GetNewTangentKeyMode( int currentTangentMode, TangentDirection leftRight, TangentMode mode )
     {
         int output = currentTangentMode;
 
         if ( leftRight == TangentDirection.Left )
         {
             output &= -7;
             output |= ( ( int ) mode ) << 1;
         }
         else
         {
             output &= -25;
             output |= ( ( int ) mode ) << 3;
         }
         return output;
     }
 
 }
 
Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image testure · Sep 05, 2012 at 08:18 PM 0
Share

And for those wondering- I'm aware that you can simply set the in/out tangent of a keyframe manually by calculating the correct tangency via atan(), but that isn't a good solution in my case since I need the actual tangent mode to change. Brute-forcing the tangent angle would still leave tangent handles, so if a user opens the curve editor and moves a keyframe the handles will be there and the keyframe will not be linear.

So, kind of a specialized problem with a specialized solution, but there you have it.

avatar image monsterBlues · Sep 26, 2012 at 05:09 AM 0
Share

Hey thanks for all this work! I wanted to set all my scale curves to linear. If you have time, I would appreciate the help.

This script doesn't crash, but nothing happens. I saved your code to a file called AnimationCurveUtility.cs, and then in my own script I have this:

     static void LinearScale$$anonymous$$eys()
     {
         // GET SELECTED ANI$$anonymous$$ATIONCLIP
         GameObject obj = Selection.activeObject as GameObject;
         
         if(obj.animation == null)
         {
             Debug.Log("No Animations on Active Transform");
             return;
         }
         
         foreach (AnimationClip anim in AnimationUtility.GetAnimationClips(obj.animation))
         {
             AnimationClipCurveData[] curveDatas = AnimationUtility.GetAllCurves(anim);
             
             for (int i = 0; i < curveDatas.Length; i++)
             {
                 
                 if(curveDatas[i]!=null)
                 {
                     if(curveDatas[i].propertyName.ToLower().Contains("scale"))
                     {
                         AnimationCurveUtility.SetLinear(ref curveDatas[i].curve);
                     }
                 }
             }
         }
     }

avatar image testure · Sep 27, 2012 at 01:42 AM 0
Share

I don't see anything immediately obvious as to what's not working- you're getting Selection.activeObject so I'm assu$$anonymous$$g that this is an editor script? What's calling LinearScale$$anonymous$$eys? Have you tried putting any debug output in to see where it's failing?

avatar image monsterBlues · Sep 27, 2012 at 03:26 PM 0
Share

Thanks for the help. I figured out the problem a little while ago. After calling your SetLinear function, I needed to re-apply the curve to the object using AnimationUtility.SetEditorCurve. I had assumed that AniamtionUtility.GetAllCurves was a live connection to the object.

avatar image techmage · Dec 21, 2013 at 09:44 PM 0
Share

This is really a great piece of work it seems, but I just can't get it to work at all. I am trying to set a curve to be stepped and it just isn't working. The curve editor in the inspector isn't showing the curve as stepped, and also when I do a Debug.Log(curve.evaluate) in code the returned values are not stepped. Has something changed about this?

avatar image
0

Answer by nicloay · Jan 13, 2014 at 06:16 PM

Hope that this page will help you http://wiki.unity3d.com/index.php/EditorAnimationCurveExtension

testure Script is almost ok, except that for linear tangent he is using SmoothTangents which, probably, should not be there, It must be left and right tangent manual calculation like this using UnityEngine; using System.Collections;

 namespace CurveExtended{
     
     public static class CurveExtension {
         
         public static void UpdateAllLinearTangents(this AnimationCurve curve){
             for (int i = 0; i < curve.keys.Length; i++) {
                 UpdateTangentsFromMode(curve, i);
             }
         }
         
         // UnityEditor.CurveUtility.cs (c) Unity Technologies
         public static void UpdateTangentsFromMode(AnimationCurve curve, int index)
         {
             if (index < 0 || index >= curve.length)
                 return;
             Keyframe key = curve[index];
             if (KeyframeUtil.GetKeyTangentMode(key, 0) == TangentMode.Linear && index >= 1)
             {
                 key.inTangent = CalculateLinearTangent(curve, index, index - 1);
                 curve.MoveKey(index, key);
             }
             if (KeyframeUtil.GetKeyTangentMode(key, 1) == TangentMode.Linear && index + 1 < curve.length)
             {
                 key.outTangent = CalculateLinearTangent(curve, index, index + 1);
                 curve.MoveKey(index, key);
             }
             if (KeyframeUtil.GetKeyTangentMode(key, 0) != TangentMode.Smooth && KeyframeUtil.GetKeyTangentMode(key, 1) != TangentMode.Smooth)
                 return;
             curve.SmoothTangents(index, 0.0f);
         }
         
         // UnityEditor.CurveUtility.cs (c) Unity Technologies
         private static float CalculateLinearTangent(AnimationCurve curve, int index, int toIndex)
         {
             return (float) (((double) curve[index].value - (double) curve[toIndex].value) / ((double) curve[index].time - (double) curve[toIndex].time));
         }
         
     }
 }


and then you can use like this

 yourLinearCurve.UpdateAllLinearTangents();
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by maciej.struzyna · Nov 04, 2014 at 08:53 AM

To get effect of "constant" tangent like in the default AnimationCurve editor window the tangentMode value is not needed. You have to set the tangent to float.PositiveInfinity. To get effect of "Linera" tangent you have to calculate the tangent accordingly like Jan wrote.

Therefore the tangentMode value is just informative (probably for the editor window) which mode for the tangent the user chosen. That is why this is not documented.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

11 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Tweened rotation animation in a single axis, spins around others if the angle flips from going lower than 0. 0 Answers

Remove unused animation curves? (Animation.Update() optimizing) 1 Answer

Animator parameter does not pick the value of the animation curve with the same name 2 Answers

Controlling speed of anim replay through an animation curve 1 Answer

I can't find some property of material after upgrade 4.3 in animation curve editor. 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges