Search Unity

How can I get the currently playing clip?

Discussion in 'Timeline' started by KristofferH, Aug 13, 2020.

  1. KristofferH

    KristofferH

    Joined:
    Oct 27, 2012
    Posts:
    52
    I need to set a specific value before every clip on a track in a timeline starts playing. So I need to somehow figure out when a new clip on the track is about to begin. Simplified the code looks like this, and is based on a tutorial posed on the Unity blog one or two years ago.

    Code (CSharp):
    1. public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    2. {
    3.         LightControllerAdv trackBinding = playerData as LightControllerAdv;
    4.  
    5.         float endVal = 0f;
    6.  
    7.         int inputCount = playable.GetInputCount(); //get number of clips on track
    8.  
    9.         for (int i = 0; i < inputCount; i++)
    10.         {
    11.             float inputWeight = playable.GetInputWeight(i);
    12.             ScriptPlayable<LightControlBehaviour> inputPlayable = (ScriptPlayable<LightControlBehaviour>)playable.GetInput(i);
    13.             LightControlBehaviour input = inputPlayable.GetBehaviour();
    14.  
    15.             endVal += input.endVal * inputWeight;
    16.         }
    17. }
    So if I understand this correctly each frame it loops through all the clips in the track in order to figure out the weight of each clip if they are supposed to blend into eachother. But is there a way to get the current clip that is active? When clip0 starts I need to set a specific value, and then when clip1 is about to start I need to change that specific value, and then again when clip2 starts, and so on. But I can't change this specific value every frame, I only want to change it once per clip.

    @seant_unity Any ideas?
     
  2. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    When you say about to start, do you mean 'is starting this frame' or will start within a given time period?

    If it's this frame, you can override OnBehaviourPlay in your clips PlayableBehaviour (LightControlBehaviour) set a variable to true. Then in the ProcessFrame above (i.e the PlayableBehaviour for the track) you can check the value on each input, make a change and then set it to false. something like:

    Code (CSharp):
    1. class LightControlBehaviour : PlayableBehaviour
    2. {
    3.     public bool becameActiveThisFrame;
    4.     public override void OnBehaviourPlay(Playable playable)
    5.     {
    6.         becameActiveThisFrame = true;
    7.     }
    8. }
    9.  
    10. class LightControlMixerBehaviour : PlayableBehaviour
    11. {
    12.     public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    13.     {
    14.         int inputCount = playable.GetInputCount(); //get number of clips on track
    15.         for (int i = 0; i < inputCount; i++)
    16.         {
    17.             ScriptPlayable<LightControlBehaviour> inputPlayable = (ScriptPlayable<LightControlBehaviour>)playable.GetInput(i);
    18.             LightControlBehaviour input = inputPlayable.GetBehaviour();
    19.             if (input.becameActiveThisFrame)
    20.             {
    21.                 input.SetSomeValueHere();
    22.                 input.becameActiveThisFrame = false;
    23.             }
    24.         }
    25.     }
    26. }

    Otherwise if you want to set a value in advance of it becoming active, it's a bit trickier, but one formula that works is

    1. Add a startTime property or non-serialized field to your PlayableAsset and PlayableBehaviour for the clip. In CreatePlayable, make sure to copy it from the PlayableAsset to the PlayableBehaviour.
    2. In your tracks CreateTrackMixer method, set the start time of the playable asset. CreateTrackMixer gets called before CreatePlayable.

    Code (CSharp):
    1.       public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    2.         {
    3.             foreach (var clip in GetClips())
    4.             {
    5.                 var asset = clip.asset as MyPlayableAsset;
    6.                 if (asset != null)
    7.                 {
    8.                     asset.startTime = clip.start;
    9.                 }
    10.             }
    11.  
    12.             return ScriptPlayable<MyMixerPlayableBehaviour>.Create(graph, inputCount);
    13.         }
    14.  
    3. In your mixer PlayableBehaviour you can use the timeline time to see if you are within a given range of the clip.

    Code (CSharp):
    1. public override void PrepareFrame(Playable playable, FrameData info)
    2. {
    3.     var timelineTime = playable.GetGraph().GetRootPlayable(0).GetTime();
    4.     for (int i = 0; i < playable.GetInputCount(); i++)
    5.     {
    6.         ScriptPlayable<MyPlayableBehaviour> scriptPlayable = (ScriptPlayable<MyPlayableBehaviour>) playable.GetInput(i);
    7.         MyPlayableBehaviour myPlayableBehaviour = scriptPlayable.GetBehaviour();
    8.         double clipStart = myPlayableBehaviour.startTime;
    9.  
    10.         if (timelineTime > clipStart - 0.5f && timelineTime <= clipStart)
    11.             myPlayableBehaviour.DoSomething();
    12.     }
    13. }
    14.  
     
    KristofferH likes this.
  3. KristofferH

    KristofferH

    Joined:
    Oct 27, 2012
    Posts:
    52
    Thanks @seant_unity. I tried your first suggestion and it seems to be working so far, great - thank you!
     
  4. Deleted User

    Deleted User

    Guest

    Instead of writing all these scripts, it would be very helpful if Unity would provide officially a method like:
    public virtual Clip GetCurrentlyPlayingClip(); in TrackAsset
    and/ or
    public virtual bool IsClipPlaying(); in PlayableAsset:confused:
     
    AnomalusUndrdog likes this.