Search Unity

Mute Tracks or Add Animations to Instance of Playable from Script

Discussion in 'Timeline' started by tcmeric, Jul 22, 2017.

  1. tcmeric

    tcmeric

    Joined:
    Dec 21, 2016
    Posts:
    190
    I am looking to see if it is possible to get all of the instances of playables within a playableDirector component.

    I am somewhat of a beginner c# programmer, so I wasent able to see a way via the API documentation, and havent seen any examples yet. I dug through all the examples posted on this thread. (Which have been helpful in writing the other C# snippets/actions).

    I want to be able to mute tracks for example, in a running timeline, or add animations at X time, etc.

    Any example scripts available?
     
  2. CraigGraff

    CraigGraff

    Joined:
    May 7, 2013
    Posts:
    44
    You might be interested in something like this:
    Code (CSharp):
    1.    
    2.     public PlayableDirector collectedTimeline;
    3.     public string playerAnimationTrackName;
    4.  
    5.     void BindPlayerToAnimationTrack(){
    6.  
    7.         //Loop through timeline tracks and find the desired track to bind the player to
    8.         foreach (var playableAssetOutput in collectedTimeline.playableAsset.outputs) {
    9.             if (playableAssetOutput.streamName == playerAnimationTrackName) {
    10.                 collectedTimeline.SetGenericBinding (playableAssetOutput.sourceObject, player);
    11.             }
    12.         }
    13.  
    14.     }
    It's from the project linked in the comments of this video.
     
  3. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    During playback, the playable graph is available through the playableGraph property on the playable director. You can grab the outputs from the graph - each track is represented by an output, or you can parse the graph from the root (playableGraph.GetRootPlayable(0)).

    Most tracks have a mixer that represents the track. This would be the input to the root. Then the inputs to the mixer are the playables representing the clip.

    Animation is more complex, but the leafs nodes are AnimationClipPlayables.

    Hope that helps.
     
  4. tcmeric

    tcmeric

    Joined:
    Dec 21, 2016
    Posts:
    190
    Thank you both. I will give that a try and see what I can do. :)
     
  5. SpiralConDave

    SpiralConDave

    Joined:
    Dec 29, 2014
    Posts:
    37
    Anything more concrete from this? Ex: how to actually mute a track at runtime?
     
  6. Immu

    Immu

    Joined:
    Jun 18, 2013
    Posts:
    240
    Same thing question here. I do get the whole playableGraph.GetRootPlayable(0), but what next once we have that? What's the missing link that could get us to 'animationTrack.muted = true' ?
    I've read about many complicated ways. Including some AnimationPlayableOutput thingy, but that aren't recognized in Unity 2018.2.4f1.

    I've also tried some AnimationTrack a = playableDirector.GetGenericBinding(animator) as AnimationTrack;
    Debug.Log(a);
    but nothing happens, the AnimationTrack isn't recognized

    The goal is always the same: how to simply 'mute' a playable track from script ?
     
  7. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    While the timeline is playing

    animationTrack.muted = true;
    playableDirector.RebuildGraph();

    should work (rebuilding can be expensive though).

    or, alternately you can strip the target animator on the output. e.g.

    Code (CSharp):
    1.         public void RuntimeMute(AnimationTrack track)
    2.         {
    3.             for (int i=0; i< playableDirector.playableGraph.GetOutputCountByType<AnimationPlayableOutput>(); i++)
    4.             {
    5.                 var output = (AnimationPlayableOutput) playableDirector.playableGraph.GetOutputByType<AnimationPlayableOutput>(i);
    6.                 if (output.GetReferenceObject() == track)
    7.                     output.SetTarget(null);
    8.             }
    9.         }
    10.  
     
    Oneiros90 likes this.
  8. Immu

    Immu

    Joined:
    Jun 18, 2013
    Posts:
    240
    Ok, so playableDirector.playableGraph.GetOutputByType<AnimationPlayableOutput>(i) is an other way to get the AnimationTrack. But, as I said, there's a weird behaviour in Unity 2018.2.4.f1, AnimationPlayableOutput isn't recognized. (everything else is, using Timeline and all..). Am I missing something or could that be an issue in this version ?
     
  9. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    Include UnityEngine.Animations. The animation-specific playable classes lives there.
     
  10. Immu

    Immu

    Joined:
    Jun 18, 2013
    Posts:
    240
    Oh. Yeah. of course ^^. Thanks.
    As I'm still working on the topic:
    I'm in the TextSwitcher default playable,and I would like to know if It's possible to get the current track the clip is on?
    So that I can test if the track is mute, and if so disable its functionality.
    I suppose I should get access at least to the director or such to do so ? But didn't found so far.
     
    Last edited: Aug 23, 2018
  11. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    There are a couple of ways to get the director.

    CreatePlayable() passes a gameObject. For timeline, that is the game object the playable director belongs to.

    Or, you can ask a playable for it's PlayableGraph. From there you can call PlayableGraph.GetResolver() -> it's an IExposedPropertyTable, but that's actually the playable director as well.

    Or, the FrameData passed into Prepare/ProcessFrame contains the PlayableOutput currently being processed.
    GetReferenceObject() on the playable output should give you the track that created it.
     
  12. Immu

    Immu

    Joined:
    Jun 18, 2013
    Posts:
    240
    Everything felt a bit cryptical ^^. Thanks for clarification :)
     
  13. Immu

    Immu

    Joined:
    Jun 18, 2013
    Posts:
    240
    ^^ Got a new problem. Weirder this time.
    Here's my example:
    On entering new clips with timeline's reading head, I'm checking if they're are of type TextSwitcher. If so, in their mixerBehaviour, I've added some function that asks to mute an AudioTrack, below in the same timeline.

    Editor mode, everything works, Playmode: everything slowly falls apart. i've got hard time to understand how it slowly degradates.

    For example, if the director does one entire play till the end, (in which the audio gets mute/unmute regularly depending on the TextSwitcher track above), once the head goes back to the start of the timeline, audio track, even unmute, displays correctly the change on the timeline, but does not give any sound anymore. It's super weird :/ (or same thing If I manually move the reading head a few times accross the timeline)

    I felt like maybe I should do some 'RebuildGraph' after muting/unmuting a track. But when I do so, I get warnings telling me that I can't do it while the graph is being evaluated. And it seems to be evaluated all the time.

    So yeah... bit lost again, can you help?
    (using 2018.2.5f1)
     
  14. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    Flag the graph to be rebuilt, and rebuild it from a monobehaviour (e.g. in an update method). Rebuilding the graph from Prepare or Process frame doesn't work because it's in the middle of execution.
     
  15. Immu

    Immu

    Joined:
    Jun 18, 2013
    Posts:
    240
    Actually, I was already doing that part of doing the rebuild within a monobehaviour, in an update method ^^'
    But maybe I'm missing the flagging part. Not sure what you meant by 'flag the graph to be rebuild'..
     
  16. CraigGraff

    CraigGraff

    Joined:
    May 7, 2013
    Posts:
    44
    Have a static boolean act as "flag". Set it to true when you want the graph to be rebuilt, then when your monobehaviour (which is checking that same bool) fires, set the boolean back to false.
     
    Immu likes this.
  17. Oneiros90

    Oneiros90

    Joined:
    Apr 29, 2014
    Posts:
    78
    Thanks for this code, it works fine but after some testing I found two limitations:
    1. Only AnimationPlayableOutput has the SetTarget method, this means that I cannot mute tracks of other types right? Is there a generic method for IPlayableOutput?
    2. I have a timeline with some Override Tracks on my Animation Track. When I mute the parent track in editor the children are still played, unless I also mute them. Using the runtime method provided however the children are muted too and that's not what I want. Is there another way to achieve the same result?
     
  18. spider853

    spider853

    Joined:
    Feb 16, 2018
    Posts:
    42

    Sorry for bumping this thread but it's very hard to find how to mute an Override track, say I have the AnimationPlayable X has an override track Y, how do I access it and mute it?

    at the moment I'm using the TimelineAsset to mute it indirectly and rebuild the graph
    Code (csharp):
    1.  
    2. track = Timeline.playableAsset.outputs.Where(x => x.streamName == "MainAnimTrack").First().sourceObject;
    3. AnimationTrack animTrack = track as AnimationTrack;
    4. if (animTrack != null)
    5. {
    6.        foreach (TrackAsset childTrack in animTrack.GetChildTracks())
    7.         {
    8.                Debug.Log(childTrack.name);
    9.                childTrack.muted = muteOverride;
    10.         }
    11.         Timeline.RebuildGraph();
    12. }
    13.  
    But wish I could do it in the graph directly
     
    Last edited: May 4, 2023
  19. spider853

    spider853

    Joined:
    Feb 16, 2018
    Posts:
    42
    So I'll answer my own question, found how to remove tracks sadly weighting doesn't work as it's get reset the next frame for whatever reason

    Code (CSharp):
    1.  
    2. for (int i = 0; i < director.playableGraph.GetRootPlayableCount(); i++)
    3.                 Process(director.playableGraph.GetRootPlayable(i));
    4.  
    Code (CSharp):
    1.  
    2. void Process(Playable playable, int depth = 0)
    3.     {
    4.         if (playable.GetPlayableType() == typeof(AnimationLayerMixerPlayable))
    5.         {
    6.             AnimationLayerMixerPlayable mixer = (AnimationLayerMixerPlayable)playable;
    7.             Debug.Log("Found Mixer!" + mixer.GetInputCount());
    8.             Debug.Log(mixer.CanSetWeights());
    9. !!!  //mixer.SetInputWeight(1, 0f);  <<<----- this doesn't work, it works only if you pause the graph, but if you resume it gets reset
    10.             mixer.DisconnectInput(1);
    11.             return;
    12.         }
    13.         Debug.Log(new string('>', depth + 1) + playable.GetPlayableType().ToString());
    14.         for (int i = 0; i < playable.GetInputCount(); i++)
    15.         {
    16.             Process(playable.GetInput(i), depth + 1);
    17.         }
    18.     }
    19.  
     
  20. spider853

    spider853

    Joined:
    Feb 16, 2018
    Posts:
    42
    So seems like I found how to weight down the layer mixer, but wish an official will state why exactly this is the behavior and if it's the intended one.

    So to disable a branch of a mixer, you need to disable the inputs of the children sub mixers
    upload_2023-5-11_17-31-21.png

    this is the minimal actions that needs to be done to set all branch to 0 and to make Animation Layer Mixer not get back to 1.0.

    Code (CSharp):
    1.  
    2. //mixer.SetInputWeight(1, 0f); <--- 2nd AnimationLayerMixer branch, gets reset next frame
    3. //mixer.GetInput(1).SetInputWeight(0, 0f); <---- Animation Offset, doesn't do anything
    4. mixer.GetInput(1).GetInput(0).SetInputWeight(0, 0f); <----- Animation Mixer, this one does it!
    5. //mixer.GetInput(1).GetInput(0).GetInput(0).SetInputWeight(0, 0f); <---- Animation Clip, doesn't change anything
    6. //mixer.DisconnectInput(1); <--- If you don't want to play with weights, do a hard cut instead
    7.