Search Unity

Duplicating a Timeline loses all the Bindings Unity v2017.2.0b6

Discussion in 'Timeline' started by tezza2k1, Aug 14, 2017.

  1. tezza2k1

    tezza2k1

    Joined:
    Feb 16, 2017
    Posts:
    13
    (Unity v2017.2.0b6)

    I dread making a new timeline because I will need to hunt for all the hierarchy objects and re-bind them. It should be a stress free process but it is not.

    There seems no particular reason that the duplication mechanism could not also duplicate the bindings.

    It makes workflow that much slower, because you have to re-find and re-bind the targets. I have chosen 2 bindings here, but my production Timlines have many more.

    I have a scripted sequence using a few common objects. So the Timelines are very similar, but I have at least 15 timelines with many tracks each.

    If you can follow my simplified example timeline:

    1. Audio Track - One speaks - Bound to an AudioSource A
    2. Animation Track - Another animates - Bound to an AnimationController B

    There is a problem, that when I duplicate a Timeline, the bindings are lost.

    So when I duplicate the example above, there is a Timeline and it has two tracks. 1. AudioTrack, 2. Animation Track. But the duplicate is bound to neither AudioSource A nor AnimationController B

    I have to hit the heirarchy pane, locate both AudioSource A and AnimationController B and drag them to their respective slots.
     
  2. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    954
    The reason for this is the asset does not include any bindings. The tracks are themselves assets, and new tracks are made. We should add a 'duplicate with bindings option', but to help you out here is a helper script that will copy a playableDirector, timeline, and all the bindings.



    Code (CSharp):
    1.    
    2. public class DuplicateTimeline
    3. {
    4.     [MenuItem("Timeline/Duplicate With Bindings", true)]
    5.     public static bool DuplicateWithBindingsValidate()
    6.     {
    7.         if (UnityEditor.Selection.activeGameObject == null)
    8.             return false;
    9.  
    10.         var playableDirector = UnityEditor.Selection.activeGameObject.GetComponent<PlayableDirector>();
    11.         if (playableDirector == null)
    12.             return false;
    13.  
    14.         var playableAsset = playableDirector.playableAsset;
    15.         if (playableAsset == null)
    16.             return false;
    17.  
    18.         var path = AssetDatabase.GetAssetPath(playableAsset);
    19.         if (string.IsNullOrEmpty(path))
    20.             return false;
    21.  
    22.         return true;
    23.     }
    24.  
    25.     [MenuItem("Timeline/Duplicate With Bindings")]
    26.     public static void DuplicateWithBindings()
    27.     {
    28.         if (UnityEditor.Selection.activeGameObject == null)
    29.             return;
    30.  
    31.         var playableDirector = UnityEditor.Selection.activeGameObject.GetComponent<PlayableDirector>();
    32.         if (playableDirector == null)
    33.             return;
    34.  
    35.         var playableAsset = playableDirector.playableAsset;
    36.         if (playableAsset == null)
    37.             return;
    38.  
    39.         var path = AssetDatabase.GetAssetPath(playableAsset);
    40.         if (string.IsNullOrEmpty(path))
    41.             return;
    42.  
    43.         string newPath = path.Replace(".", "(Clone).");
    44.         if (!AssetDatabase.CopyAsset(path, newPath))
    45.         {
    46.             Debug.LogError("Couldn't Clone Asset");
    47.             return;
    48.         }
    49.  
    50.         var newPlayableAsset = AssetDatabase.LoadMainAssetAtPath(newPath) as PlayableAsset;
    51.         var gameObject = GameObject.Instantiate(UnityEditor.Selection.activeGameObject);
    52.         var newPlayableDirector = gameObject.GetComponent<PlayableDirector>();
    53.         newPlayableDirector.playableAsset = newPlayableAsset;
    54.  
    55.         var oldBindings = playableAsset.outputs.ToArray();
    56.         var newBindings = newPlayableAsset.outputs.ToArray();
    57.  
    58.         for (int i = 0; i < oldBindings.Length; i++)
    59.         {
    60.             newPlayableDirector.SetGenericBinding(newBindings[i].sourceObject,
    61.                     playableDirector.GetGenericBinding(oldBindings[i].sourceObject)
    62.                 );
    63.         }
    64.     }
    65. }
    66.  
     
    truebototics likes this.
  3. tezza2k1

    tezza2k1

    Joined:
    Feb 16, 2017
    Posts:
    13
    Thanks a lot Sean !! ( I guess from your username ). That worked a treat. There was a small compilation error in your script, but I am using the experimental 4.6 .Net version so that may be it. There was no ToArray() method on the outputs.

    For other forum users, I have included my fixes here :

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. using UnityEngine.Playables ;
    5.  
    6. ---------------------
    7.  
    8. var oldBindings = playableAsset.    outputs.GetEnumerator();
    9. var newBindings = newPlayableAsset. outputs.GetEnumerator();
    10.  
    11.  
    12. while( oldBindings.MoveNext() )
    13. {
    14.     var oldBindings_sourceObject = oldBindings.Current.sourceObject ;
    15.  
    16.     newBindings.MoveNext();
    17.  
    18.     var newBindings_sourceObject = newBindings.Current.sourceObject ;
    19.  
    20.  
    21.     newPlayableDirector.SetGenericBinding(
    22.         newBindings_sourceObject,
    23.         playableDirector.GetGenericBinding( oldBindings_sourceObject )
    24.     );
    25. }
     
    Flurgle and truebototics like this.
  4. truebototics

    truebototics

    Joined:
    Jan 2, 2016
    Posts:
    21
    So awesome. Thank you.... This is exactly what I was looking for...

    Question though - where would I put / paste this code?
     
  5. truebototics

    truebototics

    Joined:
    Jan 2, 2016
    Posts:
    21
    Got it working.. Here's the combination of the two scripts for those interested.
    Thanks to @Tezza and @seant_unity for the code.

    Unity Timeline is amazing!!!!

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using UnityEngine.Playables ;
    4.  
    5. public class DuplicateTimeline
    6. {
    7.     [MenuItem("Timeline/Duplicate With Bindings", true)]
    8.     public static bool DuplicateWithBindingsValidate()
    9.     {
    10.         if (UnityEditor.Selection.activeGameObject == null)
    11.             return false;
    12.  
    13.         var playableDirector = UnityEditor.Selection.activeGameObject.GetComponent<PlayableDirector>();
    14.         if (playableDirector == null)
    15.             return false;
    16.  
    17.         var playableAsset = playableDirector.playableAsset;
    18.         if (playableAsset == null)
    19.             return false;
    20.  
    21.         var path = AssetDatabase.GetAssetPath(playableAsset);
    22.         if (string.IsNullOrEmpty(path))
    23.             return false;
    24.  
    25.         return true;
    26.     }
    27.  
    28.     [MenuItem("Timeline/Duplicate With Bindings")]
    29.     public static void DuplicateWithBindings()
    30.     {
    31.         if (UnityEditor.Selection.activeGameObject == null)
    32.             return;
    33.  
    34.         var playableDirector = UnityEditor.Selection.activeGameObject.GetComponent<PlayableDirector>();
    35.         if (playableDirector == null)
    36.             return;
    37.  
    38.         var playableAsset = playableDirector.playableAsset;
    39.         if (playableAsset == null)
    40.             return;
    41.  
    42.         var path = AssetDatabase.GetAssetPath(playableAsset);
    43.         if (string.IsNullOrEmpty(path))
    44.             return;
    45.  
    46.         string newPath = path.Replace(".", "(Clone).");
    47.         if (!AssetDatabase.CopyAsset(path, newPath))
    48.         {
    49.             Debug.LogError("Couldn't Clone Asset");
    50.             return;
    51.         }
    52.  
    53.         var newPlayableAsset = AssetDatabase.LoadMainAssetAtPath(newPath) as PlayableAsset;
    54.         var gameObject = GameObject.Instantiate(UnityEditor.Selection.activeGameObject);
    55.         var newPlayableDirector = gameObject.GetComponent<PlayableDirector>();
    56.         newPlayableDirector.playableAsset = newPlayableAsset;
    57.  
    58.         var oldBindings = playableAsset.    outputs.GetEnumerator();
    59.         var newBindings = newPlayableAsset. outputs.GetEnumerator();
    60.  
    61.  
    62.         while( oldBindings.MoveNext() )
    63.         {
    64.             var oldBindings_sourceObject = oldBindings.Current.sourceObject ;
    65.  
    66.             newBindings.MoveNext();
    67.  
    68.             var newBindings_sourceObject = newBindings.Current.sourceObject ;
    69.  
    70.  
    71.             newPlayableDirector.SetGenericBinding(
    72.                 newBindings_sourceObject,
    73.                 playableDirector.GetGenericBinding( oldBindings_sourceObject )
    74.             );
    75.         }
    76.     }
    77. }
     
    YAMAYAMAYAMA and Flurgle like this.
  6. awasem

    awasem

    Joined:
    Nov 25, 2015
    Posts:
    3
    Create a C# script, copy and paste this code into MonoDevelop when it opens, and name it something like duplicateTimeline. Now with your timeline selected from the Timeline menu at the top there is an option to Duplicate With Bindings. Awesome! Thanks @Tezza and @seant_unity for the code.
     
  7. awasem

    awasem

    Joined:
    Nov 25, 2015
    Posts:
    3
    Any way to edit this script to keep the duplicated timeline gameObject on the canvas? When duplicate a timeline asset it moves out of canvas. Then when I bring it back into the canvas its the wrong size.
     
  8. GonzoGan

    GonzoGan

    Joined:
    Mar 15, 2013
    Posts:
    38
    Not working on Unity 2018.2.1f1
     
  9. AdamBL

    AdamBL

    Joined:
    Oct 27, 2016
    Posts:
    29
    Hey @GonzoGan I think it is working in 2018.2, it just works in a kind of weird way. Some of the issues I had were:

    1. You need to have the GameObject that has the Playable Director component selected in your scene in order to duplicate it (not in your Project window)
    2. When you duplicate it, it creates a Clone GameObject in your scene with the duplicated Timeline in its PlayableDirector slot (rather than modifying the original GameObject)
    3. Most confusing is the fact that the duplicated Timeline still references the original GameObjects, even if it created new ones.
    So in our scene, we have all our animated elements as children of the object with the Playable Director component, and when we duplicated the Timeline it duplicated all those objects. However, the new Timeline still controlled the original objects, so instead of using the Cloned object, I simply copied the Playable Director component and pasted it on the original parent object.
     
  10. Kareeem

    Kareeem

    Joined:
    Mar 1, 2013
    Posts:
    28
    The script works without any issues in 2018.2.15f1
     
  11. Flurgle

    Flurgle

    Joined:
    May 16, 2016
    Posts:
    368
    Duplicating with bindings is not working in Unity 2019.1a10 either
     
  12. MikeFish

    MikeFish

    Joined:
    Oct 24, 2015
    Posts:
    12
    I could need some help.
    It's not working for me

    Unity 2018.3.4f1

    It clones the Timeline but without any bindings, it is doing exactly the same as ctrl D in the project window.
    I followed all instructions I could find here without any positive result.

    any help would be very appreciated.
    thanks,
    Mike

    ----------------------------
    Edit:
    my bad,
    wasn't noticing that only the new created Director hold all bindings,
    what is logical when you think about it.
    So, yeah it is working.
     
    Last edited: Feb 6, 2019
    thierry_unity likes this.
  13. PawleyBoboli

    PawleyBoboli

    Joined:
    Jan 27, 2015
    Posts:
    10
    This works great and I get no errors compiling it or using it during production. The Timeline Menu Item shows up in my editor and the duplication-with-bindings works perfect. Thanks for this!

    However, I do get errors when trying to publish a Build to WebGL. The build fails with this error in the console:

    "Assets/Timelines/DuplicateTimeline.cs(7,6): error CS0246: The type or namespace name 'MenuItem' could not be found (are you missing a using directive or an assembly reference?)"

    I cannot find any problems with the script, and I found that I can build if I just remove it from the project. Not a big issue, but if anyone has a solution so the script does not need removing, I would appreciate any info.

    Thanks.


     
  14. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    954
    Put the script in a folder named Editor. It will be excluded from player builds then. Or if you need it in a player, wrap the using UnityEditor; and [MenuItem(..)] lines with a #if UNITY_EDITOR / #endif.
     
    PawleyBoboli likes this.
  15. PawleyBoboli

    PawleyBoboli

    Joined:
    Jan 27, 2015
    Posts:
    10
    Thanks Sean. Putting it into one of the many "Editor" folders that already existed in the project worked as you described.
     
  16. twinsense360

    twinsense360

    Joined:
    Oct 13, 2016
    Posts:
    8
    This doesnt work, all it does is duplicates my hierarchy objects and makes a timeline clone, then when i try to reassign the timeline clone to a different playable component all my bindings are gone again, so basically making this script useless. why is this so hard, all i want to be able to do is copy a timeline and then edit it so i have several different variations of the same timeline that can be assigned to a single playable object but every time i try to do this i loose all object bindings, my timeline has at least 30 object bindings and i need to copy it 7 times, it is far too tedious to manually reassign all the bindings every time i make a duplicate timeline, please create a solution for this, it has been an issue since the timeline was implemented, for a tool that is supposed to make you life easier every time i find myself using timeline i have issues like this that make me absolutely hat it, the system just feels broken and illogical to me. Not to mention all the other problems i have with the design of this tool, ( why is every new track put to the bottom of the timeline list) Little things like this make the timeline system a complete pain to use and i dread anytime i need to make an animated sequence with this tool please fix it Unity.
     
    SwingNinja and BaLala_Bala like this.
  17. Cleverlie

    Cleverlie

    Joined:
    Dec 23, 2013
    Posts:
    196
    for those who say it doesn't work because it copies the hierarchy of objects, of course it does! you don't read the code you just copy paste it and run it without trying to have at least a grasp of what it does?? I mean it's ok for non programmers because an artist might not even know what the code is doing and he just wants things to get done, but devs, please.

    the code is instantiating the original gameObject ALONG with all it's children, and then rebinds the director bindings in the duplicate director, to the bindings in the original director, meaning that if you are using objects that are children of the game object that has the director component, then you will have a duplicate hierarchy of objects that are doing nothing, you still have bindings to the original objects.

    what you want to do is: instead of instantiate(), just create a new gameobject, add a playableDirector to it, and then only do the rebinding part of the code.
     
  18. Cleverlie

    Cleverlie

    Joined:
    Dec 23, 2013
    Posts:
    196

    P.S: this should really be an out of the box feature, at least when we right click the PlayableDirector component and do "Copy Component" then "Paste Component Values" should automatically rebind the bindings, which is not the case
     
    SwingNinja likes this.
  19. romi-fauzi

    romi-fauzi

    Joined:
    Aug 16, 2013
    Posts:
    147
    I created a simple script to do this, to use it simply copy the Playable Asset on the project, you'll see it will lose all bindings when applied to the PlayableDirector Component, add this script below, and assign the PlayableDirector component to the TimelineComponent slot, the source PlayableAsset (with working binding) and the target PlayableAsset (the copied one), and then choose "Duplicate Timeline" from the context menu. After this, you'll be able to assign the copied timeline to the original PlayableDirector component, with the right bindings intact.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Playables;
    3.  
    4. public class CopyTimeline : MonoBehaviour
    5. {
    6.     [SerializeField] PlayableDirector timelineComponent;
    7.     [SerializeField] PlayableAsset sourceTimeline, targetTimeline;
    8.  
    9.     [ContextMenu("Duplicate Timeline")]
    10.     public void Duplicate()
    11.     {
    12.         var oldBindings = sourceTimeline.outputs.GetEnumerator();
    13.         var newBindings = targetTimeline.outputs.GetEnumerator();
    14.  
    15.         while (oldBindings.MoveNext())
    16.         {
    17.             var oldBindings_sourceObject = oldBindings.Current.sourceObject;
    18.  
    19.             newBindings.MoveNext();
    20.  
    21.             var newBindings_sourceObject = newBindings.Current.sourceObject;
    22.  
    23.  
    24.             timelineComponent.SetGenericBinding(
    25.                 newBindings_sourceObject,
    26.                 timelineComponent.GetGenericBinding(oldBindings_sourceObject)
    27.             );
    28.         }
    29.     }
    30. }
     
    cirocontinisio likes this.