Problem with manual update

I’m having some trouble getting the manual update mode working the way I want it to.

I’m trying to have a timeline that animates the transform of several objects. The objects themselves have an Animator Controller, so they already have an animation running ‘locally’, as well as being animated ‘globally’ by the timeline. This works correctly when I set the timeline’s update mode to Game Time & play on awake. So far so good.

Now, for reasons I won’t get into, I want to be able to manually drive the speed at which this timeline plays (instead of using global timeScale), so I added a script to the timeline gameobject with the following:

using UnityEngine;
using UnityEngine.Playables;

public class TimelineVelocityController : MonoBehaviour
{
    PlayableDirector timeline;

    float velo = 1;

    // Start is called before the first frame update
    void Awake()
    {
        timeline = GetComponent<PlayableDirector>();
        timeline.initialTime = 0;
        timeline.time = 0;
    }

    // Update is called once per frame
    void Update()
    {
        timeline.time += Time.unscaledDeltaTime * velo;

        timeline.DeferredEvaluate();
    }

    public void SetVelocity(float v)
    {
        velo = v;
    }
}

Problem #1
PlayableDirector is set to Manual & PlayOnAwake is off & script is enabled
The first weird behavior happens when I leave this script enabled and start up the scene. My timeline anim is about 360 frames (6 sec) and for some reason it will always start around frame 150 (2:45 sec).

No idea why that’s happening, but I managed to avoid that bug by leaving this script disabled at first and then enabling it once the scene is loaded & running.
It even plays the animation on the timeline correctly (moving the object’s transform), but

Problem #2
PlayableDirector is set to Manual & PlayOnAwake is off & script is disabled at first
The objects will move on the trajectory defined in the timeline, but are stuck in a resting pose instead of playing their ‘local’ animation properly, like when Game Time is the updateMode. I’m guessing I somehow need to trigger the evaluation of that animator controller manually as well?

I tried to look into how this works by checking out the Unity source code, and I found this but I’m a bit stuck and don’t know where to look further. Where can I see the code that handles Game Time updating? I figured I could reverse engineer it that way perhaps, but I admit I don’t entirely understand the timeline mechanism yet.

Any ideas/help appreciated, thanks!

I just report a bug that manual updating could not make audio track play, and also could not trigger 2019.1’s new marker feature. It is unrelated to your case but maybe you should also submit a bug report.

Also try to move the time + evaluate on the playable graph directly instead of asking the playable director to do it? (though, I tried that and it didn’t fix my reported bug) That version of Evaluate could add time before evaluation.

playableDirector.playableGraph.Evaluate(Time.unscaledDeltaTime * velo);

Anything inside UnityEngine.Playables are closed source, I had trouble figuring out what was happening inside the black box too. (Now UnityEngine.Timeline is open as a package)

*Also, the playable API actually contains a settable “speed” modifier so that any delta time that goes into them got multiplied. Your premultiplying way works too, but just in case this new way could somehow fix your bug :

pd.playableGraph.GetRootPlayable(0).SetSpeed(0.1f);

This make even PlayableDirector’s auto update runs slower, because you hack its generated (root) playable to go slower. Though the director has no easy interface to this speed.

Problem #1
- Try start instead of awake, or skip updating the time on the first frame. I’m not sure off hand, but unscaled delta time may not have the 1/30 frame limitation deltatime does between Awake() and the first Update() call.

Problem #2
- You may need to call Stop() on the director in OnDisable() to get rid of the graph when the script is disabled.

@5argon - using manual time won’t play Audio or Trigger notifications, because it is effectively scrubbing the timeline, instead of playing it. (Playing it defined as the engine is driving time). Audio and Notifications require continuity of time, and manual doesn’t guarantee that.

You are correct on using the root playable speed to alter the playback speed…that’s something we should have exposed, and would like to expose, in the playable director.

PlayableDirector and Playables are (mostly) implemented in native code, so digging into them isn’t easy.

1 Like

Interesting, using unscaledDeltaTime was indeed the culprit for the initial timeline skip. Seems like it’s a large value on the first frame, so this shouldn’t be a problem in the end!

The second problem remains as long as I try using manual time. I’ve now solved the problem by using pd.playableGraph.GetRootPlayable(0).SetSpeed(0.1f); together with leaving it on Game Time, this works! Thanks @5argon & @seant_unity . The other solution made the timeline move forward, but did not actually play any of the animations on the timeline, so that’s weird.

And yes it would indeed be good to expose this speed property in the playable director.