For various reasons I want to manage my own PlayableGraph
created by using TimelineAsset.CreatePlayable()
(see this post for context Simple suggestion to prevent performance issues caused by Timeline / PlayableDirector).
I do this like this:
public static PlayableGraph CreateStandalonePlayableGraphWithBindingsFromDirector( this PlayableDirector director, GameObject graphOwner, Dictionary< Type, InitialiseTimelineTrackOutputBinding > dictTrackTypeToCustomBindingHandler )
{
var playableGraph = PlayableGraph.Create( $"{graphOwner.name}-Standalone-{director.playableAsset.name}" );
director.playableAsset.CreatePlayable( playableGraph, graphOwner );
//
// then I set up output bindings by iterating the TimelineAsset outputs and matching against the
// playable graph outputs (I can share code if it helps
//
The above works fine for all Timeline track asset types I’ve tested except ControlTrack
.
ControlTrack
has no output binding, and instead is made up of clips which each have a reference to a GameObject
and/or a Prefab
.
I can iterate the ControlTrack
clips in the TimelineAsset
, and should be able to set the clip bindings like this…
foreach( var timelineClip in controlTrack.GetClips() )
{
var clipAsset = timelineClip.asset as ControlPlayableAsset;
var controlClipBinding = director.GetReferenceValue( clipAsset.sourceGameObject.exposedName, out var isValid );
if( isValid )
{
// ...resolver is null after this line...
var resolver = playableGraph.GetResolver();
resolver.SetReferenceValue( clipAsset.sourceGameObject.exposedName, controlClipBinding );
}
}
but the value returned as resolver
by playableGraph.GetResolver()
is always null
.
How can I get a valid resolver for the PlayableGraph
without it being owned by a PlayableDirector
??
It seems to me that I need to create a class which implements IExposedPropertyTable
which somehow links all the ExposedReference
instances on the ControlPlayableAsset
clips in the ControlTrack
to their values.
There is little to no detail on how IExposedPropertyTable
is supposed to work or what its role is exactly - I assumed that the GameObject
reference on the ControlPlayableAsset
were somehow set in the graph (similarly to how PlaybleOutput
instances are bound to scene objects by using PlaybleOutput.SetUserData
)…
…however the functions in the IExposedPropertyTable
API imply that ExposedReference
values are looked up in the IExposedPropertyTable
in real time, and that exposed references do a live look up in the graphs’s IExposedPropertyTable
at runtime do get their binding - is this correct?
I got to the bottom of this.
As it turns out, you can trivially create an IExposedPropertyTable
as essentially a wrapper over a dictionary and pass that to PlayableGraph.SetResolver()
.
However, the behaviour of the ControlTrack
asset when creating the playable(s) in inserts into the PlayableGraph
is to initialise all the ExposedReference
values once on creation and they are not resolved again.
For my use case of wanting to create a PlayableGraph
containing a Timeline
not owned by a PlayableDirector
but to copy all the bindings from a PlayableDirector
then you need to:
- ensure that all the
ExposedReference
values from the PlaybleDirector
are in the your custom IExposedPropertyTable
- and that you have assigned it to the
PlayableGraph
you’re creating the timeline in before you pass it TimelineAsset.CreatePlayable()
The code I have is a bit large for posting inline, so I’ve made a .unitypackage
of a test scene and the proof of concept code I used to achieve it - will post it in the reply to this…
I hope it helps someone else because it took me waaaay too long to work all this out!!

Also, as I mentioned in this post Simple suggestion to prevent performance issues caused by Timeline / PlayableDirector …
this whole thing would have been avoidable there was the option to prevent PlaybleDirector
from automatically destroying its PlayableGraph
on Stop()/Disable()/Timeline End
(e.g. Preserve & Reuse PlayableGraph
)
Here’s a gist with the proof of concept code to:
- manually create a PlaybleGraph from a
PlayableDirector
’s TimelineAsset
- initialise it using all the bindings set up on the
PlayableDirector
- play the graph, stop the graph, restart the graph
And here’s a link to a Unity package with a sample scene showing it working: TimelineTest.unitypackage - Google Drive
Known limitations:
- this copies bindings which are visible in the
PlayableDirector
’s inspector (i.e. those accessible via PlayableDirector.GetGenericBinding
)
- it also copies bindings for
ControlTrack
timeline tracks which rely on IExposedPropertyTable
/ PlayableDirector.GetReferenceValue
- other types of timeline track which use this method of binding to scene objects will require additional code to handle them