问题
想获取状态过渡时的一些参数设置,可以使用 AnimatorController 类。因为 AnimatorController 在编辑器类 UnityEditor.Animations 的命令空间下,运行时不能引用,怎么处理?
解决
因为状态机的这些过渡参数,运行之后基本不会改变,unity也只提供了很有限的 api 修改,因此可以打包前,使用 AnimatorController 将需要的参数读取,并写入文件,供运行时使用。
比如,获取下面从 Part1 -> Part2 的运行时间:
using UnityEditor;
using UnityEditor.Animations;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
public class MeteorHammerSkillStateProcessor: Editor, IPreprocessBuildWithReport
{
public int callbackOrder { get; }
[MenuItem("AnimatorUtil/OutputMeteorSkillTime")]
public static void WriteSkillClipTime()
{
var controller = AssetDatabase.LoadAssetAtPath<AnimatorController>("Assets/#C2_Animations/Play/Configure/Robot.controller");
var _meteorSkillAnimatorStatePart1 = GetAnimationState(controller, "MeteorHammerSkill_Part1");
var _meteorSkillAnimatorStatePart2 = GetAnimationState(controller, "MeteorHammerSkill_Part2");
var _meteorSkillAnimatorStatePart3 = GetAnimationState(controller, "MeteorHammerSkill_Part3");
var _meteorSkillAnimatorStatePart4 = GetAnimationState(controller, "MeteorHammerSkill_Part4");
float time = 0f;
if (_meteorSkillAnimatorStatePart1.speed <= 0
|| _meteorSkillAnimatorStatePart2.speed <= 0
|| _meteorSkillAnimatorStatePart3.speed <= 0
|| _meteorSkillAnimatorStatePart4.speed <= 0)
{
return;
}
// TODO : 这里未考虑到 animator.speed 的值(默认 state的speed 运行时也不改变),如果运行时,animator.speed != 1, 需要将
// 获取的 clipTime 再除以 animator.speed
float clip1Time = GetClip("MeteorHammerSkill_Part1", controller).length / _meteorSkillAnimatorStatePart1.speed
* (_meteorSkillAnimatorStatePart1.transitions[0].exitTime - 0);
float clip2Time = GetClip("MeteorHammerSkill_Part2", controller).length / _meteorSkillAnimatorStatePart2.speed
* (_meteorSkillAnimatorStatePart2.transitions[0].exitTime - _meteorSkillAnimatorStatePart1.transitions[0].offset);
float clip3Time = GetClip("MeteorHammerSkill_Part3", controller).length / _meteorSkillAnimatorStatePart3.speed
* (_meteorSkillAnimatorStatePart3.transitions[0].exitTime - _meteorSkillAnimatorStatePart2.transitions[0].offset);
float clip4Time = GetClip("MeteorHammerSkill_Part4", controller).length / _meteorSkillAnimatorStatePart4.speed
* (_meteorSkillAnimatorStatePart4.transitions[0].exitTime - _meteorSkillAnimatorStatePart3.transitions[0].offset);
Debug.Log($"{clip1Time}, {clip2Time}, {clip3Time}, {clip4Time}, total {clip1Time + clip2Time + clip3Time + clip4Time}");
}
public void OnPreprocessBuild(BuildReport report)
{
// AnimatorController 在运行时不可引用。可以在打包前,将需要的参数写入 ScriptableObject 中,在运行时读取。
#if WRITE_METEOR_SKILL_TIME
WriteSkillClipTime();
#endif
}
public static AnimationClip GetClip(string clip, AnimatorController controller)
{
AnimationClip[] tAnimationClips = controller.animationClips;
if (null == tAnimationClips || tAnimationClips.Length <= 0) return null;
AnimationClip tAnimationClip;
for (int tCounter = 0, tLen = tAnimationClips.Length; tCounter < tLen; tCounter++)
{
tAnimationClip = controller.animationClips[tCounter];
if (null != tAnimationClip && tAnimationClip.name == clip)
return tAnimationClip;
}
return null;
}
public static AnimatorState FindAnimatorState(AnimatorStateMachine stateMachine, string stateName)
{
for (int i = 0; i < stateMachine.states.Length; i++)
{
if (stateMachine.states[i].state.name.Equals(stateName))
{
return stateMachine.states[i].state;
}
}
for (int i = 0; i < stateMachine.stateMachines.Length; i++)
{
var child = stateMachine.stateMachines[i];
var state = FindAnimatorState(child.stateMachine, stateName);
if (state)
{
return state;
}
}
return null;
}
public static AnimatorState GetAnimationState(AnimatorController controller, string stateName)
{
AnimatorStateMachine stateMachine = controller.layers[0].stateMachine;
return FindAnimatorState(stateMachine, stateName);
}
}