Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Creating a custom animation track for dynamic spritesheet animation

Discussion in 'Timeline' started by taylank, Dec 4, 2018.

  1. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    I'm trying to use 2D spritesheet animations with Timeline. The idea is to assign the spritesheet dynamically at runtime, so that timeline animates a sequence like two characters fighting with any choice of characters depending on what's happening in the game.

    So I set out to create an AnimationReferenceTrack playable with the wizard, which hooks up to an AnimationReference Monobehaviour in the scene that is responsible for spitting out the correct AnimationClip at runtime., which is easy enough. But I just can't figure out how to assign my animation clip to the animator, since the animator has no direct way of playing an arbitrary clip. So what am I supposed to do here?

    Any help would be greatly appreciated!
     
  2. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    In case it helps anyone, below is how I managed to do it. I'm essentially creating a new animation output for every reference input, and setting its weight to that of the source input every frame. Would appreciate feedback on whether I'm doing this the right way, or if I'm making any mistakes, leaking memory, etc.

    Also in retrospect, bypassing the Animator component completely and assigning directly to SpriteRenderer might be a better way to go about it for spritesheet animations.

    Code (CSharp):
    1. public class AnimationReferenceTrackMixerBehaviour : PlayableBehaviour
    2. {
    3.     private PlayableGraph _graph;
    4.     private AnimationPlayableOutput[] outputs;
    5.     private AnimationClipPlayable[] animClipPlayables;
    6.  
    7.     // NOTE: This function is called at runtime and edit time.  Keep that in mind when setting the values of properties.
    8.     public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    9.     {
    10.         Animator trackBinding = playerData as Animator;
    11.         int inputCount = playable.GetInputCount();
    12.         if (!trackBinding)
    13.             return;
    14.  
    15.         if (outputs == null)
    16.         {
    17.             outputs = new AnimationPlayableOutput[inputCount];
    18.         }
    19.  
    20.         if (animClipPlayables == null)
    21.         {
    22.             animClipPlayables = new AnimationClipPlayable[inputCount];
    23.         }
    24.      
    25.         for (int i = 0; i < inputCount; i++)
    26.         {
    27.             float inputWeight = playable.GetInputWeight(i);        
    28.             ScriptPlayable<AnimationReferenceTrackBehaviour> inputPlayable = (ScriptPlayable<AnimationReferenceTrackBehaviour>)playable.GetInput(i);
    29.             _graph = inputPlayable.GetGraph();
    30.             AnimationReferenceTrackBehaviour input = inputPlayable.GetBehaviour ();
    31.          
    32.             if (outputs[i].IsOutputNull())
    33.             {
    34.                 var output = AnimationPlayableOutput.Create(_graph, "AnimationOutput", trackBinding);
    35.                 outputs[i] = output;
    36.                 // Extract the animation clip to use from the track reference object
    37.                 var animationClip = input.TrackReference.GetAnimationClip(input.AnimationKey);            
    38.                 var animPlayable = AnimationClipPlayable.Create(_graph, animationClip);            
    39.                 animClipPlayables[i] = animPlayable;            
    40.                 output.SetSourcePlayable(animPlayable);
    41.             }
    42.  
    43.             outputs[i].SetWeight(inputWeight);
    44.             var localTime = (float)inputPlayable.GetTime();
    45.             var duration = (float)inputPlayable.GetDuration();
    46.             float normalizedTime = localTime / duration;
    47.  
    48.             if (Mathf.Approximately(normalizedTime, 1))
    49.             {
    50.                 animClipPlayables[i].Pause();            
    51.             }
    52.             else
    53.             {
    54.                 animClipPlayables[i].Play();
    55.             }
    56.         }
    57.     }
    58.  
    59.     public override void OnPlayableDestroy(Playable playable)
    60.     {
    61.         base.OnPlayableDestroy(playable);
    62.         if (_graph.IsValid())
    63.         {
    64.             for (int i = 0; i < outputs.Length; i++)
    65.             {            
    66.                 _graph.DestroyOutput(outputs[i]);
    67.                 _graph.DestroyPlayable(animClipPlayables[i]);
    68.             }
    69.         }
    70.  
    71.         outputs = null;
    72.         animClipPlayables = null;      
    73.     }
    74.  
    75.  
    76. }
    77.