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

Lack of scripting functionality for creating 2D animation clips by code

Discussion in '2D' started by Caspilar, Nov 21, 2013.

  1. Caspilar

    Caspilar

    Joined:
    Jul 19, 2013
    Posts:
    17
    I was having some doubts either to post this in the 2D or the Animation forum because my problems are related to both of them at the same time, but decided to write it here because they serve no purpose for 3D animation (except for the 2nd listed below).

    So, I'm creating a plugin that will extract 2D bone animation data from a text file and create the animations inside unity as .anim so that I can use the new 2D Mecanim integration.

    I'll list the problems I've faced until now. For some of them I found a workaround. They're all related to creating an animation clip via scripting.

    1-> Procedurally created Animation Clip cannot be used in Animator Controller

    After creating the animation clip in my script, when I attempted to use it in an Animator Controller I'd receive a warning message telling me that the animation clip could not be used because it contained no Muscle Definition and that I should change the Importer Settings to fix that.

    Well, as far as I know, there's no such thing for 2D animation clips and also there's no animation Importer Settings if I'm creating it by code, right? But one of the new 2D features allow you to create a 2D animation clip that works on Animator Controller by dragging a couple of sprites to the "Hierarchy" panel. But there is no way, at least I have not found it, to set the muscle definition for an animation clip by code.

    WORKAROUND: I've created an 2D animation clip the "drag'n'drop" way and cleared its curves, resulting in an "empty animation" asset. The settings needed for the Animation Controller (muscle definition, ...?) are not lost this way. Then, for each animation that I need to create, I duplicate this "empty animation" and put my own curves on it. By doing that I'm able to create an animation clip via scripting and use it in the Animator Controller. Not the prettiest thing, I know, but it works, at least...
    ---------

    2-> Artifacts with the rotation curves

    This is not really a lack of scripting functions but the lack of their documentation. I had some problems with rotation artifacts, but it has already been solved (http://forum.unity3d.com/threads/21...cts-when-creating-animation-clip-procedurally) using the AnimationClip.EnsureQuaternionContinuity function.
    ---------

    3-> How to set a keyframe to change the sprite of a SpriteRenderer at a given time.

    For what I've seen, the only data that you can input to an AnimationClip is AnimationCurves. The AnimationCurve is made of Keyframes and these are made of two floats, time and value. How could I set a keyframe so that at 3 seconds, the SpriteRenderer would change a sprite from "eyeOpen" to "eyeClose" if all I can do is set a float for a given time? This is a functionality that is available through the "Animation Window", but I can't find a way to do it via scripting.
    ---------

    To sumarize it all, I'm very happy with the new 2D features. They're awesome and all, but I'm a little bit frustrated that there are so many features available through "drag'n'drop" in the editor that cannot be reproduced in scripts. Perhaps I'm doing it all wrong and this the "legacy" way of doing animation by code but this is the only way I was able to do it.

    Thanks for reading and please let me know if you've faced this or similar problems and how you solved them.
     
    tobad likes this.
  2. Caspilar

    Caspilar

    Joined:
    Jul 19, 2013
    Posts:
    17
    Okay, I found a class (AnimationUtility) that helped me to fix all of the things that I cited. As the documentation on this subject is not the best, I'll try and explain what I did to fix it all.


    1-> Procedurally created Animation Clip cannot be used in Animator Controller

    Code (csharp):
    1.            
    2. AnimationClip animClip = new AnimationClip();
    3. // Setting it as generic allows you to use the animation clip in the animation controller
    4. AnimationUtility.SetAnimationType(animClip, ModelImporterAnimationType.Generic);
    2-> Artifacts with the rotation curves

    Code (csharp):
    1.      
    2. AnimationClip animClip = new AnimationClip();      
    3.  
    4. // Add rotation curves to it
    5. //...
    6. //...
    7.  
    8. animClip.EnsureQuaternionContinuity();
    9.  
    3-> How to set a keyframe to change the sprite of a SpriteRenderer at a given time

    The AnimationUtility allows you to add a different keyframe, the ObjectReferenceKeyframe. Example code:

    Code (csharp):
    1.  
    2. AnimationClip animClip = new AnimationClip();
    3.  
    4. // First you need to create e Editor Curve Binding
    5. EditorCurveBinding curveBinding = new EditorCurveBinding();
    6.  
    7. // I want to change the sprites of the sprite renderer, so I put the typeof(SpriteRenderer) as the binding type.
    8. curveBinding.type = typeof(SpriteRenderer);
    9. // Regular path to the gameobject that will be changed (empty string means root)
    10. curveBinding.path = "";
    11. // This is the property name to change the sprite of a sprite renderer
    12. curveBinding.propertyName = "m_Sprite";
    13.  
    14. // An array to hold the object keyframes
    15. ObjectReferenceKeyframe[] keyFrames = new ObjectReferenceKeyframe[10];
    16.  
    17. for (int i = 0; i < 10; i++)
    18. {
    19.      keyFrames[i] = new ObjectReferenceKeyframe();
    20.      // set the time
    21.      keyFrames[i].time = timeForKey(i);
    22.      // set reference for the sprite you want
    23.      keyFrames[i].value = spriteForKey(i);
    24.  
    25. }
    26.  
    27. AnimationUtility.SetObjectReferenceCurve(animClip, curveBinding, keyFrames);
    28.  
    29.  
    I thought about deleting this post, but there's so few documentation on how to do this that I decided to keep it. Hope this helps someone.
     
    Last edited: Nov 22, 2013
  3. Borluse

    Borluse

    Joined:
    Aug 16, 2012
    Posts:
    3

    Great Job! Many many thanks to you! It really helps me a lot!
    Thanks!
     
  4. giyomu

    giyomu

    Joined:
    Oct 6, 2008
    Posts:
    1,094
    thanks for the infos , don't delete your post ^^, I am sure it will be very helpful for those on the same situation
     
  5. ttzl

    ttzl

    Joined:
    Nov 20, 2013
    Posts:
    2
    I write script according your method. but the animation not work.

    I put this script in start method in a MonoBehaviour. and the game object has a SpriteRenderer component.

    and I create some sprites from atlas and put them into a " List<Sprite> frames" . I also added a Animation

    component in the game object.

    EditorCurveBinding curveBinding = new EditorCurveBinding();
    curveBinding.type = typeof(SpriteRenderer);
    curveBinding.path = "";
    curveBinding.propertyName = "m_Sprite";
    ObjectReferenceKeyframe[] keyFrames = new ObjectReferenceKeyframe[frames.Count];
    for (int i = 0; i < frames.Count; i++)
    {
    keyFrames = new ObjectReferenceKeyframe();
    keyFrames.time = i;
    keyFrames.value = frames;
    }

    AnimationClip ac = new AnimationClip ();
    ac.name = "go";
    AnimationUtility.SetObjectReferenceCurve(ac, curveBinding, keyFrames);
    animation.AddClip (ac, "go");
    animation.clip = ac;
    animation.Play (ac.name);


    but it does not work? and no errors occured. no one sprite displayed. How should I modify this script? very thanks.
     
    Last edited: Dec 2, 2013
  6. Caspilar

    Caspilar

    Joined:
    Jul 19, 2013
    Posts:
    17
    @ttzl sorry for taking this long to answer your question.

    The method I described only works in the Editor mode, not during runtine. EditorCurveBinding is inside the UnityEditor namespace. I assume you're trying to use during runtime because you said that you put this code inside a Start method of a MonoBehaviour.

    If you need to create them in runtime, I don't really know how that could be done, but if you only wish to create the animation procedurally and this can be made prior to the execution of the game, i.e. in the editor, the method should work for all I know.

    Try putting your code in a Custom Editor Script.

    Feel free to ask me any other question.
     
    tobad likes this.
  7. ttzl

    ttzl

    Joined:
    Nov 20, 2013
    Posts:
    2
    Caspilar, thank you very much. I really try to it use during runtime. Then I will try it in edit mode. thank you!
     
  8. Guadi

    Guadi

    Joined:
    Dec 12, 2013
    Posts:
    2
    Hello Unity Community, im currently having a similar problem and tried to use the provided information.
    Just one thing, are the "timeForKey" and "spriteForKey" variables or functions?
    I get a dosnt exist in the current context error.



    Its just to make sure im doing everything ok, Thanks and sorry for the noobish question.
    Im trying to learn as fast as i can to program and design Sprites, any usefull tip will be gratefull.
     
    Last edited: Dec 14, 2013
  9. Caspilar

    Caspilar

    Joined:
    Jul 19, 2013
    Posts:
    17
    @Guadi

    An ObjectKeyframeReference is made of a float representing the time and a reference object.

    They are both functions and the code I wrote was more of a pseudocode, only to indicate that the functionality exists. You should implement them to suit your needs. One possible implementation is the following

    Code (csharp):
    1.  
    2. int timeForKey(int i)
    3. {
    4.       // Here you put the code that will return the time
    5.       // for the ith keyframe
    6.  
    7.       // Example: the time of each frame will be its index
    8.       return i;
    9. }
    10.  
    11. Sprite spriteForKey(int i)
    12. {
    13.     // Here you put the code that will return the sprite that you want
    14.     // for the ith keyframe
    15.  
    16.     // Example: assuming you have a sprite array "_sprites" populated with all the sprites for each keyframe
    17.     // in order
    18.     return _sprites[i];
    19. }
    20.  
    But this is really just a simple example and most likely won't be what you need. Should get you started though
     
  10. nicloay

    nicloay

    Joined:
    Jul 11, 2012
    Posts:
    540
    Caspilar, thanks for examples. I's huge headake for me to make curve works.
    firstly i've made everything through clip.SetCurve(....) and i've got a lot of issues, like curve are correct, but with wrong properties in editor (smooth break and so on).
    just tried to make it through CurveBinding, and got more stable result.

    also i see following static methods
    Code (csharp):
    1.  
    2.     public static EditorCurveBinding FloatCurve(string inPath, System.Type inType, string inPropertyName)
    3.     {
    4.       return new EditorCurveBinding()
    5.       {
    6.         path = inPath,
    7.         type = inType,
    8.         propertyName = inPropertyName,
    9.         m_isPPtrCurve = 0
    10.       };
    11.     }
    12.  
    13.     public static EditorCurveBinding PPtrCurve(string inPath, System.Type inType, string inPropertyName)
    14.     {
    15.       return new EditorCurveBinding()
    16.       {
    17.         path = inPath,
    18.         type = inType,
    19.         propertyName = inPropertyName,
    20.         m_isPPtrCurve = 1
    21.       };
    22.     }
    23.   }
    24.  
    probably it's more easiest to work with them.
    Also i'm not sure about isPPtrCurve parameter, but i think that if isPPtrCurve=1 it's for sprite switching, otherwise for curving.
     
  11. RyuMaster

    RyuMaster

    Joined:
    Sep 13, 2010
    Posts:
    468
    OMG, stumbled upon this, and you literally saved me today! THANK YOU!
     
  12. kilik128

    kilik128

    Joined:
    Jul 15, 2013
    Posts:
    909
    someone can help to understand this please


    i have do


    $7Titw.png

    it's result add key

    but the typeof look not good
     
    Last edited: Apr 2, 2014
  13. Caspilar

    Caspilar

    Joined:
    Jul 19, 2013
    Posts:
    17
    Hi there kilik128.

    There's no need for you to use the EditorCurveBinding for Rotation curves, the process is much simpler. You can set up the rotation curve the regular way, using the AnimationClip.SetCurve. Please check the following link: https://docs.unity3d.com/Documentation/ScriptReference/AnimationClip.SetCurve.html

    The link explains how to set up curves for Transform values such as position and rotation. Be aware that the rotation may not be set using euler angles, they must be set as quaternions, and therefore you must define one curve for each of the 4 members of the quaternion x,y,z,w.
     
  14. kilik128

    kilik128

    Joined:
    Jul 15, 2013
    Posts:
    909
    Same Trouble For animated Blendshape
    someone get any idea
     
  15. exerion

    exerion

    Joined:
    Jun 29, 2011
    Posts:
    54
    Thanks Caspilar! You saved me hours :-D
     
  16. shinriyo_twitter

    shinriyo_twitter

    Joined:
    Aug 12, 2011
    Posts:
    328
    Code (csharp):
    1.  
    2. for (int i = 0; i < frames.Count; i++)
    3. {
    4.     keyFrames[i] = new ObjectReferenceKeyframe();
    5.     keyFrames[i].time = 1.0f;
    6.     keyFrames[i].value = frames;
    7. }
    8.  

     
  17. SamuZen

    SamuZen

    Joined:
    Jan 26, 2014
    Posts:
    7
    @Caspilar ,

    I have a Animator Controller Already working for my character, but i'm trying to make an editor Script to multiply the values of the animations curve ... ex: i want him to bounce more , so i ill get all curves with localPosition.y and Multiple by some value ...

    Do you know how to get and edit animations from Animator Controller ?

    if this is not possible , i will get all animations thats inside the character with AnimationUtility.getAnimationClips()... And Create new AnimationClips with new values ... but how to replace this new animations clips to the animatorController ? and keep the controller Working ...

    Thanks
     
  18. ltlejeune

    ltlejeune

    Joined:
    Aug 6, 2019
    Posts:
    24
    Exhumating the topic since I'm doing the exact same as mentioned in this thread, except that whenever I play the animation in the animator, the same sprite is rendered for all key frames. (in Editor and in-game)
    In order to make Unity recognized that the key frames have different sprites, is to manually drag one frame 1mm any direction, then back to its position. Then all a sudden the animation works fine :confused: (The animation can be detached and reattached to any Animator it will work fine from this point).
    cf. https://forum.unity.com/threads/animationclip-programmatically-created-editor-lost.1202059/

    I hope the initial post creator have some ideas for me.

    Thanks in advance.
     
  19. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,316
    You're replying to a post made 7 years ago to a user who had this as their last post (it took me a few moments to see that). This was when 2D wasn't even much of a thing then and had no dedicated animation system.

    Please, if you have something specific to add then please make a new post, it's free. Don't necro threads.

    There's also a dedicated Animation sub-forum here if you have general animation things to discuss.

    Thanks.
     
    ltlejeune likes this.
  20. ltlejeune

    ltlejeune

    Joined:
    Aug 6, 2019
    Posts:
    24