I've stumbled upon something I never imagined to be a problem but... it is. I am writing a visualiazion/simulation toolkit using unity 5.5.
The used model is already imported and animation clips are set via the editor. This is what my animation state controller lookes like:
I want to display the AnimationStates in a DropdownMenu. Whenever a user selects an entry, the current animation state should be set to the selected one.
e.G. User selected Autoplay: State set to clip Autoplay, after that to SimpleRotate, then to ComplexRotate and again to Autoplay (just a simple loop).
But if the user selects Idle, ComplexRotate 0 or SimpleRotate 0, the animation should play once and then stay at the end of the clip. (Just like I did it in the animation controller.
Heres a rather pseudo-version of what i want:
//Retrieve all states to populate the dropdown:
List<AnimationState> states = myAnimationController.GetAllStates();
foreach(AnimationState state in states)
Dropdown.add(state.name)
//callback
OnDropdownIndexChanged(int item)
{
string stateName = ..... //removed for simplicity
myAnimationController.setState(stateName)
}
On top of that it would be nice if I could check the transitions FROM and TO the states. (Autoplay is the only state that should be shown in the dropdown for the loop)
Can this be realized with a custom Animation Controller? Or did I miss something?
I see two options here. A rather simple one and a bit more complex, but robust, one.
Option One
You use the RuntimeAnimatorController
. This will get you all the AnimationClips in the given Animator at runtime. You can play a State in an AnimatorController at any given time by using Animator.Play(StateNameOrHash)
. Unfortunately, for this option, this will only play a State by its Name or NameHash (as statet in the docs). Because we only have the AnimationClips
and not the States from the Controller this option would involve to rename the States in the controller to the exact name of the Animation Clip. Then you could play a State like this:
myAnimator.Play(myAnimationClip.name);
This is a fast option to get you started but not a good option in the long run because it has essentially two drawbacks:
AnimationClip
. Therefore you would not be able to retrief these States through the RuntimeAnimatorController
because it only returns the AnimationClips
AnimationClips
. I dont know your situation here but it would be better to use descriptive names then the names from the AnimationClips for better understanding.Option Two
This option is more complex. I will give you a quick overview of the Workflow and then explain the parts in more detail or give references to get you started!
Part 1: Use an Editor Script to extract all the States from your AnimatorController. This link should give you an hint on how to achieve this. Short (not tested) example code:
private void writeAllStates()
{
AnimatorControllerLayer[] allLayer = controller.layers;
List<string> stateNames = new List<string>();
for (int i = 0; i < allLayer.Length; i++)
{
ChildAnimatorState[] states = allLayer[i].stateMachine.states;
for (int j = 0; j < states.Length; j++)
{
// you could do additional filtering here. like "would i like to add this state to my list because it has no exit transition" etc
if (shouldBeInserted(states[i]))
{
// add state to list
stateNames.Add(states[j].state.name);
}
}
}
// now generate a textfile to write all the states
FileGenerator.createFileForController(controller, stateNames);
}
Keep in mind: the used AnimatorController
is within the UnityEditor.Animations
namespace. This can be a public field in an EditorScript to easily give you access.
Part 2: Your scriptfile should create a class like this one:
public class AnimatorControllerStates
{
public List<string> states = new List<string>
{
"Idle",
"ComplexRotate 0",
"Autoplay",
...
}
}
Name the class somehow related to your AnimatorController you created it for. And make this class a field in your runtime script. Then you could get access to all your states like this:
myAnimatorControllerStates.states
This array can fill your dropdown. You would save the index of the dropdown and then can play the state by the index.
Part 3: Now it would be realy easy to just play your state in your script:
string state = myAnimatorControllerStates.states[mySelectedIndex];
myAnimatorController.Play(mySelectedIndex);
I dont know the scope of your project but to have a maintainable application I would prefer option two at any time.