Search Unity

Playables API / Feedback

Discussion in 'Animation' started by Guessmyname, Oct 30, 2015.

  1. Guessmyname

    Guessmyname

    Joined:
    Feb 12, 2011
    Posts:
    24
    So I'm mucking around with the Playables API to try and feel out what's possible with it. Ran into a few entertaining snags already, so I figured it would be best to start a thread and help out anyone else interested in it. This is kind of Feedback, kind of Support so I admit I wasn't exactly sure where to put it, but I figure I'm going to break something at some point anyway.

    Those unaware of what Playables are (or are promising to be, given they're still in an experimental state at the moment), the Manual discusses them here. Tracking down the Scripting API docs is... awkward because the Experimental namespace doesn't appear in the navigation dropdown for some reason but the pages are there; you just have to use the links in the Manual page to find them. There's also a Unite video on upcoming animation features which shows off Playables; the Playables section kicks off here.

    The gist of them is that it in theory lets you extend on the Mechanim Statemachine system; rather than the Animator component being wedded to one monolithic state machine, you can blend between them, blend in other clips and do all other kinds of shenanigans with them via scripting; you can blend animation clips with animation clips, animation clips with state machines, state machines with other state machines and so on.

    In my case, what I'm particularly hoping to be able to use this for is weapons animations in a top-down game. The design calls for a wide variety of weapon types all with wildely different mechanics... and hence, wildly different statemachines needed to animate them. I've previously experimented with trying to do this with a sort of primitive 'moveset swapping' using Animation Override Controllers to define the precise animation clips and having the structure for every possible weapon statemachine within the AC tied to a different int 'weaponType' parameter but it's... messy, clunky for making changes with and generally not the nicest system to operate on.

    Here, I'm hoping to be able to store the statemachine for each weapon within the weapon itself, such that it is responsible for declaring how it gets used and animated rather than trying to cram every possible system in the main character anim controller. Quite obviously this is going to involve AnimatorControllerPlayables and a fair amount of shenanigans just to get it working. In a beautifully ideal world I'd love to set it up Dark Souls-style with swappable weapons in each hand (so you might in total have three statemachines working; left hand weapon, right hand weapon and the main character, or two for a two-handed weapon and so on). How well you can blend statemachines together using Playables - particularly if they can be layered over each other with masking - is going to be the interesting question and the real limiting factor.

    At the moment I'm just doing tests and trying various systems - there is no weapon manager or anything, it's pure bare-bones prototyping. So far what I've observed is:
    • If your Animator has an animator controller predefined, it will override any Playable set with Play(); - you'll have to remove it first (animator.runtimeAnimatorController = null; or just unset it in the Inspector) to get it to work.
    • When using the MixerPlayable, never let your input weights total anything other than 1.
    • Really, really wish we had some way of naming Playables just so the graph visualiser was easier to read.
    • You don't set animation controller parameters through the Animator any more; they have to be set in the Playable itself. Same functions, though.
    • As I'm not using Root Motion, can't comment on how well that works/doesn't work with Playables and multiple animation controllers.
    • Related to the above... can't seem to get 'Get' parameters to work, ie animation parameters driven by curves in the animation clip.
    Anyone else done anything with the Playables API; any advice to add? The loss of parameter curves hits kind of hard; I'm only getting the default values out from the AnimationControllerPlayables, has anyone had any luck with getting that to work or should I expect that in a later Unity version?
     
    theANMATOR2b likes this.
  2. Guessmyname

    Guessmyname

    Joined:
    Feb 12, 2011
    Posts:
    24
    So further experiments has exposed some... odd behaviour with animation parameters:
    • As noted earlier, the Animator must not have an Animation Controller specified for it to run Playables. Despite this, calling Set/Get Bool/Float etc on the animator still works. It just coughs up odd warnings in the console: "Animator is not playing a Playable" (it is playing; the GraphVisualiser and the fact the character animates is proof. Parameter-driven state transitions are still happening perfectly fine too, which proves the Set functions are getting through; probably best practise to set values in the AnimatorControllerPlayables though).
    • Using property Get functions on the Animator returns the type default (ie false or 0) - along with the above warning about the Animator not playing a Playable. Using the property Get functions on the AnimatorControllerPlayable returns the parameter default (ie whatever was set in the AC as the default value).
    • Live-link in the Animator window and parameters in general seem to be broken; state transitions occur perfectly fine (meaning parameters are being set) but in the window they don't appear to change, neither can they be edited.
    At this current time, unfortunately, I'm going to have to write off setting up a Playable system to blend controllers together; unfortunately I need to be able to get the parameter curves as part of my character movement system (one of the things I use them for is to block player input when certain animations are playing); in their current state (5.2.1f1) they're just a little too buggy to work with.

    I'll come back to try this again in the next Unity release.

    EDIT: Actually I do have beta access, so I could try poking around with that... looking at the patch notes they seem to have caught these...

    EDIT 2: Most of these issues are fixed in the Beta for 5.3... except for one thing. A curve-driven parameter in an AnimationControllerPlayable will not return to its default value when no clip is controlling it; it will return to 0.
     
    Last edited: Nov 1, 2015
  3. Barry100

    Barry100

    Joined:
    Nov 12, 2014
    Posts:
    200
    im getting the error 'animator is not playing a playable' and its killing my game! there consol fills up with it and slows unity to a crawl making my game unplayable in the editor. I am rolling back to 5.1.2 right now so i dont have to put up with it..
     
  4. Barry100

    Barry100

    Joined:
    Nov 12, 2014
    Posts:
    200
    roll back made no difference! cant work out what ive done wrong but its really doing my nut in!
     
  5. Guessmyname

    Guessmyname

    Joined:
    Feb 12, 2011
    Posts:
    24
    Barry, could you post your code? From my experience that message pops up in two situations:
    The first, is that you have an Animator with no AnimationController somewhere that also isn't playing a Playable (so it's effectively empty). Unity tries to update it, finds it's empty and throws up the error, every frame.
    The other case is when you call functions on an Animator with no runtimeController (f.ex because it's running a Playable); any get/set parameter call will cause that message to appear in the console... meaning if you're setting/getting a bunch of parameters at once per frame Unity will also spam the console a bunch of times per frame, making it keel over wailing (it seriously feels like Debug.Log calls got a lot laggier lately).

    If you want to call get/set parameter functions on an AnimatorControllerPlayable you need to call them on the Playable not the Animator; the Animator's Get/Set parameter functions are for it's runtimeController, which needs to be null for it to playing Playables.

    Here's some sample test code to give you an idea:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.Experimental.Director;
    4.  
    5. public class PlayablesTest2 : MonoBehaviour {
    6.    
    7.     public RuntimeAnimatorController mainController;
    8.     public RuntimeAnimatorController secondaryController;
    9.    
    10.     AnimatorControllerPlayable mainPlayable;
    11.     AnimatorControllerPlayable secondaryPlayable;
    12.     AnimationMixerPlayable root;
    13.    
    14.     public Animator animator;
    15.    
    16.     //just an example parameter
    17.     public string groundedParameter = "isGrounded";
    18.     public bool isGrounded; //lets me define the value of isGrounded in the controller from the inspector at runtime
    19.    
    20.     [Range(0,1)] //controls the bias between the primary and secondary controllers; 1 = the primary controller has full weight, 0 = the secondary has full weight
    21.     public float primaryBias = 1;
    22.    
    23.     void Awake(){
    24.         //look for the Animator component if it hasn't been specified
    25.         if(animator == null) animator = GetComponent<Animator>();
    26.        
    27.         //make the AnimationControllerPlayables from the AnimationControllers set in the inspector;
    28.         mainPlayable = new AnimatorControllerPlayable(mainController);
    29.         secondaryPlayable = new AnimatorControllerPlayable(secondaryController);
    30.        
    31.         //make the 'root' playable that will be blending the above
    32.         root = new AnimationMixerPlayable();
    33.        
    34.         //add the playableControllers to the root
    35.         root.AddInput(mainPlayable);
    36.         root.AddInput(secondaryPlayable);
    37.        
    38.         //set their weights. The indexes are the order they were added to the mixer in the previous lines of code (0 = primary, 1 = secondary etc)
    39.         root.SetInputWeight(0, primaryBias);
    40.         root.SetInputWeight(1, 1f-primaryBias);
    41.        
    42.         //tell the animator to play the root
    43.         animator.Play(root);
    44.     }
    45.    
    46.     void Update(){
    47.         GraphVisualizerClient.Show(root, "test");
    48.         mainPlayable.SetBool(groundedParameter, isGrounded); //note that I am calling SetBool on the Playable, not the Animator!
    49.        
    50.         //you want the total weights across all inputs to sum to 1, or hilarity will ensue
    51.         root.SetInputWeight(0, primaryBias);
    52.         root.SetInputWeight(1, 1f-primaryBias);
    53.     }
    54.    
    55. }
    56.  
    I also wouldn't roll back if I were you; the latest version of Unity seems so far to have fixed the parameter bugs I was running into so the Playable API is actually viable now.

    I've also transcribed the code for the CrossFadeMixer shown in the Unite video, for anyone interested:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.Experimental.Director;
    4.  
    5. public class CrossFadePlayable : AnimationMixerPlayable {
    6.  
    7.     private float m_TransitionDuration = 1.0f;
    8.     private float m_TransitionTime = Mathf.Infinity;
    9.     private float m_TransitionStart = 0.0f;
    10.    
    11.     private bool m_InTransition = false;
    12.    
    13.     public void CrossFade(AnimationPlayable playable, float transitionDuration)
    14.     {
    15.         AddInput(playable);
    16.         m_TransitionDuration = transitionDuration;
    17.         m_TransitionTime = 0.0f;
    18.         m_TransitionStart = Time.time;
    19.        
    20.         m_InTransition = true;
    21.     }
    22.    
    23.     public override void PrepareFrame(FrameData info)
    24.     {
    25.         if(m_TransitionTime <= m_TransitionDuration)
    26.         {
    27.             m_TransitionTime = Time.time - m_TransitionStart;
    28.             float weight = Mathf.Clamp01(m_TransitionTime / m_TransitionDuration);
    29.             SetInputWeight(0, 1.0f-weight);
    30.             SetInputWeight(1, weight);
    31.         }
    32.         else if(m_InTransition)
    33.         {
    34.             AnimationPlayable destinationPlayable = GetInput(1) as AnimationPlayable;
    35.             ClearInputs();
    36.             AddInput(destinationPlayable);
    37.             m_InTransition = false;
    38.         }
    39.     }
    40.    
    41. }
    42.  
    Other observations from initial testing and horsing around:
    • Not noticed any bugs with get/set parameters in playable controllers any more; they appear to have all been fixed (animation curve driven parameters work, default values work etc etc)
    • If a Playable on a Mixer has a weight of 0 it does not update. With controllers, this means they won't undergo state transitions at 0 weight, and things like triggers will only fire when the weight is > 0 again. Something to be aware of.
    • GetFloat on playable controllers is not affected by the controller's weight, so if you need to blend it you'll have to do it manually (which I consider a good thing for the record, but something to be aware of).
     
  6. Guessmyname

    Guessmyname

    Joined:
    Feb 12, 2011
    Posts:
    24
    Gah, ran into a crash bug; seems to involve Animator initialisation when Play is called with a Playable. What's odd about it is that it works perfectly fine on my testbed, but on the character itself it reliably crashes on start ~50% of the time (I've even tried just passing it a AnimatorControllerPlayable directly to see if it helps, but no). Also had problems with the Animator failing to initialise on Animator.Play, which doesn't quite crash Unity but it does spam warnings to the console to the point it slows to about 1 frame per minute because of get/set animation property functions...

    Not remotely sure what's occurring here; I've filed a bug report in any case. The crash is pretty easy to reproduce though what exactly happened to invite it I have no idea (it doesn't happen with the testbed...)