检索所有 Animator 状态并在代码中手动设置它们

Retrieve all Animator States and set them manually in code

我偶然发现了一些我从未想过会成为问题的事情,但是......它确实是。 我正在使用 unity 5.5 编写 visualiazion/simulation 工具包。

使用的模型已经导入,通过编辑器设置动画片段。这是我的动画状态控制器的样子:

我想在 DropdownMenu 中显示 AnimationStates。每当用户选择一个条目时,当前动画状态都应设置为所选条目。

例如用户选择了自动播放:状态设置为剪辑自动播放,之后为 SimpleRotate,然后为 ComplexRotate,然后再次为自动播放(只是一个简单的循环)。

但如果用户选择 Idle、ComplexRotate 0 或 SimpleRotate 0,动画应该播放一次,然后停留在剪辑的末尾。 (就像我在动画控制器里做的一样

这是我想要的一个伪版本:

//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)
}

最重要的是,如果我可以检查从状态到状态的转换,那就太好了。 (自动播放是循环下拉列表中唯一应显示的状态)

这可以用自定义动画控制器来实现吗?还是我错过了什么?

我在这里看到两个选项。一个相当简单的,有点复杂,但很健壮,一个。

选项一

您使用了RuntimeAnimatorController。这会让你 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)。因为我们只有 AnimationClips 而不是来自控制器的状态,所以此选项将涉及将控制器中的状态重命名为动画剪辑的确切名称。然后你可以像这样玩一个状态:

myAnimator.Play(myAnimationClip.name);

这是一个快速入门的选项,但从长远来看并不是一个好的选择 运行,因为它本质上有两个缺点:

  • 您只能在 AnimatorController 中使用平面层次结构。没有 SubStateMachines/Blendtrees 因为它们没有用 AnimationClip 表示。因此,您将无法通过 RuntimeAnimatorController 检索这些状态,因为它仅 returns AnimationClips
  • AnimatorController 可能很难理解,因为状态的命名类似于 AnimationClips。我不知道你在这里的情况,但最好使用描述性名称然后使用 AnimationClips 中的名称以便更好地理解。

选项二

这个选项比较复杂。我将为您简要介绍工作流程,然后更详细地解释各部分或提供参考以帮助您入门!

  1. [编辑器脚本] 从您的 AnimatorController 生成一个包含所有状态的脚本。 (可能是 "hardest" 部分)
  2. 使用生成的脚本在您的下拉列表中填写状态
  3. 发挥你的状态:)

第 1 部分: 使用编辑器脚本 extract all the States from your AnimatorController。 link 应该会提示您如何实现此目的。简短(未测试)示例代码:

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);
}

请记住:使用的 AnimatorControllerUnityEditor.Animations 命名空间内。这可以是 EditorScript 中的 public 字段,以便您轻松访问。

第 2 部分: 您的脚本文件应该创建一个像这样的 class:

public class AnimatorControllerStates
{
    public List<string> states = new List<string>
    {
        "Idle",
        "ComplexRotate 0",
        "Autoplay",
        ...
    }
}

命名 class 以某种方式与您为其创建的 AnimatorController 相关。并将此 class 设为 运行 时间脚本中的一个字段。然后你可以像这样访问你的所有状态:

myAnimatorControllerStates.states

这个数组可以填充您的下拉列表。您将保存下拉列表的索引,然后可以按索引播放状态。

第 3 部分:现在只需在脚本中播放您的状态就真的很容易了:

string state = myAnimatorControllerStates.states[mySelectedIndex];
myAnimatorController.Play(mySelectedIndex);

我不知道您项目的范围,但为了拥有一个可维护的应用程序,我更愿意随时选择选项二。