Create Mecanim states and transitions by script

Hi,
we will have a large number of animations (~1000) and therefore think about creating parts of the animation graph automatically by script. Potentially we might import animations from an excel sheet or maybe we just automate some recurring tasks like connecting an attack animation to the idle state(s).

Here is an example on how to adding some transitions from the selected state to the idle state. Note that I stripped out lots of tests, e.g. the selected state could be null.

using UnityEngine;
using UnityEditor;
using UnityEditorInternal;

AnimatorController m_controller = AnimatorController.lastActiveController;
StateMachine m_stateMachine = m_controller.GetLayerStateMachine(AnimatorController.lastActiveLayerIndex);
State selectedState = Selection.activeObject as State;
State idleState = m_stateMachine.FindState(idleName);
Transition tin = m_stateMachine.AddTransition(idleState, selectedState);
tin.SetConditionEvent(0, "GeneralState");
tin.SetConditionMode(0, TransitionConditionMode.Equals);
tin.SetEventTreshold(0, 42);
tin.AddCondition();
tin.SetConditionEvent(1, "Random");
tin.SetConditionMode(1, TransitionConditionMode.Less);
tin.SetEventTreshold(1, 0.5f);
Transition tout = m_stateMachine.AddTransition(selectedState, idleState);
tout.SetExitTime(0, 0.9f);

However, while I can create states, transitions and conditions, I have the problem that this throws lots of ArgumentExceptions:

This answer says ā€œYou changed the content of your GUI between the Layout and Repaint eventā€, but I don’t have any control over the OnGUI of the animator window. Also the animator window graphics get kind of messed up and look broken. It takes a click into the animator window to force it to repaint properly.

Yes, I know I working with undocumented internal API. But we need this because doing this by hand would most likely cause a lot of hard to find bugs. Did anyone figure out a way to this the right way? Or maybe just a hack to at least force the Animator window to repaint?

Hey,

I’m having a similar problem. I created my state machines by hand (although I wish I had done them in code, that was a tedious process matching up the transition timing on different layers). Now, I’'m trying to create a list of states and their hashcodes, so I can create an array of layer dictionaries, so i can pass in the hashcode and get the layername, or vice versa.

Anyways, I’m also using UnityEditorInternal.AnimatorController. It works FANTASTIC. I’m using Unity 4.2. When I test this code, it pulls out all of the states, and all of the animations for each state.

The only problem is in Building the project. If I try to ā€œBuildā€ for windows/mac, it throws an error and says the UnityEditorInternal namespace is not found.

Does anyone know how to get around this? Also, here’s the code to find all the states and their transitions, in exchange for someone hopefully helping me.

		UnityEditorInternal.AnimatorController ac = GetComponent<Animator>().runtimeAnimatorController as UnityEditorInternal.AnimatorController;
		int numLayers = ac.layerCount;
				
		for(int i = 0; i<numLayers; i++){
			UnityEditorInternal.AnimatorControllerLayer layer = ac.GetLayer(i);
			Debug.Log ("Layer " + i + " is: " + layer.name + " and has " + layer.stateMachine.stateCount + " states");
			UnityEditorInternal.StateMachine sm = layer.stateMachine;
			for(int n = 0; n<sm.stateCount; n++){
				UnityEditorInternal.State state = sm.GetState(n);
				Debug.Log ("State " + state.name + " is " + state.GetHashCode());
				UnityEditorInternal.Transition[] list = sm.GetTransitionsFromState(state);
				for(int j = 0; j<list.Length; j++){
					UnityEditorInternal.Transition transition = list[j];
					Debug.Log ("Transition: " + transition.name + " is " + transition.GetHashCode());
				}
			}
		}

Just like UnityEditor namespace UnityEditorInternal is only available in the editor, not at runtime in the player. Probably you might want to generate your dictionary in the editor and serialize it somehow, so you can load it again in the player.

However, to get the hash of a state name at runtime you already have to know the layer anyway? You would use Animator.GetCurrentAnimatorStateInfo for that which gets the layer index, right?

I’m in the early stages of detecting changes in states, and triggering events. I can get the GetCurrentAnimatorStateInfo(0), but it gives me the hash code. I’m trying to simplify it, so I can do something like this psuedocode:

if(animator.isInTransition){
 if(lastState == "idle"  nextState == "attack"){
    StartAttack();
 }else{
   CantAttackWhileNotIdle();
 }
}

I’m creating the dictionary when the animator is created, and I want to use the dictionary to simplify state detection and stuff. Otherwise, I would have to go in and manually create variables for each state and transition, and it would have to be manually updated every time I change a state or transition. This method automates that part at runtime, so I can just create a statemachine and not have to worry about mapping state and transition names to their hashes.

You should not convert hashes back to strings, except maybe for debugging.

We are using something like this:

public class States
{
	public static readonly int Attack= Animator.StringToHash("Attack");
	public static readonly int GetHit = Animator.StringToHash("GetHit");
	public static readonly int Walk= Animator.StringToHash("Walk");
	public static readonly int Idle= Animator.StringToHash("Idle");
}

So you can compare against States.Attack instead of ā€œattackā€. However, we are mostly matching tag hashes instead of state name hashes, since we always have multiple animations (and states) for the same thing.

1 Like

I think this is more or less what I’m doing. I was going to make the dictionary/hashtable so I could do states[ā€˜attack’] instead of states.attack. I want it to be set up automatically at runtime, so I don’t have to worry about updating the States class every time I add a new state and 10 new transitions into and out of the state.

So basically, what I’m going to wind up doing, is creating a .xml file (or json or something i dunno) every time I ā€œtestā€ the build. Then when I go to Build it, it’ll load the file instead of creating the file, and it should have all the latest animation data to use. Obviously I’ll have to update the controller to respect that data, but I won’t have to go through the tedious process every time I change something. The point of this function is to take human error out of the equation.

I’m also going to use it as the base of setting up an events system for Mecanim. Forget paying $35, I’ll detect state changes and transitions on my own dammit.

Hi Guys
trying desperately to add the conditions in the state machine via script. I can add the states but not the conditions.

In Particular

  • tin.SetConditionEvent(0, ā€œGeneralStateā€);
  • tin.SetConditionMode(0, TransitionConditionMode.Equals);
  • tin.SetEventTreshold(0, 42);
  • tin.AddCondition();
  • tin.SetConditionEvent(1, ā€œRandomā€);

These methods dont come up when i try to call them. I can use instead
UnityEditorInternal.Transitiontin = sm.AddTransition(STATE_TO_CONNECT, state);
UnityEditorInternal.Transitiontout = sm.AddTransition(state, STATE_TO_CONNECT);

tin.AddCondition();
UnityEditorInternal.AnimatorConditionconIn = tin.GetCondition(0);

and once i have the condition i find some methond to call to set the condition but i dont have methods to add a simple stupid boolean value equals true.
Any suggestions?

Martin maybe?
regards
Egi

I now managed to create all states and conditions via code

but i get this error
index < m_IntCount

very very very frustrating - all animation transitions are set and look exactly like if have been created by hand but the animator goes bananas when i test the app.
and the weird red error
index < m_IntCount

pls help.
regards
eg

Sound like you try to set condition data without having added the condition first? Without seeing your code I can just guess.

Hi Martin
thanks for getting back
The code is below. I can now connect all the animations but it doesnt work. ALl the animations looks connected and the conditions when i check them are there but it doesnt work.

for(intn = 0; n<sm.stateCount; n++){
UnityEditorInternal.Statestate = sm.GetState(n);
Debug.Log ("State " +n+ " index " + state.name + " is " + state.GetHashCode() + " instanceID " + state.GetInstanceID());

//connectthestatetoalltheothersiftheotherispresentinthearrayallAnimationsArr
foreach (stringmoveinallAnimationsArr)
{
if (move == state.name)
{
Debug.Log ("-----------connecting state hash: "+STATE_TO_CONNECT.GetHashCode() + " with " + state.name );
sm.AddTransition(STATE_TO_CONNECT, state);
sm.AddTransition(state, STATE_TO_CONNECT);

UnityEditorInternal.Transitiontin = sm.AddTransition(STATE_TO_CONNECT, state);
UnityEditorInternal.Transitiontout = sm.AddTransition(state, STATE_TO_CONNECT);

UnityEditorInternal.AnimatorConditionconIn = tin.GetCondition(0);
conIn.mode = TransitionConditionMode.Equals;
conIn.parameter = (state.name);

UnityEditorInternal.AnimatorConditionconOut = tout.GetCondition(0);
conOut.mode = TransitionConditionMode.Equals;
conOut.parameter = (STATE_TO_CONNECT.name);

}

}

for(int n = 0; n<sm.stateCount; n++){
                UnityEditorInternal.State state = sm.GetState(n);
                Debug.Log ("State " +n+ " index " + state.name + " is " + state.GetHashCode() + " instanceID " + state.GetInstanceID());

                // connect the state to all the others if the other is present in the array allAnimationsArr
                foreach (string move in allAnimationsArr)
                {
                    if (move == state.name)
                    {
                        Debug.Log ("-----------connecting state hash: "+STATE_TO_CONNECT.GetHashCode() + " with " + state.name  );
                        sm.AddTransition(STATE_TO_CONNECT, state);
                        sm.AddTransition(state, STATE_TO_CONNECT);
                       
                        UnityEditorInternal.Transition tin = sm.AddTransition(STATE_TO_CONNECT, state);
                        UnityEditorInternal.Transition tout = sm.AddTransition(state, STATE_TO_CONNECT);

                        UnityEditorInternal.AnimatorCondition conIn = tin.GetCondition(0);
                        conIn.mode = TransitionConditionMode.Equals;
                        conIn.parameter = (state.name);

                        UnityEditorInternal.AnimatorCondition conOut = tout.GetCondition(0);                       
                        conOut.mode = TransitionConditionMode.Equals;
                        conOut.parameter = (STATE_TO_CONNECT.name);


                                       
                    }


                }

You add the transitions twice, there are 4 calls to AddTransition. I’m not sure Unity does handle that the way you expect it to.

I seriously hope that undocumented internal API gets documented and opened! It’d allow a huge amount of flexibility in areas that previously got me stuck, and allow us to write better tools to boot.

Looks that way:
http://blogs.unity3d.com/2014/06/26/shiny-new-animation-features-in-unity-5-0/

Hi Martin
there are two states so i need two calls to connect them together.
Do you have a snap code that does the job withiut incurring in the error i get?
i just want to connect the states via code with a simple boolean condition set.
thanks a lot
egi

took me ages to create a state machine with 70 states interconnected manually and although i have the code to do it it just wont work. frustrating
when unity 5 comes about?