Search Unity

Modify graph by code

Discussion in 'Timeline' started by paintbox1, Aug 11, 2017.

  1. paintbox1

    paintbox1

    Joined:
    Jan 20, 2014
    Posts:
    36
    Hi, I'm trying to create a voice over clip, which in my imagination consists of
    one audio clip + multiple viseme clips(changes of the mouth blendshape values according to the heard phonemes at the corresponding timestamps)

    The voice over clip gets all the information from an ExposedReference to a custom voice over Asset.

    1. Can I embed audio and viseme clips in my voice over clip?
    2. Can I automatically create these audio and viseme "subclips" when the ExposedReference to the asset in the voice over clip gets assigned? Or maybe by a button near the ExposedReference field?

    I'd be grateful for any suggestions.

    Thanks
     
  2. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    That sounds super interesting!

    So here are a few suggestions that might help.
    - If your custom voice over is an Asset (i.e. on disk, and not in scene), you don't need an exposed reference. Just a regular C# reference will work.
    - For the audio, your best option is to create and reference a separate audio track. The timeline API lets you create/move/modify/delete clips and tracks.
    - You can create a custom editor for your voice over clip playable asset to add buttons. Or, probably better, is create a custom editor for your custom track that does the audio track management. The tracks give you much more access to timeline data than the clip playable assets do.

    Hope that helps!!
     
    paintbox1 likes this.
  3. paintbox1

    paintbox1

    Joined:
    Jan 20, 2014
    Posts:
    36
    Thanks, that really helped me! (I've sent you a PM)

    But now after I created the audio and viseme tracks and clips the timeline editor doesnt refresh automatically. Can I somehow force this when I'm done?
     
  4. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    Ah right -- we've nothing exposed in the public API to do that. I will definitely put that on our backlog as something that is necessary to add.

    I did a bit of hunting for a workaround until we can add that, and here's what I came up with. Here's a bit of background in case someone comes up with something better

    The timeline window has a few different refresh modes
    - basic refresh just redraws and evaluates the Timeline playable graph. Most common. (fast)
    - RebuildGraph will rebuild the timeline graph, but keeps the same view. Used when data is modified. (slower)
    - RebuildView will rebuild the timeline view. Used when clips and tracks are added or removed. (also rebuilds the graph). (slowest)

    In your case you need to do it all. The workaround is using the Undo system. This code tricks the TimelineWindow into doing rebuilding everything. It's not pretty but it works.

    Alternately you can try changing the selection over multiple frames.

    In the code the 'RebuildTimeline' object could be your track.

    Code (CSharp):
    1.     public override void OnInspectorGUI()
    2.     {
    3.         if (GUILayout.Button("Rebuild"))
    4.         {
    5.             var dummyObject = target as RebuildTimeline;
    6.             var timeline = TimelineEditor.timelineAsset;
    7.             if (timeline != null)
    8.             {
    9.                 timeline.CreateTrack<AnimationTrack>(null, "Blah");
    10.  
    11.                 // fake an undo with Timeline in the name to cause a rebuild
    12.                 Undo.IncrementCurrentGroup();
    13.                 Undo.RecordObject(dummyObject, "Timeline Dummy Undo");
    14.                 dummyObject.dummy = dummyObject.dummy + 1;
    15.                 Undo.PerformUndo();
    16.                 var window = EditorWindow.GetWindow<EditorWindow>(false, "Timeline", false);
    17.                 if (window)
    18.                     window.Repaint();
    19.                 EditorGUIUtility.ExitGUI();
    20.             }
    21.         }
    22.  
     
    senkal_ and renaudbedard_SEM like this.
  5. paintbox1

    paintbox1

    Joined:
    Jan 20, 2014
    Posts:
    36
    Wow, didn't expect support on the weekend.

    Seems you're working with a newer API version. There's nothing called TimelineEditor in my version of unity.
    But I can just access the timeline through my track.

    Code (csharp):
    1.  
    2.     private VoiceOverTrack track
    3.     {
    4.         get
    5.         {
    6.             return this.target as VoiceOverTrack;
    7.         }
    8.     }
    9.  
    10.     private void UpdateTimelineEditorView()
    11.     {
    12.         var timeline = this.track.timelineAsset;
    13.         if (timeline != null)
    14.         {
    15.             // fake an undo with Timeline in the name to cause a rebuild
    16.             Undo.IncrementCurrentGroup();
    17.             Undo.RecordObject(this.track, "Timeline Dummy Undo");
    18.             this.track.muted = true;
    19.             Undo.PerformUndo();
    20.             var window = EditorWindow.GetWindow<EditorWindow>(false, "Timeline", false);
    21.             if (window)
    22.                 window.Repaint();
    23.             EditorGUIUtility.ExitGUI();
    24.         }
    25.     }
    26.  
    Calling this method after the modifications instantly displays the changes.
    Thanks again!
     
    senkal_ likes this.
  6. renaudbedard_SEM

    renaudbedard_SEM

    Joined:
    Sep 29, 2016
    Posts:
    4
    Hi! Slightly hijacking this thread but it's related. :)

    After creating a new track using CreateTrack<T>, it's always appended to the end of the timeline or track group.
    Is there any way to control the order of tracks within a group/timeline?
     
  7. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    Not through the Timeline API - this is something we should add an API call for though.

    If you need a workaround, the serialized property you can edit on TimelineAsset is named m_Tracks (It's a List<TrackAsset>). Retrieving and reordering that through reflections or serializedObjects should do the trick. For group tracks it's List<TrackAsset> m_Children.

    You might need to serialize, or add & remove a dummy track to get the internal caches to invalidate after changing it.
     
    renaudbedard_SEM likes this.
  8. prisonerjohn

    prisonerjohn

    Joined:
    Jul 31, 2013
    Posts:
    28
    Hey @seant_unity, is this undo workaround still the case? I'm writing a method to delete all clips on a track:

    Code (CSharp):
    1. public void ClearClips()
    2. {
    3.     m_Clips.Clear();
    4. }
    I don't see my changes reflected in the UI until I change some code somewhere and everything refreshes. Is there a cleaner way of doing this or is the undo trick still valid?
     
  9. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    If you use TimelineAsset.DeleteClip() it will clear the caches for you. Or the Undo trick works too.