Search Unity

Question about Timeline Pausing and Virtual Camera Priority

Discussion in 'Cinemachine' started by Multithreaded_Games, Dec 19, 2017.

  1. Multithreaded_Games

    Multithreaded_Games

    Joined:
    Jul 6, 2015
    Posts:
    122
    Hi all,

    In my current setup, there are several instances where we manually pause the timeline using an outside custom TimelineManager object. Usually we do this to wait for a character to move to a specific spot or wait until a certain input has been received or for dialogue to finish, etc.

    However, I've noticed that when we call Timeline.Pause() (or whatever the function's called), Cinemachine automatically selects the camera with the highest priority to view the scene through. In order to prevent this behavior, we've taken to using Activation Tracks to manually disable/activate cameras. This is quite cumbersome and requires a lot of properly synced up tracks (many of which are often spatially separated in the inspector)--for instance, we have to have Cinemachine track itself, then any Animation tracks that drive the camera (if applicable) and finally, an Activation Track to turn on/off the camera.

    In other words, is there an easy way to hook into Cinemachine--I imagine the brain receives some sort of OnTimelinePause event or something of the sort? Essentially, we'd like for Cinemachine to keep the focus on whatever camera was 'live' when the Timeline was paused and not do any camera switches at all when the timeline is paused.

    Or perhaps I'm wrong about all of this and doing something incorrectly--feel free to correct me if so! Thanks again for the awesome tools!
     
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    In the current implementation, Pause() causes the timeline to release its Cinemachine tracks - it's as if the timeline has ended, and the cameras revert back to the standard priority system. It's a pain, but that's the way it is for now.

    Here's an idea can you try: instead of calling Pause(), set the PlayableDirector's timeUpdateMode to Manual. I'm thinking that might keep the timeline alive, but without ticking the clock. To resume, set the timeUpdateMode back to what it was before. Just a thought. Tell me if that works for you.
     
  3. Multithreaded_Games

    Multithreaded_Games

    Joined:
    Jul 6, 2015
    Posts:
    122
    @Gregoryl,

    Unfortunately this doesn't seem to work as the PlayableDirector doesn't seem to acknowledge the update change at runtime (IE, the timeline continues scrubbing through even though the mode shows "Manual" in the Inspector).

    It does, however, acknowledge it if I set it in the Inspector beforehand. So that got me thinking, why not just always use Manual and pass deltaTime in via my TimelineManager's Update function? This approach should work, but unfortunately, I cannot find any documentation about the PlayerController.Tick() as alluded to here: https://docs.unity3d.com/ScriptReference/Playables.DirectorUpdateMode.Manual.html.

    Maybe this is just named something differently? Or perhaps it really is inaccessible at the moment--either way, some clarification would be much appreciated! Thanks again.
     
  4. Multithreaded_Games

    Multithreaded_Games

    Joined:
    Jul 6, 2015
    Posts:
    122
    @Gregoryl,

    Sorry to blow up this thread, but I also attempted to increment the ActiveDirector's 'time' value in my Update loop, but with the playmode set to Manual, it doesn't seem to be acknowledging this either, even though I can see the time value change in the inspector and I can see the timeline scrubbing as well, but it isn't executing any of the tracks it comes across.
     
  5. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    @Multithreaded_Games

    There's a workaround you can use instead of PlayableDirector.Pause(). Gregory is correct, the Pause() methods suspends the timeline at the current time, but stops evaluating it. That's why it 'releases' the cinemachine cameras. Instead you can keep the timeline running, but prevent time from advancing by using playableDirector.playableGraph.GetRootPlayable(0).SetSpeed(0).

    This will pause the timeline in the more traditional sense of the word. You can resume by resetting the speed back to 1. This is something on our backlog of issues to address with a better solution.

    I hope that helps.
     
  6. Multithreaded_Games

    Multithreaded_Games

    Joined:
    Jul 6, 2015
    Posts:
    122
    @seant_unity,

    Thanks for the response! I actually did something pretty hacky--not super proud of it, but let's be honest, I've done worse :p As long as I know I'm only doing cuts, I just hook into the CinemachineMixerClip and force whatever camera is coming up to be set as the "SoloCamera." Obviously not a perfect or final solution, but for now, it gets the job done and prevents camera switching while the timeline is paused.
     
  7. Bdelcast

    Bdelcast

    Joined:
    Jul 11, 2014
    Posts:
    25
    Hello!
    Question, I have been having the same issue, If I pause the timeline with
    director.playableGraph.GetRootPlayable(0).SetSpeed(0); all the animations pause too, and it looks very jarring, so I was trying to find the current position of the active Cinemachine virtual camera (or the blend position), to set the Released camera to that transform, but I can't even find where these cameras are accessible, and though I got all cameras from the CinemachineCore, I don't know know which camera is currently live either, as the Cinemachine brain shows it in editor but doesn't expose it.

    If you don't mind me asking, how did you manage to prevent all the switching?
    Thanks so much.
     
  8. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    @Bdelcast I think you might want to separate the timelines: one for the stuff that you want to pause, and one for the animations that you want to keep running during the pause.
     
    Bdelcast likes this.
  9. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    The current Virtual Camera transform is, at all times, the transform of the Unity Camera, because CM is driving it. When you deactivate the last CM vcam, the Unity Camera's transform will be exactly as CM left it, unless you have some other code that is modifying it.

    Sorry, I don't understand this question
     
  10. Bdelcast

    Bdelcast

    Joined:
    Jul 11, 2014
    Posts:
    25
    Sorry, Maybe I was not very clear.

    So, I have a simple cutscene text dialogue, where the cinemachine camera is paused and dialogue bubbles come up, and wait for a controller prompt to continue.
    The timeline and virtual cameras play normally until with an activation track I enable some game objects that OnEnable() pause the game and advance the dialogue.

    This works nicely, because looping animations, such as Idles and generic dialogue continue, while the camera is stopped. which is the reason why director.playableGraph.GetRootPlayable(0).SetSpeed(0); is OK, but not ideal.

    Problem is (and seemingly random), that if I call Pause() instead on my playable director. The camera returns to some other virtual camera position on the scene. when I resume, it returns to the correct position.

    No other scripts are changing my mainCamera's position, so I don't understand why this would be happening.

    I'm not sure if that explains my problem a little clearer.

    Thanks.
     
  11. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    When you pause the timeline, it stops forcing its virtual camera to be active. And then the last active virtual camera becomes the active one.

    - Double check the priority setting on all your virtual cameras
    - Timeline will "force on" a virtual camera, even if it is not active, even if it has lower priority, even if the vcam gameobject isn't even enabled.
     
    Bdelcast likes this.
  12. Bdelcast

    Bdelcast

    Joined:
    Jul 11, 2014
    Posts:
    25
    Right, yeah that's what I was noticing.
    So, Is there any way in which I can Get the latest "LIVE" camera, and set it's priority "highest", so that it always respect the last section played? Not particularly clean, but that should work, right?

    Thanks!
     
  13. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    I don't think that's a great idea. The way to understand it is this:
    1. In the absence of Timeline, CM does its thing and chooses the vcam based on priority, game events etc. i.e. the game logic you have set up.
    2. When a timeline activates a vcam with a Cinemachine Shot, this vcam acts as an override for the CM system. CM is still doing its thing in the background, activating vcams and all that, but the result is overridden by the timeline's choice. The timeline's choice always wins, and this regardless of active states, priorities, etc - these things are ignored by timeline.
    3. Note that it is only Cinemachine Shots that override the vcam. Timelines without CM shots will not affect the current active vcam. If you have multiple CM shots active at once (via multiple timelines or CM tracks) then you get multiple vcam overrides that behave like a stack - the most recent one prevails.
    4. When the Timeline ends, all overrides are "released", and the brain starts paying attention again to the vcam that was activated outside of Timeline's override - i.e. the camera that would be active if there were no timeline running.
    5. The problem with the Pause() method is that it effectively ends the timeline, causing the CM override to be released.
    6. SetSpeed(0) does not release the override.
    So, if you want some specific vcam to be active after the timeline is released, make the game logic choose that vcam independently of timeline.

    Hacking things to artificially set the current vcam to the vcam that the timeline happened to release because Pause releases everything is a rabbit-hole down which you don't want to jump. Much better to work with the paradigm, and separate the timelines. Use SetSpeed(0) to avoid releasing the vcam override.
     
    Last edited: Jan 10, 2018
    Bdelcast likes this.
  14. Multithreaded_Games

    Multithreaded_Games

    Joined:
    Jul 6, 2015
    Posts:
    122
    Sorry to chime in here... while I do see what @Gregoryl is saying, I ran into the exact same problem. Throw in time constraints, blah blah blah--well, you see where I'm going with this. This is, by no means, a permanent fix to the problem, but this is what I'm doing to always focus on a single camera. This code lives in the CinemachineMixer.cs--the last line was there already, for reference as to where you need to insert this:

    Code (CSharp):
    1.  
    2. if(camA != null && CinemachineBrain.SoloCamera != camA)
    3. {
    4.      if (mBrain.m_DefaultBlend.m_Style == CinemachineBlendDefinition.Style.Cut)
    5.      {
    6.           CinemachineBrain.SoloCamera = camA;
    7.      }
    8. }
    9.  
    10. // Override the Cinemachine brain with our results
    11. mBrainOverrideId = mBrain.SetCameraOverride(mBrainOverrideId, camB, camA, camWeight, deltaTime);
    And again... I do see what you're saying, but I would strongly suggest adding this sort of behavior to the CinemachineBrain as it can be incredibly frustrating trying to selectively disable/enable cameras, especially when there are a bunch of them. And honestly, when you're just doing straight cuts and not interested in whether or not a camera can 'get' a good shot or any kind of blending behavior, I see no reason to have to fiddle with the priority system. For instance, when a Timeline is sufficiently complicated, you really can't even see all of the tracks at once. Couple that with some weird snapping mechanisms (I understand that Timeline is still in its infancy) and it can be very time-intensive trying to make sure certain cameras do what is needed using Activation tracks and whatnot. Having a bunch of activation tracks just adds more clutter.
     
    PhaetonLT and Bdelcast like this.
  15. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    Maybe you could describe your use-case a little more so I can better understand your suggestion. You seem to have a setup that relies heavily (entirely?) on timeline, and has no non-timeline game-play mode that uses vcams. Is that right? You want the vcam chosen by timeline to remain active after timeline ends, always, until the next timeline vcam override. Right?
     
    Multithreaded_Games likes this.
  16. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    Maybe, instead of abusing Solo, you could call vcam.MoveToTopOfPrioritySubqueue(). Then you don't have to worry about when Solo gets disabled, and are not killing the blending system.
    Code (CSharp):
    1.         /// <summary>When multiple virtual cameras have the highest priority, there is
    2.         /// sometimes the need to push one to the top, making it the current Live camera if
    3.         /// it shares the highest priority in the queue with its peers.
    4.         ///
    5.         /// This happens automatically when a
    6.         /// new vcam is enabled: the most recent one goes to the top of the priority subqueue.
    7.         /// Use this method to push a vcam to the top of its priority peers.
    8.         /// If it and its peers share the highest priority, then this vcam will become Live.</summary>
    9.         public void MoveToTopOfPrioritySubqueue()
    10.         {
    11.             UpdateVcamPoolStatus();
    12.         }
     
    Bdelcast and Multithreaded_Games like this.
  17. Multithreaded_Games

    Multithreaded_Games

    Joined:
    Jul 6, 2015
    Posts:
    122
    Hey @Gregoryl, that is correct! We're using a different Camera system for our non-cutscene (IE, timeline) gameplay. So the only time Cinemachine cameras come into play is during our cutscenes/timelines.

    And don't get me wrong, I totally understand why you guys set it up the way you did, it's a really powerful and versatile system, but for our particular use-case, we just need one camera active all the time. Basically, once the timeline is over, we revert to our standard, already implemented camera system. But while using the Timeline, basically, our use case would be covered if Timeline didn't 'release' its override when explicitly paused.
     
  18. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    @Multithreaded_Games ok, I get it. Thanks for the clarification. You could probably get the behaviour you want without hacking the CM mixer code. Hook into the Brain's CameraActivated event, and call vcam.MoveToTopOfPrioritySubqueue() whenever a vcam gets activated. That should do the trick.
     
    Bdelcast and Multithreaded_Games like this.
  19. Multithreaded_Games

    Multithreaded_Games

    Joined:
    Jul 6, 2015
    Posts:
    122
    Thank you, @Gregoryl. I suspected there was probably a better, less hacky way to do what I needed.
     
  20. tealm

    tealm

    Joined:
    Feb 4, 2014
    Posts:
    108
    Be aware that setting the speed to 0/1 multiple times will eventually cause audio playback of audiotracks in the timeline to stop working. (Unity 2018.1b05)

    In an attempt to work around this i tried using timeScale = 0 and AudioListener.pause = true, but Timeline AudioTracks use some different method for audio playback it seems.

    @seant_unity do you have any tips on how we can manipulate playback of an AudioTrack in Timeline?
     
    Menion-Leah and dadude123 like this.
  21. seoyeon222222

    seoyeon222222

    Joined:
    Nov 18, 2020
    Posts:
    187

    Has it changed the way it works? (i'm using 2022.3.6f1)
    Pause() doesn't seem to release the Cinemachine track.

    for example i have two virtual Camera
    Cam1 : Priority 10 / SetActive True
    Cam2 : Priority 100 / SetActive True
    In Timeline, Cam1 is playing
    upload_2024-1-18_10-49-29.png
    Only Cam1 plays because the timeline overrides the priority.

    but
    If i Pause the Timeline in the middle,
    (manual way or using PlayableDirector.Pause() or ....SetSpeed(0))
    Still it works as Cam1

    I want to run Cam2 while the Timeline is paused,
    so i try
    Cam2.MoveToTopOfPrioritySubqueue();
    SetActive true -> false -> true
    but it still behaves as Cam1.

    I was expecting to be able to set the priority of the cinemachine via script,
    since the ProcessFrame is not evaluated when the timeline is paused.
    Is there something wrong with my understanding or testing?:(


    My purpose in making these attempts is simple.
    Pause in the middle of a timeline cutscene.
    After doing something uncontrolled with the timeline,
    (via script, using different VCams)
    play the paused timeline again.

    If there's a better way to do it, I'd love to hear about it
     
  22. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    You can activate a new Timeline with just Cam2 in it. That will override everything (including the paused timeline).