Search Unity

Pause timeline but loop active animations, possible?

Discussion in 'Timeline' started by volvis615, Mar 3, 2019.

  1. volvis615

    volvis615

    Joined:
    Nov 10, 2013
    Posts:
    14
    Hello,

    I'm trying to find if I can use PlayableDirector for animated dialogue, such as those seen in later Zelda titles or the Persona series.

    The big issue I have is that when I pause the timeline to wait for player input, the characters also freeze very unnaturally.

    Currently I've avoided the issue by scripting tracks that update Animator's properties instead of controlling it directly, but I've been looking for a smarter way to go about it.

    Can you add animations to timeline without them being lock-step in their timescale? Is timeline not the right tool for this kind of scenario?
     
  2. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    Short answer: timeline doesn't support that right now.

    Longer Answer:

    It is possible with timeline, but requires some scripting customization. A good starting point is Ciro's blog: https://blogs.unity3d.com/2018/04/05/creative-scripting-for-timeline/

    However, this is a use case for timeline that we are quite interested in exploring right now. Some of the work we have planned has this type of scenario in mind.
     
    Demexis likes this.
  3. volvis

    volvis

    Joined:
    Jan 10, 2013
    Posts:
    11
    Alright, thank you for the update seant_unity!

    Do you think this is something that might materialize before 2020? If you don't feel confident about answering that, I'll know to look into if we can brew a solution in-house.
     
  4. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    It might. We are looking into creating some more example custom tracks that would better handle this type of scenario. The current plan would have them in users hands before 2020, but that is subject to change.
     
  5. Littlenorwegian

    Littlenorwegian

    Joined:
    Oct 19, 2012
    Posts:
    143
    Sorry to necro, but I am in this exact situation as described here.

    Is this possible now, in some way, @seant_unity ? Because that would be amazing! :D
     
  6. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    No, we don't have that sample. I really wish we did.

    However, there are a few ways to accomplish this. For example, similar to the original post.

    1) Leave a gap (no extrapolation on the previous clip) on your animation track where you want the looping animation to be. (I'm assuming you have something to pause and/or loop the timeline)
    2) Place a signal on the animation track before the gap to trigger the looping state on the animator.
    3) Place a signal after the gap to trigger the looping state to end.

    The gap will revert control to the animator. If you ease out the previous clip and ease in the next, and place the signals before/after the eases, then it will blend from and to the animator.

    Alternately I think it would be possible to write a custom animation track that allows the animation to 'free run' when the graph is being looped, but I haven't validated that.

    For pausing/looping/etc... there is this post as well as a bunch of other forums posts.
    https://blogs.unity3d.com/2018/04/05/creative-scripting-for-timeline/
     
    josefgrunig likes this.
  7. Littlenorwegian

    Littlenorwegian

    Joined:
    Oct 19, 2012
    Posts:
    143
    Thank you very much @seant_unity

    I may have devised a great method. But it hinges on one detail to be fully automated. (I got it working, but requires you to feed a string manually)


    Is there any way to get the name of the animation currently being played on a character , that's been assigned in Timeline?

    As in, the animation Timeline is overriding the animator with. If I try the conventional method of getting the animation name, it gets the name of the animation it's playing in its own Animator. NOT the one assigned by Timeline.
     
  8. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    There's no accessible property to do that. The reason the animator gives you the controllers current clip is that the controller is still playing underneath the timeline....i.e. timeline is running independently and overwriting what the state machine is doing - all the animator really knows is something else is giving it a pose, and not which clips generated it.

    The only alternative is to write something that determines which animation clips are currently being played by the timeline.

    Here's a sample of how to do it.

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEngine.Animations;
    4. using UnityEngine.Playables;
    5.  
    6. [ExecuteAlways]
    7. public class PrintAnimationNames : MonoBehaviour
    8. {
    9.     public Animator who;
    10.    
    11.     void Update()
    12.     {
    13.         if (who == null)
    14.             return;
    15.  
    16.         PlayableDirector director = GetComponent<PlayableDirector>();
    17.         if (director == null || !director.playableGraph.IsValid())
    18.             return;
    19.  
    20.         // goes through each animation track
    21.         var animationOutputs = director.playableGraph.GetOutputCountByType<AnimationPlayableOutput>();
    22.         for (int i = 0; i < animationOutputs; i++)
    23.         {
    24.             var output = (AnimationPlayableOutput) director.playableGraph.GetOutputByType<AnimationPlayableOutput>(i);
    25.             if (output.GetTarget() != who)
    26.                 continue;
    27.  
    28.             var root = output.GetSourcePlayable();
    29.             if (!root.IsValid())
    30.                 continue;
    31.  
    32.             // walks the playable graph, searching for animation clips
    33.             var port = output.GetSourceOutputPort();
    34.             if (port >= 0)
    35.                 root = root.GetInput(0);
    36.            
    37.             var queue = new Queue<Playable>();
    38.             queue.Enqueue(root);
    39.             while (queue.Count > 0)
    40.             {
    41.                 var playable = queue.Dequeue();
    42.                 for (int j = 0; j < playable.GetInputCount(); j++)
    43.                 {
    44.                     // skips playables with 0 weight or disabled
    45.                     if (playable.GetInputWeight(j) > 0 && playable.GetInput(j).GetPlayState() == PlayState.Playing)
    46.                         queue.Enqueue(playable.GetInput(j));
    47.                 }
    48.  
    49.                 if (playable.IsPlayableOfType<AnimationClipPlayable>())
    50.                 {
    51.                     var clipPlayable = (AnimationClipPlayable) playable;
    52.                     // skips the editor generated clip of the default pose
    53.                     if (clipPlayable.GetAnimationClip() != null && clipPlayable.GetAnimationClip().name != "DefaultPose")
    54.                         Debug.Log(clipPlayable.GetAnimationClip().name);
    55.                 }
    56.             }
    57.         }
    58.     }
    59. }
    60.  
     
  9. Littlenorwegian

    Littlenorwegian

    Joined:
    Oct 19, 2012
    Posts:
    143
    Very close now @seant_unity and thank you. :)

    I tried your script, attached it to my director and selected an animator. And I get nothing, sadly, in debug log.

    It does register having 1 in the queue, but never reaches the spot marked in red where it's going to post the animation name. Maybe I am misunderstanding the setup or utility. It implemented without any errors or warning.

    upload_2020-4-22_23-28-15.png
     

    Attached Files:

  10. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    Which version of Unity/Timeline are you using? If I remember right we fixed an issue with 0 weights which might be the problem.... so try removing playable.GetInputWeight(j) > 0 - change it to just
    if (playable.GetInput(j).GetPlayState() == PlayState.Playing).
     
  11. Littlenorwegian

    Littlenorwegian

    Joined:
    Oct 19, 2012
    Posts:
    143
    @seant_unity I am using Unity 2019.2.3f1

    I tried your solution and sadly it still doesn't go into the
    if (playable.IsPlayableOfType<AnimationClipPlayable>())
    part of the script.

    Just to show, I have gotten the system to work
    https://www.dropbox.com/s/q8rrm866fapp61y/PausedAnimation.mp4?raw=1

    The only problem is right now I need to fire off a signal for every animated character to loop an animation.
    If I could just get the name of the animation, that would go many miles to make the ease of use here simple and intuitive. :)
     
  12. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    That looks great! Good stuff.

    I used 2019.3 to build that, and there are differences to the graph between 2019.2 and 2019.3. So.... one last try...replace the while loop with this.

    Code (CSharp):
    1.            while (queue.Count > 0)
    2.             {
    3.                 var playable = queue.Dequeue();
    4.                 for (int j = 0; j < playable.GetInputCount(); j++)
    5.                 {
    6.                     if (playable.IsPlayableOfType<AnimationMixerPlayable>() && playable.GetInputWeight(j) <= 0.01f)
    7.                         continue;
    8.    
    9.                     queue.Enqueue(playable.GetInput(j));
    10.                 }
    11.  
    12.                 if (playable.IsPlayableOfType<AnimationClipPlayable>())
    13.                 {
    14.                     var clipPlayable = (AnimationClipPlayable) playable;
    15.                     // skips the editor generated clip of the default pose
    16.                     if (clipPlayable.GetAnimationClip() != null && clipPlayable.GetAnimationClip().name != "DefaultPose")
    17.                         Debug.Log(clipPlayable.GetAnimationClip().name);
    18.                 }
    19.             }
    20.  
     
  13. josefgrunig

    josefgrunig

    Joined:
    Jan 23, 2017
    Posts:
    62
    Thank you very much @seant_unity for the hints: I am trying this method but when entering the gap the offset position are not blended between the timeline track and the animator state. Any suggestions?
     
  14. Sangemdoko

    Sangemdoko

    Joined:
    Dec 15, 2013
    Posts:
    222
    Sorry for the necro but I am in the same situation. I want to make a system that is very intuitive since I won't be the one placing things on the timeline.

    @Littlenorwegian would you mind sharing a bit more insight on your solution? Or some a screenshot of your timeline structure?

    I have branching dialogues so I'm planing of making one timeline per dialogue box, but only when a timeline is necessary (character or camera moving)

    Currently the direction I'm going into is holding the timeline at the end and making custom tracks
     
  15. vambier

    vambier

    Joined:
    Oct 1, 2012
    Posts:
    102
    Could you share how you finally did this?