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

Generate AnimatorController with MenuItem

Discussion in 'Scripting' started by Tohrak, Mar 29, 2019.

  1. Tohrak

    Tohrak

    Joined:
    Nov 2, 2015
    Posts:
    12
    Hello,
    I made a small script to generate an AnimatorController with each AnimationClip selected as a Layer and Parameter to save me some time :

    Code (CSharp):
    1. [MenuItem("Assets/Generate Animator", false, 1000)]
    2.     static void GenerateAnimator(){
    3.  
    4.         if(Selection.activeObject){
    5.  
    6.             string path = AssetDatabase.GetAssetPath(Selection.activeInstanceID);
    7.             if(path.Length > 0){
    8.                 path = System.IO.Path.GetDirectoryName(path);
    9.             }
    10.  
    11.             Object[] animations = Selection.GetFiltered(typeof(AnimationClip), SelectionMode.Assets);
    12.  
    13.             AnimatorController animatorController = new AnimatorController();
    14.  
    15.             AssetDatabase.CreateAsset(animatorController, AssetDatabase.GenerateUniqueAssetPath(path + "/AnimatorController.controller"));
    16.  
    17.             foreach(AnimationClip animationClip in animations){
    18.                 string layerName = animatorController.MakeUniqueLayerName(animationClip.name);
    19.                 string parameterName = animatorController.MakeUniqueLayerName(animationClip.name);
    20.                 animatorController.AddLayer(layerName);
    21.                 animatorController.AddMotion(animationClip , animatorController.layers.Length - 1);
    22.                 animatorController.AddParameter(parameterName, AnimatorControllerParameterType.Float);
    23.  
    24.                 AnimatorControllerLayer animatorControllerLayer = animatorController.layers[animatorController.layers.Length - 1];
    25.                 animatorControllerLayer.stateMachine.states[0].state.speedParameter = parameterName;
    26.                 animatorControllerLayer.stateMachine.states[0].state.speedParameterActive = true;
    27.                 animatorControllerLayer.defaultWeight = 1;
    28.                 animatorControllerLayer.blendingMode = AnimatorLayerBlendingMode.Additive;
    29.                 // /!\ Bug ?  Any AnimatorControllerLayer modification done here doesn't do anything to the final AnimatorController.
    30.  
    31.                 AnimatorControllerParameter animatorControllerParameter = animatorController.parameters[animatorController.parameters.Length - 1];
    32.             }
    33.  
    34.             Undo.RegisterCreatedObjectUndo(animatorController, "Create" + animatorController.name);
    35.             Selection.activeObject = animatorController;
    36.         }
    37.     }
    It works well, except for the AnimatorControllerLayer part, no modification is saved. For exemple, when I set the AnimatorControllerLayer defaultWeight to 1 with : animatorControllerLayer.defaultWeight = 1;, the created layer will still have a defaultWeight of 0.

    Strangely enough, this bug only appear when the script is run from a MenuItem. If I run the same script within a WindowEditor for exemple, it works, all AnimatorControllerLayer modification are saved. But it will be more effective for me to create those AnimatorController with MenuItem.

    Any idea ? Is it a bug, or did I miss something ?

    And another thing, is there any way to Repaint() the AnimatorWindow ? If I Delete the AnimatorControllerLayer which is selected in the AnimatorWindow, I got this error :

    MissingReferenceException: The object of type 'AnimatorStateMachine' has been destroyed but you are still trying to access it.
    Your script should either check if it is null or you should not destroy the object.

    The error stop spawning as soon as I select another Layer.


    Or maybe there is a way to unselect the current Layer in the AnimatorWindow ?

    Thank you for your time.
     
  2. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    957
    The array of layers is returned as a copy and not a reference, so you need to modify the fields on that copy and then reassign the layers field. For example:

    Code (CSharp):
    1. AnimatorControllerLayer[] layers = animatorController.layers;
    2. layers[0].defaultWeight = 1;
    3. layers[1].defaultWeight = 1;
    4. layers[1].blendingMode = AnimatorLayerBlendingMode.Additive;
    5. animatorController.layers = layers;
     
  3. Tohrak

    Tohrak

    Joined:
    Nov 2, 2015
    Posts:
    12
    Thank you very much, it works now. =)