Search Unity

  1. Unity 2018.3 is now released.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. We've updated our Terms of Service. Please read our blog post from Unity CTO and Co-Founder Joachim Ante here
    Dismiss Notice
  4. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  5. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

Add Key via C# script in Custom Clip?

Discussion in 'Timeline' started by ghtx1138, Dec 13, 2018.

  1. ghtx1138

    ghtx1138

    Joined:
    Dec 11, 2017
    Posts:
    61
    Hi

    I am trying to add keys into a custom clip but am failing miserably. My custom timeline works OK and I can add a new clip of my custom type to the track.

    But having no luck adding keys within the clip. I've tried MyClip.SetCurve (which I suspect might be for the Animator) and MyClip.curves.SetCurves - neither of those work - I get a Null Reference Exception.

    Here's my useless code:

    Code (CSharp):
    1. var myCurve = new AnimationCurve();
    2. myCurve.AddKey(1.0f, 65.0f);
    3. myCurve.AddKey(2.2f, 65.0f);
    4. newCustomClip.curves.SetCurve("", typeof(float), "ShapeWeight", myCurve);
    ShapeWeight is the variable defined in my Custom Behaviour visible as Shape Weight in the Inspector for the Custom Clip. I can confirm that I can Add Keys on Shape Weight manually.

    Any thoughts would be greatly appreciated.

    <edit> I'm on 2018.2.16f1
     
    Last edited: Dec 13, 2018
  2. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    644
    In the call to SetCurve, I believe the type should be set to the type of your clip ... i.e. typeof(CustomPlayableAsset).
     
  3. ghtx1138

    ghtx1138

    Joined:
    Dec 11, 2017
    Posts:
    61
    Hi seant_unity

    Unfortunately this did not work. I've tried a number of plausible (and implausible alternatives)

    Code (CSharp):
    1.                         //newCustomClip.curves.SetCurve("", typeof(BlendShapesClip), "ShapeWeight", myCurve); // Null Exception
    2.                         //newCustomClip.curves.SetCurve("", typeof(BlendShapesClip), "Shape Weight", myCurve); // Null Exception
    3.                         //newCustomClip.curves.SetCurve("Blend Shapes Clip.Shape Weight", typeof(BlendShapesClip), "ShapeWeight", myCurve);  // Null Exception
    4.                         //newCustomClip.animationClip.SetCurve("", typeof(BlendShapesClip), "ShapeWeight", myCurve); // Null
    5.                         //newCustomClip.animationClip.SetCurve("", typeof(BlendShapesClip), "Shape Weight", myCurve); // Null
    6.                         //newCustomClip.curves.SetCurve("", typeof(BlendShapesClip), "Blend Shapes Clip.Shape Weight", myCurve); // Null Exception
    I've stripped the playable to bare bones. I'm creating the clip from a editor and it gets inserted into the timeline so I guess my references and Playables code are somewhat correct.

    Code (CSharp):
    1.                         var newCustomClip = track.CreateClip<BlendShapesClip>();
    2.                         newCustomClip.displayName = "My Clip";
    3.                         newCustomClip.duration = 10;
    I get a Null Reference Exception - which I guess can mean either:
    - the path is wrong
    - the type is wrong
    - the property name is wrong

    I've verified by Debug that there are keys in myCurve.

    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. AddData.OnGUI () (at Assets/AddData.cs:66)
    Please let me know if I can try something else or upload the code somewhere. I'd love to be able to insert keys into timeline programmatically.

    Thanks


     
  4. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    644
    What does your blend shape clip and playable code look like? (just the fields is enough, not the methods).
     
  5. ghtx1138

    ghtx1138

    Joined:
    Dec 11, 2017
    Posts:
    61
    Thanks for keeping with me seant. I'm sorry but I don't know what you mean by "playable code" and "not methods". I am self taught and lacking basic skills :(.

    Please let me know if I can provide more info.

    Thanks
     

    Attached Files:

  6. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    644
    Ok, I figured it out. Instead of AnimationClip.SetCurve, use UnityEditor.AnimationUtility.SetEditorCurve().

    e.g.
    Code (CSharp):
    1.             UnityEditor.AnimationUtility.SetEditorCurve(
    2.                 clip.curves,
    3.                 UnityEditor.EditorCurveBinding.FloatCurve(string.Empty, typeof(BlendShapeClip), "ShapeWeight"),
    4.                 AnimationCurve.Linear(0,0, (float)clip.duration, 1)
    5.             );
    6.  
     
  7. ghtx1138

    ghtx1138

    Joined:
    Dec 11, 2017
    Posts:
    61
    Hi Sean

    I don't think I can use UnityEditor.AnimationUtility.SetEditorCurve on a CustomClip. I'm getting errors:
    ArgumentNullException: Argument cannot be null. Parameter name: clip
    and if I remove the ".curves" on clip.curves I get:
    error CS1503: Argument `#1' cannot convert `UnityEngine.Timeline.TimelineClip' expression to type `UnityEngine.AnimationClip'

    I tried casting the CustomClip to an AnimationClip with a view to un-casting it later (if that is even possible) but that failed of course.

    Please let me know if you have any other ideas. I'd love to get this working and the only way I can think of doing it at the moment is writing a .playable text file - which doesn't look all that easy and is definitely inelegant.

    Another option is decompiling timeline.dll and looking for the Add Key function but I am so out of my depth already.

    I wonder if 2018.3 would be worth trying?

    Thanks again for sticking with me on this difficult quest :)

    Cheers
     
  8. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    644
    ah, here try this. It will allocate the appropriate animation clip

    Code (CSharp):
    1.             if (clip.curves == null)
    2.             {
    3.                 // there are public methods for this in 2019.1
    4.                 typeof(TimelineClip).GetMethod("AllocateAnimatedParameterCurves", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(clip, null);
    5.             }  
    6.  
     
  9. ghtx1138

    ghtx1138

    Joined:
    Dec 11, 2017
    Posts:
    61
    Sorry for the slow response Sean. I've been scratching my head about this.

    Reflection in 2018.2 did not work for me.

    Using this code:

    Code (CSharp):
    1. typeof(TimelineClip).GetMethod("AllocateAnimatedParameterCurves", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(newCustomClip, curve);
    I get this error:

    Code (CSharp):
    1. Assets/AddData.cs(62,163): error CS1502: The best overloaded method match for `System.Reflection.MethodBase.Invoke(object, object[])' has some invalid arguments
    2.  
    3. Assets/AddData.cs(62,185): error CS1503: Argument `#2' cannot convert `UnityEngine.AnimationCurve' expression to type `object[]'
    My curve is defined like this:

    Code (CSharp):
    1. AnimationCurve curve;
    2. Keyframe[] keys;
    3. keys = new Keyframe[3];
    4. keys[0] = new Keyframe(0.0f, 0.0f);
    5. keys[1] = new Keyframe(1.1f, 1.5f);
    6. keys[2] = new Keyframe(2.0f, 0.0f);
    7. curve = new AnimationCurve(keys);
    I downloaded 2019.1 and got the same errors with reflection.

    Can you direct me to the public methods for setting curves in clips in 2019.1 please? I can't see anything in intellisense and though I downloaded the 2019.1 docs I can't find anything in there.

    Thanks again for your continued help.

    Cheers
     
  10. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    644
    .Invoke(newCustomClip, new object[]{curve}); should do the trick.

    In 2019.1, you can simply call newCustomClip.CreateCurve("animClipName") to create the animation clip that stores the curves instead of using reflection.

    (The 2019.1 docs are WIP and located here)
     
  11. ghtx1138

    ghtx1138

    Joined:
    Dec 11, 2017
    Posts:
    61
    Thanks for your reply Sean.

    Unfortunately I'm getting this error and hope you might have a suggestion:
    Code (CSharp):
    1. TargetParameterCountException: parameters do not match signature
    2. System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:188)
    The code I am using:
    Code (CSharp):
    1. AnimationCurve curve;
    2. Keyframe[] keys;
    3. keys = new Keyframe[3];
    4. keys[0] = new Keyframe(0.0f, 0.0f);
    5. keys[1] = new Keyframe(1.1f, 1.5f);
    6. keys[2] = new Keyframe(2.0f, 0.0f);
    7. curve = new AnimationCurve(keys);
    8. var newCustomClip = track.CreateClip<BlendShapesClip>();
    9. newCustomClip.displayName = "My New Clip";
    10. newCustomClip.duration = 3f;
    11. typeof(TimelineClip).GetMethod("AllocateAnimatedParameterCurves", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(newCustomClip, new object[] { curve });
    I'll also have a look at the 2019.1 option. Maybe I can create the playable in 2019 and import it into 2018.

    Cheers
     
  12. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    644
    My mistake, it should be just .Invoke(newCustomClip, new object[] {})
     
  13. ghtx1138

    ghtx1138

    Joined:
    Dec 11, 2017
    Posts:
    61
    Wow. It works! Thanks very much Sean

    Is there any preferred order of operations here? It looks like we're "priming" the method by invoking it and then setting the curve. I guess I'll have to see how setting a large number of keys on a number of clips via loops goes.

    Thanks again for hanging in there Sean. Cheers

    Code (CSharp):
    1.  
    2. Keyframe[] keys;
    3. keys = new Keyframe[3];
    4. keys[0] = new Keyframe(0.0f, 0.0f);
    5. keys[1] = new Keyframe(1.1f, 1.5f);
    6. keys[2] = new Keyframe(2.0f, 0.0f);
    7. curve = new AnimationCurve(keys);
    8.  
    9. var newCustomClip = track.CreateClip<BlendShapesClip>();
    10.  
    11. newCustomClip.displayName = "My New Clip";
    12. newCustomClip.duration = 3f;
    13.  
    14. typeof(TimelineClip).GetMethod("AllocateAnimatedParameterCurves", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(newCustomClip,new object[]{});
    15. newCustomClip.curves.SetCurve("", typeof(BlendShapesClip), "ShapeWeight", curve);
    16.  
     
  14. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    644
    Yes, the Allocate call just needs to be done once to create the animation clip and flag it as a subasset. Glad that it's working!