Search Unity

How to add .anim files into a controller?

Discussion in 'Animation' started by xzaxzaazx, Apr 26, 2019.

  1. xzaxzaazx

    xzaxzaazx

    Joined:
    Mar 18, 2019
    Posts:
    10
    upload_2019-4-26_14-56-40.png
    I can see Unity creating a Button controller like this, it's a perfect structure, but I found the only way to merge these files is modify and merge their source code. I had to do this everytime when I ever created a new .anim file.
    无标题.png
    Also the .anim files can't be renamed or deleted.
    Anyone knows some easy way to create .anim file in a .controller file like this?
     
    Last edited: Apr 28, 2019
    rboerdijk likes this.
  2. xzaxzaazx

    xzaxzaazx

    Joined:
    Mar 18, 2019
    Posts:
    10
    Finally got a perfect solution by my Editor Script:
    (warning:All runtime assets like Animations and AnimatorControllers will be saved after this operation!)
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using UnityEditor.Animations;
    6.  
    7. public class NestAnimClips : MonoBehaviour
    8. {
    9.     [MenuItem("Assets/Nest AnimClips in Controller",true)]
    10.     static public bool nestAnimClipsValidate(){
    11.      
    12.         return Selection.activeObject.GetType() == typeof(UnityEditor.Animations.AnimatorController);
    13.     }
    14.     [MenuItem("Assets/Nest AnimClips in Controller")]
    15.     static public void nestAnimClips()
    16.     {
    17.         AnimatorController anim_controller = (AnimatorController)Selection.activeObject;        
    18.         Object[] objects=AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(anim_controller));
    19.         if (anim_controller != null)
    20.         {
    21.             AssetDatabase.SaveAssets();
    22.             List<ChildAnimatorState> states=new List<ChildAnimatorState>();
    23.             AnimatorControllerLayer[] layers = anim_controller.layers;
    24.             foreach(AnimatorControllerLayer layer in layers){
    25.                 states.AddRange(layer.stateMachine.states.ToList<ChildAnimatorState>());
    26.             }
    27.             if(states.Count>0){
    28.                 for (int i = 0; i < states.Count; i++)
    29.                 {
    30.                     if(states[i].state.motion){
    31.                         var newClip=Object.Instantiate((AnimationClip)states[i].state.motion) as AnimationClip;
    32.                         newClip.name=states[i].state.motion.name;
    33.                         AssetDatabase.AddObjectToAsset(newClip, anim_controller);
    34.                         AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(newClip));
    35.                         states[i].state.motion=newClip;
    36.                     }                        
    37.                 }
    38.             }
    39.         }
    40.         for (int i = 0; i < objects.Length; i++)
    41.         {
    42.             if(objects[i].GetType()==typeof(AnimationClip)){
    43.                 Object.DestroyImmediate(objects[i],true);
    44.             }
    45.         }
    46.         AssetDatabase.SaveAssets();
    47.     }
    48. }
     
    vedram likes this.
  3. vedram

    vedram

    Joined:
    May 23, 2018
    Posts:
    5
    @xzaxzaazx Perfect solution, thank you! Works really well.
    I just had problems if same AnimationClip would be used in multiple layers, then it would duplicate them in .asset file. So I adjusted your code to not create duplicates:

    Code (CSharp):
    1. [MenuItem( "Assets/Nest AnimationClips in Controller", true )]
    2. static public bool nestAnimClipsValidate() => Selection.activeObject.GetType() == typeof( AnimatorController );
    3.  
    4. [MenuItem( "Assets/Nest AnimationClips in Controller" )]
    5. static public void nestAnimClips()
    6. {
    7.     AnimatorController anim_controller = (AnimatorController)Selection.activeObject;
    8.     if( anim_controller == null ) return;
    9.  
    10.     // Get all objects currently in Controller asset, we'll destroy them later
    11.     UnityEngine.Object[] objects = AssetDatabase.LoadAllAssetsAtPath( AssetDatabase.GetAssetPath(anim_controller) );
    12.  
    13.     AssetDatabase.SaveAssets();
    14.  
    15.     // Add animations from all animation layers, without duplicating them
    16.     var oldToNew = new Dictionary<AnimationClip, AnimationClip>();
    17.     foreach( AnimatorControllerLayer layer in anim_controller.layers )
    18.     {
    19.         foreach( var state in layer.stateMachine.states )
    20.         {
    21.             var old = state.state.motion as AnimationClip;
    22.             if( old == null ) continue;
    23.  
    24.             if( !oldToNew.ContainsKey(old) )    // New animation in list - create new instance
    25.             {
    26.                 var newClip = UnityEngine.Object.Instantiate(old) as AnimationClip;
    27.                 newClip.name = old.name;
    28.                 AssetDatabase.AddObjectToAsset( newClip, anim_controller );
    29.                 AssetDatabase.ImportAsset( AssetDatabase.GetAssetPath( newClip ) );
    30.                 oldToNew[old] = newClip;
    31.                 Debug.Log( "Nested animation clip: " + newClip.name );
    32.             }
    33.  
    34.             state.state.motion = oldToNew[old];
    35.         }
    36.     }
    37.  
    38.     // Destroy all old AnimationClips in asset
    39.     for( int i = 0; i < objects.Length; i++ )
    40.     {
    41.         if( objects[i].GetType() == typeof( AnimationClip ) )
    42.         {
    43.             UnityEngine.Object.DestroyImmediate( objects[i], true );
    44.         }
    45.     }
    46.     AssetDatabase.SaveAssets();
    47. }
    48.  
     
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    You could change the menu path to "CONTEXT/AnimatorController/Nest AnimationClips in Controller" and give the method a "MenuCommand command" parameter (command.context will give you the selected object). That way it will appear in the context menu for Animator Controllers so you don't clutter up the main Assets menu and it will work properly if you have multiple Animator Controllers selected at once.
     
    Vedran_M likes this.
  5. rboerdijk

    rboerdijk

    Joined:
    Aug 4, 2018
    Posts:
    96
    I ran into this and was surprised I couldn't add clips to the default generated button-animation.
    Found this post and tried it and indeed it works nicely.
     
  6. Davood_Kharmanzar

    Davood_Kharmanzar

    Joined:
    Sep 20, 2017
    Posts:
    411
    @xzaxzaazx
    amazing script ...
    but seems that can't collect animation clips that stored on fbx files!!??
    does exists any solution? [unity 2020.x]