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.
Finally got a perfect solution by my Editor Script:
(warning:All runtime assets like Animations and AnimatorControllers will be saved after this operation!)
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Animations;
public class NestAnimClips : MonoBehaviour
{
[MenuItem("Assets/Nest AnimClips in Controller",true)]
static public bool nestAnimClipsValidate(){
return Selection.activeObject.GetType() == typeof(UnityEditor.Animations.AnimatorController);
}
[MenuItem("Assets/Nest AnimClips in Controller")]
static public void nestAnimClips()
{
AnimatorController anim_controller = (AnimatorController)Selection.activeObject;
Object[] objects=AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(anim_controller));
if (anim_controller != null)
{
AssetDatabase.SaveAssets();
List<ChildAnimatorState> states=new List<ChildAnimatorState>();
AnimatorControllerLayer[] layers = anim_controller.layers;
foreach(AnimatorControllerLayer layer in layers){
states.AddRange(layer.stateMachine.states.ToList<ChildAnimatorState>());
}
if(states.Count>0){
for (int i = 0; i < states.Count; i++)
{
if(states[i].state.motion){
var newClip=Object.Instantiate((AnimationClip)states[i].state.motion) as AnimationClip;
newClip.name=states[i].state.motion.name;
AssetDatabase.AddObjectToAsset(newClip, anim_controller);
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(newClip));
states[i].state.motion=newClip;
}
}
}
}
for (int i = 0; i < objects.Length; i++)
{
if(objects[i].GetType()==typeof(AnimationClip)){
Object.DestroyImmediate(objects[i],true);
}
}
AssetDatabase.SaveAssets();
}
}
@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:
[MenuItem( "Assets/Nest AnimationClips in Controller", true )]
static public bool nestAnimClipsValidate() => Selection.activeObject.GetType() == typeof( AnimatorController );
[MenuItem( "Assets/Nest AnimationClips in Controller" )]
static public void nestAnimClips()
{
AnimatorController anim_controller = (AnimatorController)Selection.activeObject;
if( anim_controller == null ) return;
// Get all objects currently in Controller asset, we'll destroy them later
UnityEngine.Object[] objects = AssetDatabase.LoadAllAssetsAtPath( AssetDatabase.GetAssetPath(anim_controller) );
AssetDatabase.SaveAssets();
// Add animations from all animation layers, without duplicating them
var oldToNew = new Dictionary<AnimationClip, AnimationClip>();
foreach( AnimatorControllerLayer layer in anim_controller.layers )
{
foreach( var state in layer.stateMachine.states )
{
var old = state.state.motion as AnimationClip;
if( old == null ) continue;
if( !oldToNew.ContainsKey(old) ) // New animation in list - create new instance
{
var newClip = UnityEngine.Object.Instantiate(old) as AnimationClip;
newClip.name = old.name;
AssetDatabase.AddObjectToAsset( newClip, anim_controller );
AssetDatabase.ImportAsset( AssetDatabase.GetAssetPath( newClip ) );
oldToNew[old] = newClip;
Debug.Log( "Nested animation clip: " + newClip.name );
}
state.state.motion = oldToNew[old];
}
}
// Destroy all old AnimationClips in asset
for( int i = 0; i < objects.Length; i++ )
{
if( objects[i].GetType() == typeof( AnimationClip ) )
{
UnityEngine.Object.DestroyImmediate( objects[i], true );
}
}
AssetDatabase.SaveAssets();
}
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.
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.