Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Playable: Split Animation Clip

Discussion in '5.4 Beta' started by Ziboo, Apr 20, 2016.

  1. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    356
    Hi,

    Something I would like to see, and I desperately need, is to have more api functions to manipulate clips (curves, time, etc..)

    I have an object with only one animation clip of 800 frames, that I need to cut into multiple 100 frames clip at runtime.

    So maybe something like:
    Code (CSharp):
    1. AnimationClip clip ;
    2. AnimationClipPlayable playable = AnimationClipPlayable.Create(clip);
    3. playable.SetStartFrame(0);
    4. playable.SetEndFrame(100);
    5. GetComponent<Animator>().Play(playable);
    Because for now I can only use the Legacy System to do something like that and that's sad :(
     
  2. catherineproulx

    catherineproulx

    Unity Technologies

    Joined:
    Apr 24, 2014
    Posts:
    16
    Hi,

    You can do this using the CustomPlayables (Documentation found here: http://docs.unity3d.com/540/Documentation/Manual/Playables-Custom.html)

    Something along the lines of this would probably meet your needs (You can then use the SplitClipPlayable within a mixer, a PlayQueue like in the manual example, etc.):

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Experimental.Director;
    3.  
    4. public class SplitClipPlayable : CustomAnimationPlayable
    5. {
    6.     AnimationClipPlayable m_ClipPlayable;
    7.     float startTime;
    8.     float endTime;
    9.  
    10.     public void SetClip(AnimationClip clip, float start, float end)
    11.     {
    12.         if (m_ClipPlayable.IsValid())
    13.         {
    14.             Playable.Disconnect(this, 0);
    15.             m_ClipPlayable.Destroy();
    16.         }
    17.      
    18.         m_ClipPlayable = AnimationClipPlayable.Create(clip);
    19.         Playable.Connect(m_ClipPlayable, this);
    20.         startTime = start;
    21.         endTime = end;
    22.     }
    23.  
    24.     override public void PrepareFrame(FrameData info)
    25.     {
    26.         float clipTime = info.time + startTime;
    27.         // loop if needed
    28.         while ( clipTime > endTime )
    29.         {
    30.             clipTime -= (endTime - startTime);
    31.         }
    32.         m_ClipPlayable.time = clipTime;
    33.     }
    34.  
    35.     public void OnDestroy()
    36.     {
    37.         if ( m_ClipPlayable.IsValid())
    38.             m_ClipPlayable.Destroy();
    39.     }
    40. }
    41.  
    42. [RequireComponent(typeof(Animator))]
    43. public class PlaySplitClip : MonoBehaviour
    44. {
    45.     public AnimationClip clip;
    46.     public float start;
    47.     public float end;
    48.  
    49.     private SplitClipPlayable m_SplitClip;
    50.  
    51.     void Start()
    52.     {
    53.         m_SplitClip = Playable.Create<SplitClipPlayable>();
    54.         m_SplitClip.SetClip(clip, start, end);
    55.         GetComponent<Animator>().Play(m_SplitClip);
    56.     }
    57.  
    58.     void OnDestroy()
    59.     {
    60.         m_SplitClip.Destroy();
    61.     }
    62. }
     
  3. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    356
    Thanks !

    I tried it, it's cool. But I don't really get what's goind on in the PrepareFrame method...

    Why not just ? :
    Code (CSharp):
    1. if(info.time > endTime)
    2.             m_ClipPlayable.time = startTime;
    And it seems that when using this script, changing the speed value of the playable does nothing ...

    EDIT:
    My mistake, I understand now, info.time is actually a refrence to Time.time, I thought it was the actual clip time.

    But the speed issue remain...
     
  4. catherineproulx

    catherineproulx

    Unity Technologies

    Joined:
    Apr 24, 2014
    Posts:
    16
    Yeah, it's just a little toy demo I cobbled together. (I wanted to substract the duration instead of just setting the start time for a smoother loop, but maybe that's overkill. :) )

    You'll probably want to tweak it for your needs. For the speed for instance, you'd have to forward that from the CustomPlayable to the AnimationClipPlayable nested inside. But you get the general idea.
     
  5. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    356
    Thats what I did but it doesn't work.
    I'm updating the speed during the update but it changes nothing

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.Experimental.Director;
    4.  
    5. public class SplitClipPlayable : AnimationPlayable
    6. {
    7.     public AnimationClipPlayable m_ClipPlayable;
    8.     float startTime;
    9.     float endTime;
    10.     public float speed;
    11.  
    12.  
    13.     public void SetClip(AnimationClip clip, float startFrame, float endFrame)
    14.     {
    15.         m_ClipPlayable = new AnimationClipPlayable(clip);
    16.      
    17.         Playable.Connect(m_ClipPlayable, this);
    18.  
    19.         startTime = startFrame / clip.frameRate;
    20.         endTime = endFrame / clip.frameRate;
    21.     }
    22.  
    23.     override public void PrepareFrame(FrameData info)
    24.     {
    25.  
    26.         float clipTime = info.time + startTime;
    27.         // loop if needed
    28.         while ( clipTime > endTime )
    29.         {
    30.             clipTime -= (endTime - startTime);
    31.         }
    32.         m_ClipPlayable.time = clipTime;
    33.  
    34.     }
    35.  
    36.  
    37. }
    38. [RequireComponent(typeof(Animator))]
    39. public class PlaySplitClip : MonoBehaviour
    40. {
    41.     public AnimationClip clip;
    42.     public float StartFrame;
    43.     public float EndFrame;
    44.     public float Speed;
    45.     private SplitClipPlayable m_SplitClip;
    46.     void Start()
    47.     {
    48.         m_SplitClip = new SplitClipPlayable(); // Playable.Create<SplitClipPlayable>();
    49.  
    50.         m_SplitClip.SetClip(clip, StartFrame, EndFrame);
    51.         GetComponent<Animator>().Play(m_SplitClip);
    52.     }
    53.  
    54.     void Update()
    55.     {
    56.         m_SplitClip.m_ClipPlayable.speed = Speed;
    57.     }
    58. }
    By the way, I'm using 5.3.4, so I change some code.
     
  6. catherineproulx

    catherineproulx

    Unity Technologies

    Joined:
    Apr 24, 2014
    Posts:
    16
    You'll need to take the speed into account when setting the m_ClipPlayable.time too.

    I think clipTime = info.time*speed+ startTime does the trick, but I'll let you figure out the looping.
     
  7. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    356
    Alright thanks !
    I did some modification if someone is interested:
    - Set range of animations
    - Pause/Unpause
    - Scrubing
    - Change Speed

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.Experimental.Director;
    4.  
    5. public class SplitClipPlayable : AnimationPlayable
    6. {
    7.     private AnimControllerParams acp;
    8.     private AnimationClip clip;
    9.     private AnimationClipPlayable m_ClipPlayable;
    10.  
    11.     public void SetAnimCtrlParams(ref AnimControllerParams animControllerParams)
    12.     {
    13.         if (clip != animControllerParams.Clip)
    14.         {
    15.             clip = animControllerParams.Clip;
    16.             if (m_ClipPlayable != null)
    17.             {
    18.                 m_ClipPlayable.Dispose();
    19.             }
    20.             m_ClipPlayable = new AnimationClipPlayable(clip);
    21.             Connect(m_ClipPlayable, this);
    22.         }
    23.  
    24.         acp = animControllerParams;
    25.     }
    26.  
    27.  
    28.     public override void PrepareFrame(FrameData info)
    29.     {
    30.         if (acp.Paused)
    31.         {
    32.  
    33.         }
    34.         else if (acp.Scrubing)
    35.         {
    36.             acp.ClipTime = Mathf.Lerp(acp.StartTime, acp.EndTime, acp.NormalizedPosition);
    37.         }
    38.         else
    39.         {
    40.             acp.ClipTime += info.deltaTime*acp.Speed;
    41.  
    42.             if (acp.Looping)
    43.             {
    44.                 if (acp.ClipTime > acp.EndTime)
    45.                 {
    46.                     acp.ClipTime = acp.StartTime;
    47.                 }
    48.                 else if (acp.ClipTime < acp.StartTime)
    49.                 {
    50.                     acp.ClipTime = acp.EndTime;
    51.                 }
    52.             }
    53.             else
    54.             {
    55.                 acp.ClipTime = acp.EndTime;
    56.             }
    57.  
    58.             acp.NormalizedPosition = Mathf.InverseLerp(acp.StartTime, acp.EndTime, acp.ClipTime);
    59.         }
    60.  
    61.         // Set Time
    62.         m_ClipPlayable.time = acp.ClipTime;
    63.     }
    64. }
    65.  
    66. [Serializable]
    67. public class AnimControllerParams
    68. {
    69.     public AnimationClip Clip;
    70.  
    71.     public float StartFrame;
    72.     public float EndFrame;
    73.  
    74.     public float Speed;
    75.  
    76.     public bool Paused;
    77.     public bool Looping;
    78.     public bool Scrubing;
    79.  
    80.     [Range(0f, 1f)]
    81.     public float NormalizedPosition;
    82.  
    83.     public float ClipTime;
    84.  
    85.  
    86.     public float StartTime
    87.     {
    88.         get { return StartFrame/Clip.frameRate; }
    89.     }
    90.  
    91.     public float EndTime
    92.     {
    93.         get { return EndFrame/Clip.frameRate; }
    94.     }
    95. }
    96.  
    97. [RequireComponent(typeof (Animator))]
    98. public class PlaySplitClip : MonoBehaviour
    99. {
    100.     public AnimControllerParams AnimControllerParams;
    101.     private SplitClipPlayable m_SplitClip;
    102.  
    103.  
    104.     private void Start()
    105.     {
    106.         m_SplitClip = new SplitClipPlayable(); // Playable.Create<SplitClipPlayable>();
    107.         UpdateClip();
    108.  
    109.         GetComponent<Animator>().Play(m_SplitClip);
    110.     }
    111.  
    112.     private void UpdateClip()
    113.     {
    114.         if (m_SplitClip == null)
    115.             return;
    116.  
    117.         m_SplitClip.SetAnimCtrlParams(ref AnimControllerParams);
    118.     }
    119.  
    120.     private void OnValidate()
    121.     {
    122.         UpdateClip();
    123.     }
    124.  
    125. }
    It's working great.

    But there is something strange.
    Try to force the m_ClipPlayable.time to 0 for instance in the PrepareFrame, or check Paused.
    You will see that the animated object is shacking...
    Is there another place I can tell the anim to truly pause ?

    I tried "state = PlayState.Paused;" but that does nothing