Search Unity

Timeline and overriding the Animator

Discussion in 'Timeline' started by mcurtiss, Oct 9, 2019.

  1. mcurtiss

    mcurtiss

    Joined:
    Nov 29, 2012
    Posts:
    18
    I've been diving into the Timeline and Playables for the past few days but I've been unable to come up with a clean solution to what I think is a simple/common use case.

    The simplified version of what I want to be able to do:

    - Have a standard animator which defines idle and movement blend states for a character. (e.g. two different movement blend states that transition between themselves via a bool parameter (like a wounded vs.healthy movestate, with the transition handled via a 'Damaged' bool).

    - Have a suite of items which have associated abilities. (e.g. 'dagger' item which defines an 'attack' ability).

    - These abilities take the form of timelines, which have associated animations and gameplay triggers (e.g. on the dagger prefab there is a timeline on which a 'dagger attack' animation is placed. This way it can be scrubbed by the designer to identify proper time to trigger gameplay events).

    ^This much is very easy and straightforward to accomplish. However, I do not want the 'dagger attack' timeline to interrupt any movement animation or root motion that may or may not be playing via the active animator. The attack animation should instead have an upper-body AvatarMask applied when moving, so that the animator is always driving the legs/root motion.

    This goal of conditional masking seems like it should be easily achieved via
    SetLayerMaskFromAvatarMask() function on AnimationLayerMixerPlayable, as is seen in the manual. In this case we use the layerMixer with two inputs, the first is the character's runtimeAnimatorController wrapped in a AnimatorControllerPlayable, and the other input is the animation clip. Easy enough, but here I've run into a hurdle:

    - I am unsure how to sync the AnimatorControllerPlayable with the already running animator. This issue is twofold:
    - There is a hiccup when activating the AnimatorControllerPlayable, as the character's animator is already, say, halfway through it's current animation, but the animatorControllerPlayable starts at the beginning. I'm unsure how to sync the timing. I've tried the following, with SetPropagateSetTime(), but it does not appear to work:

    Code (CSharp):
    1.  var source =animator.playableGraph.GetOutputByType<AnimationPlayableOutput>(0).GetSourcePlayable();
    2.             double time = source.GetTime();
    3.             ctrlPlayable.SetTime(time);

    - If the AnimatorControllerPlayable is built *after* the animator has been running and it's parameters have been changed, then the animatorControllerPlayable inherits none of the changed parameters. SO lets say the animator has been running and the bool 'Damaged' has changed from the default 'false' to 'true'. If we then create a new AnimatorControllerPlayable at this time, then the 'Damaged' value will be the default 'false'. What's odd is that from this point on, the new AnimatorControllerPlayable will then match any new changes to the Animator's parameters. It seems like the bindings have been set, but not initialized to match the associated animator.

    Am I going about this all wrong? Again, I wish to have timeline-defined abilities which interface with the animator in such a way that the player is able to move and attack at the same time, and the animator is able to handle the movement animation, while the timeline handles the attack aimation. If there is an alternative or intended way of achieving this, please let me know.

    edit: I should mention that I am aware that I could conditionally rebuild the character's AnimationOverrideController based on what abilities are equipped, and then use the timeline to activate bool values on the animator instead of playing the actual animations themselves, but this significantly increases the amount of maitenance of the animator, and it also obfuscates the workflow. This solution could work but it seems like the Timeline can be put to better use here.
     
    Last edited: Oct 9, 2019
  2. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,010
    The approach seems legitimate. The animation track in timeline allows you to assign an avatar mask - have you tried that? It may still try to apply root motion - in which case you could use a custom (and simpler) animation track that strips away all the root motion and recording complexity. (I think I may have a sample one if you need it)
     
  3. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,010
    But the approach does seem legit.
     
  4. mcurtiss

    mcurtiss

    Joined:
    Nov 29, 2012
    Posts:
    18
    @seant_unity

    I wasn't able to use the default animation track because it overrides the animator completely; i.e. if you apply an avatar mask to the animator track, the masked bones will play the given animation, but the unmasked bones don't animate at all. If I have an Avatar mask that enables the upper body and apply it to an animation track - when the timeline plays, it looks like the unmasked foot IK solvers collapse to the origin, and other unmasked bones revert to T-Pose - this is all while the Animator is playing.

    What seems to be happening is that if an AnimationOutput on the timeline graph has any weighted mixers active, then it completely overwrites the AnimationOutput of the animator - as such, no blending between the two separate graphs (Timeline and Animator) seems possible with the tools available. It seems that you can't for example, have a single input AnimationLayerMixer with an avatar mask as the source for the Timeline AnimationOutput. That won't blend with the existing animator. Again, non-masked bones will t-pose, and foot IK's collapse to the origin. For all transforms in the rig, an AnimationOutput is either all off, or all on. This is why I've had to wrap the runtimeAnimationController inside the timeline's graph to get that data, and hence the issues getting it to sync with the existing animator when it is instantiated.

    I'd definitely be interested in the sample you've got.
     
    Last edited: Oct 10, 2019
  5. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,010
    My mistake -- you are correct...The avatar mask doesn't properly apply between timeline and animator controllers. I would recommend filing a bug with your specific use case, but that obviously doesn't help in the short term.

    And even a custom animation track wouldn't fix that.

    Unfortunately I don't see another way. Hiding this behind a custom clip that manages the rebuilding/reassigning of the controller/layer might lessen the burden of the workflow for the artists.
     
  6. mcurtiss

    mcurtiss

    Joined:
    Nov 29, 2012
    Posts:
    18
    Okay, thanks for taking at look at this issue. I will file a bug report.