Set PlayableDirector bindings from script

Hi guys.

I have a lot of custom Playables and all of it have a same TrackBindingType (MyType, for example).

[TrackClipType(typeof(ClipType1))]
[TrackBindingType(typeof(MyType))]
public class Type1Track : TrackAsset
{
    //Some code here
}

e.t.c.

Idea is, load a timeline asset from resources set it to GO Director and instantiate all of Director bindings with the same component typeof MyType form this GO.

I understang how to do it manually, but manually setting ruin all the conception. I wonder it must be something like that:

    void Start()
    {
        PlayableDirector director = this.GetComponent<PlayableDirector>();
        director.playableAsset = this.TimelineAsset;
        //Set bindings here
        director.SOME_BIND_METHOD(GetComponent<MyType>());

        director.Play();
    }

Any suggestions. Thanks.

The method you are looking for is PlayableDirector.SetGenericBinding.

The first parameter is a reference to the track, which you can either from iterating through the timeline assets, or it’s also the sourceObject field of the PlayableBinding struct returned from PlayableAsset.outputs.

The second parameter is the component you want to bind to the track.

4 Likes

Thanks for answer. Another question.
Is there any method to get this binded object inside of PlayableBehaviour exept void ProcessFrame(Playable playable, FrameData info, object playerData) method?
For exmple I need access to “object playerData” (my binded object) variable at the start of playable track plays or at the start of Graph Plays. Methods OnBehaviourPlay and OnGraphStart dont have an “object playerData” argument.

No, but there are a couple of workarounds.

If you are using a Mixer Playable for the track, ProcessFrame runs every frame. You can grab it on the first frame that’s called and pass it to it’s inputs if you need it on clip playables.

Or you pass it down from the track… something like

public class MyPlayableAsset
{
    public Object binding { get; set; }
    ....
   
    public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
    {
            // pass the binding to the playable instance
            var handle = ScriptPlayable<ParticleControlPlayable>.Create(graph, template);
            handle.GetBehaviour().binding = binding;
            return handle;
    }
   
}

public class MyTrack : TrackAsset
{
     ...
        public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
        {
              // before building, update the binding field in the clips assets;
              var director = go.GetComponent<PlayableDirector>();
              var binding = director.GetGenericBinding(this);
             
              foreach (var c in GetClips())
              {
                    var myAsset = c.asset as MyPlayableAsset;
                    if (myAsset != null)
                        myAsset.binding = binding;             
              }
   
              return base.CreateTrackMixer(graph, go, inputCount);
        }

}
1 Like

Thanks, this code work fine. One more thing.

I try change playable speed at runtime like this:

            PlayableDirector director = this.GetComponent<PlayableDirector>();
            for (int i = 0; i < director.playableGraph.GetOutputCount(); i++)
            {
                director.playableGraph.GetOutput(i).GetSourcePlayable().SetSpeed(0.1F);
            }
            director.Play();

But its throw a “InvalidOperationException: This PlayableGraph is invalid. To create a valid PlayableGraph, please use the CreateGraph method.” exception.

Is there any method to change playable speed at runtime?

That will do it, but the playablegraph isn’t valid until after director.Play() is called.

I suppose that you can not set speed of individual tracks of timeline, but you should set speed of the director itself. The whole timeline playable tracks are controlled via rootplayable graph so you should try the following code.

director.playableGraph.GetRootPlayable().SetSpeed();

Right – msl_manni is absolutely correct.

HI, can someone help me please?
How can i take all my Playable Director’binding objects to a list,i want to create a list that contains all binding objects from my current Playable Director.

And i dont know how to right it,so can you make a small script as example for me to catch he ideia please.

1 Like

There isn’t an API call to do it, but you can use the serialized properties to discover the internal list.

    public static void DisplayBindings(PlayableDirector director)
    {
        var obj = new SerializedObject(director);
        var bindings = obj.FindProperty("m_SceneBindings");
        for (int i = 0; i < bindings.arraySize; i++)
        {
            var binding = bindings.GetArrayElementAtIndex(i);
            var trackProp = binding.FindPropertyRelative("key");
            var sceneObjProp = binding.FindPropertyRelative("value");
            var track = trackProp.objectReferenceValue;
            var sceneObj = sceneObjProp.objectReferenceValue;

            Debug.LogFormat("Binding {0} {1}", track != null ? track.name : "Null", sceneObj != null ? sceneObj.name : "Null");
        }
    }

Is it possible to get to the gameobject of the sceneObjProp

i would like to deactive the current blinded object in a timeline

PlayableDirector.GetReferenceValue() will retrieve the gameObject. You need the key value, which is the exposedName field of the ExposedReference on your clip.

e.g. something like

bool isValid = false;
var exposedName = ((ControlPlayableAsset) clip.asset).sourceGameObject.exposedName;
var binding = director.GetReferenceValue(exposedName, out isValid);

I had to do something similar - in my case swapping out the brain used in any timeline track of a child. It’s my first brain surgery :smile:

    void TransplantBrain(PlayableDirector director, CinemachineBrain newBrain) {
      var outputs = director.playableAsset.outputs;
      foreach (var output in outputs) {
        if (output.outputTargetType == typeof(CinemachineBrain)) {
          director.ClearGenericBinding(output.sourceObject);
          director.SetGenericBinding(output.sourceObject, newBrain);
        }
      }
    }

I should also mention that in order for this to work I had to “jiggle the handle” by disabling and re-enabling the brain.

        var directorList = page.GetComponentsInChildren<PlayableDirector>();
        newBrain.enabled = false;
        foreach (var director in directorList) {
          TransplantBrain(director, newBrain);
        }
        newBrain.enabled = true;
      }
3 Likes

Even the simple task of setting up multiple PlayableDirector bindings on a prefab and then switching between them with a script as needed feels obtuse. I just want to switch the Playable here between the different bindings already on the director.

I can see the bindings and the playable assets right there in the Editor, but I can’t figure out how to navigate these APIs to get to them.

3 Likes

Has this API matured at all? It still looks too low-level glancing at the API docs.

As a footnote, my game’s development could easily be 2x more productive if Unity’s APIs were more mature and developer friendly. It doesn’t appear Unity is putting enough effort into this. As someone who put aside a lucrative Silicon Valley tech leadership career to pursue my dream of making a video game, this is literally costing me 100s of thousands of dollars.

3 Likes