How to implement custom Timeline tracks

This article describes how to implement a custom Timeline track.

 

As shown in the figure above, if we want to implement a custom Timeline track, we need to create at least four scripts -- CutomTrack,CustomClip,CustomMixer, and CustomPlayableBehavior.

1.CustomTrack (inherited from TrackAsset)

[Serializable]//Guarantee serialization
[TrackClipType(typeof(CustomClip))]//Indicates which Clip Track adds
[TrackBindingType(typeof(GameObject))]//Represents what type of object Track binds (GameObject or any Component, etc.)
[TrackColor(0.53f,0.0f,0.08f)]//Indicates the identification color for the front of the Track track in the editor (not important)
public class CustomTrack : TrackAsset
{
    //Rewrite this factory method to create a Mixer when you play tracks.
    public override Playable CreateMixer(PlayableGraph graph,GameObject go,int inputCount)
    {
        var mixerPlayable = ScriptPlayable<CustomMixer>.Create(graph);//This is the Playable, mixerPlayable.GetBehavior () driven by CustomMixer as CustomMixer;You can get CustomMixer
        mixerPlayable.SetInputCount(inputCount);
        return mixerPlayable;
    }
}

By name, this class represents a track. When you complete the class declaration, you can add this track in TimelineWindow.

The role of CustomTrack

1. Orbital resources themselves, CustomTrack represents orbital resources.

2. Declare the types of binding resources that can be added.

2. Declare the ClipAsset that can be added, telling the editor through the TrackClipType property that you can add that Clip in the track.

3. Create a Mixer. (Mixer's role goes on below)

2.CustomClip (inherited from ClipAsset)

[Serializable]
public class CustomClip : PlayableAsset , ITimelineClipAsset
{
    //As you can see from the naming, the template here is a template, and Clip creates a new object at runtime based on the template you assigned.
    //As you will see below, CustomPlayableBehaviour is best if we only put data, and the logic is done by mixer
    public CustomPlayableBehaviour template = new CustomPlayableBehaviour();

    //The role of ExposedReference, if there is no ExposedReference, can you reference references in Scene (only from Assets)
    public ExposedReference<Transform> exampleValue;

    //ClipCaps is a must-implement property that represents what functions your Clip supports and affects how your editor operates on Clip.
    //For example, Blending supports fusion on behalf of your Clip. In the editor, you can drag two Clips to a common time period, and you can edit the fused fusion curve.
    //If fusion is enabled, the weight of each time period Clip (CustomPlayableBehaviour in this case) is not just 0 and 1. It is possible that both Clips have a weight of 0.x at the same time. (Of course, you have to implement the appropriate fusion logic based on the weight yourself, otherwise everything is meaningless.)
    //In the IDE, see the ClipCaps declaration for more information
    public ClipCaps
    {
        get{ return ClipCaps.Blending; }
    }

    //Rewrite this factory method to create a ScriptPlayable <T> when the track is played, with a subsection on playable, which is created by
    //A special playable (and also an interface) driven by the playablebehaviour
    public override Playable CreatePlayable (PlayableGraph graph,GameObject owner)
    {
        var playable = ScriptPlayable<CustomPlayableBehaviour>.Create(graph,template);

        CustomPlayableBehaviour behaviour = playable.GetBehaviour();
        behaviour.exampleValue = exampleValue.Resolve(graph.GetResolver());

        return playable;
    }
}

The role of CustomClip:

1. The fragment resource itself, CustomClip represents the fragment resource.

2. As a factory, create a CustomPlayableBehavior at run time based on the properties you assign to the template when editing.

3. Define what Clip supports. Blending, Xxtrapolate, and so on.

3. CustomMixer (inherited from PlayableBehavior)

public class CustomMixer:PlayableBehaviour
{
    public override void OnPlayableCreate(Playable playable)
    {
        ...
    }
    public override void OnPlayableDestroy(Playable playable)
    {
        ...
    }

    public override void PrepareFrame(Playable playable, FrameData info)
    {
        ...
    }
    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        for(int i = 0 ; i < playable.GetInputCount(); i++)//Get all the segments on the track
        {
            float weight  = playable.GetInputWeight(i);//Get the fragment of the fragment at the current frame
            var clipPlayable = (ScriptPlayable<CustomPlayableBehaviour>)playable.GetInput(i);
            CustomPlayableBehaviour behaviour = clipPlayable.GetBehaviour();//Get CustomPlayableBehaviour
            ...//Next, you can override the logic based on Clip's weight (if you don't set blend in ClipCaps, only one fragment should have a weight of 1 and the other zero)
        }
    }

    //The four virtual methods above are the most common, and OnBehaviourPause is not recommended and is not well controlled (pauses for various reasons). It's also good to judge if pauses occur directly based on changes in time value.
}

From the above code, we can see that Mixer can get all the tracks on the current frame orbit, and the corresponding weights. Also because of this capability, unit officially recommends that CustomPlayableBehavior only store data and that functionality be written on CustomMixer. Since CustomPlayableBehavior can only get information about itself in this fragment, CustomMixer can get information about all the fragments on the track, so it can process multiple fragments (similar to fusion)However, if the logic is written in CustomPlayableBehaviour, it is not possible.

4. CustomPlayableBehaviour (inherited from PlayableBehaviour)

public class CustomPlayableBehaviour:PlayableBehaviour
{
    public Transform exampleValue;
}

CustomPlayableBehavior (or CustomClipData) is responsible for declaring the fields required for each Clip to run.

Extended introduction: Playable

Playable is a structure defined by Unity that represents everything that can be played (video, animation, sound, etc.).

1. It can be used to create complex and flexible data and connect as nodes of a tree.

2. It can set weight s for each of its children.

ScriptPlayable<T>is a special kind of Playable. Its main function is to customize Playable. This is a generic structure, and T must inherit from PlayableBehavior. We can implement in PlayableBehavior any logic that we want to implement in playback (PlayableBehavior.PrepareFrame and PlayableBehavior.ProcessFrame)

Reference resources:

https://docs.unity3d.com/ScriptReference/Playables.Playable.html

Unite Europe 2017 - Extending Timeline with your own playables

 

Tags: Unity

Posted on Wed, 08 Sep 2021 17:10:41 -0400 by scooter41