Search Unity

Modify AnimatorController through script and save it?

Discussion in 'Animation' started by teighshiclbw, Jan 15, 2019.

  1. teighshiclbw

    teighshiclbw

    Joined:
    Jun 26, 2018
    Posts:
    34
    Here's script:
    Code (CSharp):
    1.     [MenuItem("Tools/ModifyAnimator")]
    2.     static void ModifyAnimator()
    3.     {
    4.         foreach (Object o in Selection.objects)
    5.         {
    6.             AnimatorController animatorController = o as AnimatorController;
    7.  
    8.             foreach (ChildAnimatorState childAnimatorState in animatorController.layers[0].stateMachine.states)
    9.             {
    10.                 AnimatorState state = childAnimatorState.state;
    11.                 if (state.name == "State1")
    12.                 {
    13.                     AnimatorStateTransition newTransition = new AnimatorStateTransition();
    14.                     newTransition.destinationState = animatorController.layers[0].stateMachine.states[1].state;
    15.                     state.AddTransition(newTransition);
    16.                 }
    17.             }
    18.  
    19.             EditorUtility.SetDirty(animatorController);
    20.             AssetDatabase.SaveAssets();
    21.         }
    22.     }
    It load a AnimatorController from selection and found state1 and add transition to state2, then save this AnimatorController.
    Very simple.


    These code add transition succesfully,animator inspector shows it.
    But if I close Unity and open unity again, the transition disappear,AnimatorController not modify at all.

    How can I save modified AnimatorController through script?
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Could the problem be that one of the things you are getting is a struct that needs to be reassigned after being modified? Try setting childAnimatorState.state = state at the end of the if so it knows it has been modified. Or it could be any of the other things that you're getting but not setting.

    The fact that it shows what you want in the animator window suggests that's not the problem, but it's worth a try.

    I always hated having to deal with AnimatorControllers because scripting them was such a pain (and can only be done in the editor).
     
  3. teighshiclbw

    teighshiclbw

    Joined:
    Jun 26, 2018
    Posts:
    34
    Code (CSharp):
    1.     [MenuItem("Tools/ModifyAnimator")]
    2.     static void ModifyAnimator()
    3.     {
    4.         foreach (Object o in Selection.objects)
    5.         {
    6.             AnimatorController animatorController = o as AnimatorController;
    7.  
    8.             for (int i = 0; i < animatorController.layers[0].stateMachine.states.Length; i++)
    9.             {
    10.                 if (animatorController.layers[0].stateMachine.states[i].state.name == "State1")
    11.                 {
    12.                     AnimatorStateTransition newTransition = new AnimatorStateTransition();
    13.                     newTransition.destinationState = animatorController.layers[0].stateMachine.states[1].state;
    14.                     animatorController.layers[0].stateMachine.states[i].state.AddTransition(newTransition);
    15.                 }
    16.             }
    17.  
    18.             EditorUtility.SetDirty(animatorController);
    19.             AssetDatabase.SaveAssets();
    20.         }
    21.     }
    Here's new code.
    Modify data directly.
    And it still not work unfortunately.
    Yes, animator code is pain.
     
  4. KittyAnn

    KittyAnn

    Joined:
    Dec 25, 2018
    Posts:
    10
    I had a problem with my transitions from idle to walking not working; it wasn't playing just the one animation. Here's a video I found that helped. He puts all his code in too. Very simple & easy to follow along with. Hope this helps!
     
  5. teighshiclbw

    teighshiclbw

    Joined:
    Jun 26, 2018
    Posts:
    34
    Sorry, but I think we are not talking about the same thing.
    This video you post is about how to use Animator through script. I know how to use it.
    I just don't know how to save a script-modified AnimatorController.
     
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    I seem to remember that the AnimatorController returns a ton of things by copy instead of by reference. I believe that goes for both
    animatorController.layers
    and
    stateMachine.states
    .

    So you might need to:

    Code (csharp):
    1. var layers = animatorController.layers;
    2. //edit layers
    3. animatorController.layers = layers;
    Though I'm a bit confused by the thing showing up in the animator window at all if that's the case.
     
  7. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I suspect that one of the structs has a reference to its parent so the values do get set correctly, they just don't get flagged as dirty.

    Basically the opposite of the particle system where C# doesn't allow you to set
    Code (CSharp):
    1. particleSystem.emission.enabled
    directly because it's a struct, so you have to do
    Code (CSharp):
    1. var emission = Target.emission;
    2. emission.enabled = value;
    and then (unlike in an AnimatorController) you can't even reassign
    Code (CSharp):
    1. particleSystem.emission = emission;
    because it's a readonly property.
     
  8. teighshiclbw

    teighshiclbw

    Joined:
    Jun 26, 2018
    Posts:
    34
    Code (CSharp):
    1.     [MenuItem("Tools/ModifyAnimator")]
    2.     static void ModifyAnimator()
    3.     {
    4.         foreach (Object o in Selection.objects)
    5.         {
    6.             AnimatorController animatorController = o as AnimatorController;
    7.  
    8.             AnimatorControllerLayer[] layers = animatorController.layers;
    9.             for (int i = 0; i < layers[0].stateMachine.states.Length; i++)
    10.             {
    11.                 if (layers[0].stateMachine.states[i].state.name == "State1")
    12.                 {
    13.                     AnimatorStateTransition newTransition = new AnimatorStateTransition();
    14.                     newTransition.destinationState = layers[0].stateMachine.states[1].state;
    15.                     layers[0].stateMachine.states[i].state.AddTransition(newTransition);
    16.                 }
    17.             }
    18.             animatorController.layers = layers;
    19.  
    20.             EditorUtility.SetDirty(o);
    21.             AssetDatabase.SaveAssets();
    22.         }
    23.     }
    Nope, it's not work.
     
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Yup, I was wrong.

    Looked into it, the issue is that AddTransition with a AnimatorStateTransition doesn't add the transition asset to the controller asset. The version that takes a State does, though. So this works:

    Code (csharp):
    1.  
    2. [MenuItem("Tools/ModifyAnimator")]
    3. public static void ModifyAnimator() {
    4.     foreach (Object o in Selection.objects) {
    5.         AnimatorController animatorController = o as AnimatorController;
    6.  
    7.         var sm = animatorController.layers[0].stateMachine;
    8.         sm.states[0].state.AddTransition(sm.states[1].state);
    9.  
    10.         EditorUtility.SetDirty(o);
    11.         AssetDatabase.SaveAssets();
    12.     }
    13. }
    14.  
    You could also do what Unity does internally in CreateTransition, which you can see here (line 122), ie. add the transition to the controller manually.
     
    teighshiclbw likes this.
  10. teighshiclbw

    teighshiclbw

    Joined:
    Jun 26, 2018
    Posts:
    34
    This actually work!
    THANKS!
     
  11. XDura

    XDura

    Joined:
    Oct 27, 2016
    Posts:
    26
    Also worked for me, I was struggling with this because I was creating them with the AddTransition that takes the AnimatorStateTransition and once I closed the editor the transitions disappeared.

    Thanks!
     
  12. newMember

    newMember

    Joined:
    Sep 24, 2019
    Posts:
    14
    I do generation of stateMachines via script, but why doesn't this work for saving them either? Can you help, please?

    Code (CSharp):
    1. public AnimatorController CurrentController;
    2. AnimatorControllerLayer[] layers = CurrentController.layers;
    3. AnimatorControllerLayer baseLayer = layers[0];
    4. baseLayer.stateMachine.AddStateMachine(new AnimatorStateMachine(), Vector3.zero);
    5. EditorUtility.SetDirty(CurrentController);
    6. AssetDatabase.SaveAssets();

    UPD. I found solution for this issue


    Code (CSharp):
    1.     [MenuItem("Tools/ModifyAnimator")]
    2.     public static  void ModifyAnimator()
    3.     {
    4.         Object w = AssetDatabase.LoadAssetAtPath("Assets/Anim/TestAC_Generator.controller", typeof(AnimatorController));
    5.         AnimatorController animatorController = w as AnimatorController;
    6.  
    7.         var sm = animatorController.layers[0].stateMachine;
    8.         AnimatorStateMachine s = new AnimatorStateMachine();
    9.         s.name = "2222";
    10.  
    11.         sm.AddStateMachine(s, Vector3.zero);
    12.  
    13.         foreach (var a in sm.stateMachines)
    14.         {
    15.             //if (AssetDatabase.GetAssetPath(a.stateMachine) != "") // doesn't work for some reasons
    16.             if (AssetDatabase.GetAssetPath(a.stateMachine).Length == 0)
    17.             {
    18.                 AssetDatabase.AddObjectToAsset(a.stateMachine, AssetDatabase.GetAssetPath(animatorController));
    19.                 a.stateMachine.hideFlags = HideFlags.HideInHierarchy;
    20.             }
    21.         }
    22.     }
    You should repeat the process of adding to asset for every piece of stuff you add
     
    Last edited: Dec 12, 2019
    Pleija and Cj-bc like this.