检索所有 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 中的名称以便更好地理解。
选项二
这个选项比较复杂。我将为您简要介绍工作流程,然后更详细地解释各部分或提供参考以帮助您入门!
- [编辑器脚本] 从您的 AnimatorController 生成一个包含所有状态的脚本。 (可能是 "hardest" 部分)
- 使用生成的脚本在您的下拉列表中填写状态
- 发挥你的状态:)
第 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);
}
请记住:使用的 AnimatorController
在 UnityEditor.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);
我不知道您项目的范围,但为了拥有一个可维护的应用程序,我更愿意随时选择选项二。
我偶然发现了一些我从未想过会成为问题的事情,但是......它确实是。 我正在使用 unity 5.5 编写 visualiazion/simulation 工具包。
使用的模型已经导入,通过编辑器设置动画片段。这是我的动画状态控制器的样子:
例如用户选择了自动播放:状态设置为剪辑自动播放,之后为 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
检索这些状态,因为它仅 returnsAnimationClips
- AnimatorController 可能很难理解,因为状态的命名类似于
AnimationClips
。我不知道你在这里的情况,但最好使用描述性名称然后使用 AnimationClips 中的名称以便更好地理解。
选项二
这个选项比较复杂。我将为您简要介绍工作流程,然后更详细地解释各部分或提供参考以帮助您入门!
- [编辑器脚本] 从您的 AnimatorController 生成一个包含所有状态的脚本。 (可能是 "hardest" 部分)
- 使用生成的脚本在您的下拉列表中填写状态
- 发挥你的状态:)
第 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);
}
请记住:使用的 AnimatorController
在 UnityEditor.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);
我不知道您项目的范围,但为了拥有一个可维护的应用程序,我更愿意随时选择选项二。