Track processors are executed once per track during the TimelineMixer's setup phase, or when rebinding a track at runtime. They handle the setup of connections to mixers and outputs on the PlayableGraph, and also the disconnection process.
In short, they tell Timeline Mixer what to do with this track, and how to connect it on Timeline mixers PlayableGraph.
- Important
- Basic knowledge of the Unity Playables API is recommended before proceeding with this section. Reviewing Unitys Playables documentation will make this section easier to understand.
Let's look at the Playable Graph generated by the SignalTrackProcessor:
Track processors manage the section highlighted in red. They connect the Timeline to any required mixer and other playables, and then to the output (which is bound to your component).
Custom Track Processors
You can customize how the timeline connects to its outputs on the Playable Graph by using a custom Track Processor. If your custom track doesn't have a specialized track processor, TimelineMixer will use a default one.
To create one, follow these steps:
- Create a new C# script.
- Add the TrackProcessor attribute, passing in the type of the timeline track.
- Inherit from TrackProcessor instead of MonoBehaviour.
- Override the ProcessTrack method and connect your playables there.
- Override the DisconnectTrack method if you use custom disconnection logic for rebinding tracks
Here's an example, this is the LightControlTrackProcessor included with the TimelineMixer package:
[TrackProcessor(typeof(LightControlTrack))]
public class LightControlTrackProcessor : TrackProcessor
{
public Dictionary<Light, ScriptPlayableOutput> lightOutputs = new Dictionary<Light, ScriptPlayableOutput>();
private Dictionary<Playable, PlayableNode> mixerNodes = new Dictionary<Playable, PlayableNode>();
public override void ProcessTrack(PlayableGraph graph, TrackAsset track, ScriptPlayable<TimelinePlayable> playable, int trackIndex, Object boundComponent, ref PlayableConnectionInfo info)
{
ScriptPlayableOutput output;
Playable lightMixer;
PlayableNode lightMixerNode;
if (track.isEmpty && !track.hasClips) return;
if (boundComponent == null) return;
if (lightOutputs.ContainsKey(boundComponent as Light))
{
lightOutputs.TryGetValue(boundComponent as Light, out output);
lightMixer = output.GetSourcePlayable();
mixerNodes.TryGetValue(lightMixer, out lightMixerNode);
}
else
{
output = ScriptPlayableOutput.Create(graph, track.name);
lightOutputs.Add(boundComponent as Light, output);
lightMixer = ScriptPlayable<LightControlMixer>.Create(graph);
ScriptPlayable<LightControlMixer> scriptPlayable = (ScriptPlayable<LightControlMixer>)lightMixer;
lightMixerNode = new PlayableNode(lightMixer);
lightMixerNode.Name = "Light Mixer Node";
scriptPlayable.GetBehaviour().SetPlayableNode(lightMixerNode);
mixerNodes.Add(lightMixer, lightMixerNode);
}
output.SetSourcePlayable(lightMixerNode.GetPlayable());
output.SetUserData(boundComponent as Light);
ConnectPlayable(lightMixerNode, info.GetTimelineNode(), trackIndex, ref info);
}
}
This example would make a track processor that is applied to a custom timeline track called LightControlTrack.
- Note
- Track processors are discovered automatically at runtime through reflection and cached, one instance of each track processor is activated
The PlayableNode class
TimelineMixer.PlayableNode
PlayableNode provides a lot of quality of life features, its purpose is to wrap a Playable such as AnimationMixerPlayable and track its connections, and make connecting and disconnecting easier.
You can find out what ports on one Playable are connected which ports on another, not an easy task normally.
Instead of directly connecting Playables to each other, wrap them in a PlayableNode and use that to connect to other PlayableNodes. Doing this tracks all your connections, and makes navigating downstream AND upstream on the graph possible in an easy way.