Search Unity

  1. Calling all beginners! Join the FPS Beginners Mods Challenge until December 13.
    Dismiss Notice
  2. It's Cyber Week at the Asset Store!
    Dismiss Notice

Custom Activation Track

Discussion in 'Timeline' started by prisonerjohn, Jan 21, 2019.

  1. prisonerjohn

    prisonerjohn

    Joined:
    Jul 31, 2013
    Posts:
    23
    Hello,

    I'm trying to write a custom Timeline track/clip system that also includes the functionality of the built-in Activation track.

    I've already set up my custom scripts following the Extending Timeline blog post, and my code in `XxxBehaviour.ProcessFrame()` is working fine.

    I'm assuming I now need a way to get a reference to the `GameObject` linked to the Track, so that I can call `SetActive()` on it whenever `OnBehaviourPause()` and `OnBehaviourPlay()` are called.

    Is my thinking correct? If so, I can't figure out how to get access to this `GameObject`. I can keep the reference to the object passed in `ProcessFrame()`, but it gets nullified every once in a while, it looks like `PlayableAsset.CreatePlayable` gets called again and clears everything.
     
  2. JamesB

    JamesB

    Unity Technologies

    Joined:
    Feb 21, 2012
    Posts:
    112
    Hello there, if I understand your question then unfortunately I have bad news: this is extremely difficult to work around because of the nature of Timeline. Timelines are made of Playable graphs which are reconstructed when a change happens or when entering play mode - I believe this will be what is nulling the GameObject reference that you are using. Can you elaborate on exactly what you want to the custom playable to do? There might be a work around in the details.
     
  3. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,068
    For the activation track, we took a slightly different approach. We used just a plain old playable for the clips, and a track mixer behaviour that enables/disables the gameObject (passed into ProcessFrame) based on whether the mixer playable has an input with non-zero weight.

    This approach, thought marginally less efficient, was far more robust and deterministic.
     
  4. prisonerjohn

    prisonerjohn

    Joined:
    Jul 31, 2013
    Posts:
    23
    @JamesB Here's what I'm trying to do: I basically have an object that's a sequence with a start and end frame, and I want to retime it so that it uses an animation clip to play through from start to end over the duration of the clip. So if the clip lasts 2 seconds, the sequence plays over 2 seconds, if the clip lasts 10 seconds, the sequence plays over 10 seconds, etc.

    My PlayableBehaviour looks something like this:

    Code (CSharp):
    1.  
    2.     public class FrameSequenceBehaviour
    3.         : PlayableBehaviour
    4.     {
    5.         public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    6.         {
    7.             var frameSequence = playerData as FrameSequence;
    8.             if (frameSequence != null)
    9.             {
    10.                 var pct = (float)(playable.GetTime() / playable.GetDuration());
    11.                 frameSequence.SetLerpPct(pct);
    12.             }
    13.         }
    14.     }
    15.  
    What I'd like to do is also make it so that frameSequence.gameObject gets enabled/disabled when the the playhead is off any clip in the track. Basically combining this with how the Activation track works.

    I was able to get this working with an ExposedReference, but I don't want to use that, and would rather just use the object associated with the Track, so that I can just change it for the whole track and not on a per clip basis.

    @seant_unity Does the clip have to be a "plain old playable" for that approach to work? Do you think something similar could work if I moved my code to a Mixer?

    Thanks for the help!
     
  5. JamesB

    JamesB

    Unity Technologies

    Joined:
    Feb 21, 2012
    Posts:
    112
    @prisonerjohn After extensive testing, there is no perfectly robust way to perform an operation when a PlayableBehaviour starts and ends. There are lots of ways of getting close but I've run into the same problems as you're having and always hit edge cases where the operation is not performed. I've discussed it with @seant_unity at length and unless something has changed in the API or something in the underlying code, it's just not possible. My recommendation would be to use a separate Activation track. Not really what you want to hear I'd imagine. Who knows, maybe Sean will leave a post correcting me. (Please correct me Sean!)
     
  6. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,068
    It doesn't have to be a plain old playable at all, it can be any type of playable behaviour. In fact, it will work well with the already existing (clip) behaviour you have.

    Here's the process frame for track behaviour:

    Code (CSharp):
    1.         public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    2.         {
    3.             var boundGameObject = playerData as GameObject;
    4.             if (boundGameObject == null)
    5.                 return;
    6.  
    7.             int inputCount = playable.GetInputCount();
    8.             bool hasInput = false;
    9.             for (int i = 0; i < inputCount; i++)
    10.             {
    11.                 if (playable.GetInputWeight(i) > 0)
    12.                 {
    13.                     hasInput = true;
    14.                     break;
    15.                 }
    16.             }
    17.  
    18.             boundGameObject.SetActive(hasInput);
    19.         }
    20.  
     
  7. prisonerjohn

    prisonerjohn

    Joined:
    Jul 31, 2013
    Posts:
    23
    Thanks @seant_unity, that seems to be working!

    I had to put that code in a mixer behaviour, it wouldn't work if I put it directly in the playable behaviour, the input count (or weights) were coming back empty.
     
  8. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,068
    Exactly. By track behaviour, I meant mixer :)
     
  9. JamesB

    JamesB

    Unity Technologies

    Joined:
    Feb 21, 2012
    Posts:
    112
    @prisonerjohn Bit of a mix up on my part. When I said that you can't cover all edge cases, I was referring to things that happen if you don't let the playable go all the way through. To catch this you need to make sure your boundGameObject is set correctly in the OnGraphStop and OnPlayableDestroy methods (you'll need to cache the boundGameObject of course). The reason I was pessimistic about it working was that a few months ago there was a bug where OnGraphStop wasn't always getting called when it should meaning there was an edge case that could not be covered. I can't remember the exact details of it now but the bug has been fixed so there should be no more problems.

    Sorry for the mix up, my bad.
     
  10. prisonerjohn

    prisonerjohn

    Joined:
    Jul 31, 2013
    Posts:
    23
    Thanks @JamesB, I'll make sure to add relevant code in those methods to cover those cases.