美术做一个模型,附带很多动画,然后就创建和管理动画状态机,然后类似的模型可能有很多,也就是我们所理解的皮肤,低级的是换贴图,高级一点的换模型,如果模型比较多的话,美术要创建和管理很多相同的动画,重复性的劳动,这会就需要有个动画生成器了。
我们可以根据美术的要求通过代码创建一个AnimatorController,但一旦美术修改什么需求我们就要跟着修改会比较麻烦,比较简便的是美术先创建一个动画控制器模板,然后接下来重复性的劳动我们就通过程序工具来解决。思路是copy模板,修改模板里面动画状态的Motion,指向当前模型的动画clip,Motion有的是简单的动画clip,这个直接替换没啥好说的,有的是混合树BlendTree,这个替换会碰到小坑,如果我们获取到混合树的Motion然后遍历里面的Children,替换每个child的Motion是替换不了的,这个我也是查看了BlendTree的源码才知道,源码里面Children的管理(增加和删除)都是通过覆盖的方式替换Children数组,但我也有通过替换Children数组的方式来实现还是没有替换成功,最后还是通过代码创建BlendTree的方式来替换BlendTree才成功了。
登录后复制
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using System;
using System.IO;
using System.Windows.Forms;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
public class AnimatorGeneratorWindow : OdinEditorWindow
{
string fileDirectory;
bool recursion = false;//是否是递归模式
[UnityEditor.MenuItem("Tools/AnimatorGenerator")]
private static void Open()
{
var window = GetWindow<AnimatorGeneratorWindow>();
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(450, 500);
}
[InlineEditor(InlineEditorModes.LargePreview)] //对材质和mesh有效,可以预览
[LabelText("动画模板")]
public AnimatorController AnimatorControllerTemplate;
[Button("选择要生成的目录(递归遍历)", ButtonSizes.Medium), GUIColor(1, 1, 0)]
[HorizontalGroup("ChooseMenu")]
[EnableIf("AnimatorControllerTemplate", InfoMessageType.Error)]
private void ChooseMenus()
{
Debug.Log("选择目录");
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.RootFolder = Environment.SpecialFolder.MyComputer;
if (fbd.ShowDialog() == DialogResult.OK)
{
fileDirectory = fbd.SelectedPath;
recursion = true;
}
Debug.Log("选择目录:" + fileDirectory);
}
[Button("选择要生成的目录(单文件目录)", ButtonSizes.Medium), GUIColor(1, 1, 0)]
[HorizontalGroup("ChooseMenu")]
[EnableIf("AnimatorControllerTemplate", InfoMessageType.Error)]
private void ChooseMenu()
{
Debug.Log("选择目录");
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.RootFolder = Environment.SpecialFolder.MyComputer;
if (fbd.ShowDialog() == DialogResult.OK)
{
fileDirectory = fbd.SelectedPath;
recursion = false;
}
Debug.Log("选择目录:" + fileDirectory);
}
[Button("生成动画控制器", ButtonSizes.Medium), GUIColor(0, 1, 1)]
[HorizontalGroup("Buttons")]
[EnableIf("AnimatorControllerTemplate", InfoMessageType.Error)]
private void GeneratorAnimatorButton()
{
Debug.Log("生成动画控制器");
CreateAnimatorAssets();
}
private void CreateAnimatorAssets()
{
if (!Directory.Exists(fileDirectory))
{
throw new Exception("目录不存在或者路径不存在");
}
if (AnimatorControllerTemplate == null)
{
Debug.LogError("没有选择动画模板");
return;
}
var animatorFilePath = AssetDatabase.GetAssetPath(AnimatorControllerTemplate);
var dirArray = fileDirectory.Split('\\');
var pathLastDirectoryName = dirArray[dirArray.Length - 1];
var animatorExtension = Path.GetExtension(animatorFilePath);
if (recursion)
{
var folders = Directory.GetDirectories(fileDirectory);
foreach (var folder in folders)
{
SingleFolderDispose(folder, animatorFilePath);
}
}
else
{
SingleFolderDispose(fileDirectory, animatorFilePath);
}
}
private void SingleFolderDispose(string folder, string animatorFilePath)
{
DirectoryInfo info = new DirectoryInfo(folder);
string folderName = info.Name;
var newAnimatorFilePath = Path.Combine(folder, folderName + Path.GetExtension(animatorFilePath));
File.Copy(animatorFilePath, newAnimatorFilePath, true);
AssetDatabase.Refresh();
AnalyzeAnimController(folder, newAnimatorFilePath);
AssetDatabase.Refresh();
var obj = LoadFbx(folder, newAnimatorFilePath);
PrefabUtility.SaveAsPrefabAsset(obj, string.Format("{0}/{1}.prefab", folder, folderName));
DestroyImmediate(obj);
}
private GameObject LoadFbx(string folder, string animatorFilePath)
{
//找到fbx,找到当前目录下名字不带@符号的fbx 进行实例化
FileInfo tempFile = null;
DirectoryInfo folderDirectoryInfo = new DirectoryInfo(folder);
var files = folderDirectoryInfo.GetFiles();
foreach (var fileInfo in files)
{
if (!fileInfo.Name.Contains("@") && fileInfo.Name.Contains(".fbx") && !fileInfo.Name.Contains(".meta")) //TODO:也可以根据floder名去找对应的fbx
{
tempFile = fileInfo;
break;
}
}
if (tempFile == null)
{
throw new Exception(string.Format("目录:{0} 没有找到不带@的fbx", folder));
}
var obj = Instantiate(AssetDatabase.LoadAssetAtPath<GameObject>(GetAssetPath(tempFile.FullName))) as GameObject;
//找到controller
var controller = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(GetAssetPath(animatorFilePath));
obj.GetComponent<Animator>().runtimeAnimatorController = controller;
return obj;
}
private void AnalyzeAnimController(string floder, string controllerPath)
{
var assetPath = GetAssetPath(controllerPath);
var animatorController = AssetDatabase.LoadAssetAtPath<AnimatorController>(assetPath);
string animationFolder = Path.GetDirectoryName(assetPath) + "\\animations";
animationFolder.Replace("\\", "/");
//animatorController的Parameters不需要修改
//遍历所有的layer
for (int i = 0; i < animatorController.layers.Length; i++)
{
var layer = animatorController.layers[i];
AnimatorStateMachine sm = layer.stateMachine;
RecursionAnalyzeAnimatorStateMachine(sm, animationFolder);
}
}
private string GetAssetPath(string fullPath)
{
var strs = fullPath.Split(new string[] { "Assets" }, StringSplitOptions.None);
var assetPath = "Assets" + strs[strs.Length - 1];
assetPath.Replace("\\", "/");
return assetPath;
}
private void RecursionAnalyzeAnimatorStateMachine(AnimatorStateMachine stateMachine, string animationFlolder)
{
//遍历states
for (int i = 0; i < stateMachine.states.Length; i++)
{
var animatorState = stateMachine.states[i];
var motion = animatorState.state.motion;
if (motion != null)
{
if (motion is BlendTree)
{
BlendTree bt = motion as BlendTree;
ChildMotion[] childMotions = new ChildMotion[bt.children.Length];
for (int j = 0; j < bt.children.Length; j++)
{
var childMotion = bt.children[j];
var motionClip = GetAnimationClip(childMotion.motion.name, animationFlolder);
if (motionClip == null)
{
Debug.LogError("没有找到" + motion.name + "的动画控制器");
}
else
{
Debug.Log(string.Format("Name:{0} Motion:{1}", animatorState.state.name, childMotion.motion)); //根据名字找到对应的prefab 然后找出里面的动画文件加载
//childMotion.motion = (Motion)motionClip;
//var newChildMotion = new ChildMotion() { motion = motionClip, cycleOffset = childMotion.cycleOffset, mirror = childMotion.mirror, directBlendParameter = childMotion.directBlendParameter, position = childMotion.position, threshold = childMotion.threshold, timeScale = childMotion.timeScale };
//childMotion = newChildMotion;
childMotions[j] = new ChildMotion() { motion = (Motion)motionClip, cycleOffset = childMotion.cycleOffset, mirror = childMotion.mirror, directBlendParameter = childMotion.directBlendParameter, position = childMotion.position, threshold = childMotion.threshold, timeScale = childMotion.timeScale };
}
}
//bt.children = childMotions;
BlendTree newBt = new BlendTree()
{
blendParameter = bt.blendParameter,
blendParameterY = bt.blendParameterY,
blendType = bt.blendType,
hideFlags = bt.hideFlags,
maxThreshold = bt.maxThreshold,
minThreshold = bt.minThreshold,
name = bt.name,
useAutomaticThresholds = bt.useAutomaticThresholds,
children = childMotions,
};
animatorState.state.motion = newBt;
}
else
{
animatorState.state.motion = null;
var motionClip = GetAnimationClip(motion.name, animationFlolder);
if (motionClip == null)
{
Debug.LogError("没有找到" + motion.name + "的动画控制器");
}
else
{
animatorState.state.motion = (Motion)motionClip;
Debug.Log(string.Format("Name:{0} Motion:{1}", animatorState.state.name, motion));
}
}
}
}
//遍历substatemachine
for (int j = 0; j < stateMachine.stateMachines.Length; j++)
{
var stateMachines = stateMachine.stateMachines[j];
RecursionAnalyzeAnimatorStateMachine(stateMachines.stateMachine, animationFlolder);
}
}
private AnimationClip GetAnimationClip(string motionName, string animationFolder)
{
var motionNameExt = motionName.Substring(motionName.IndexOf("_"));
DirectoryInfo directoryInfo = new DirectoryInfo(animationFolder);
FileInfo tempFileInfo = null;
var files = directoryInfo.GetFiles("*.FBX", SearchOption.AllDirectories);
for (int i = 0; i < files.Length; i++)
{
if (files[i].Name.EndsWith(motionNameExt + ".FBX")) //有可能是Robert01_gun_jump_start 对应的Robert01@Robert01_gun_jump
{
tempFileInfo = files[i];
break;
}
}
if (tempFileInfo != null)
{
var datas = AssetDatabase.LoadAllAssetsAtPath(GetAssetPath(tempFileInfo.FullName));
if (datas.Length == 0)
{
Debug.Log(string.Format("Can't find clip in {0}", tempFileInfo.FullName));
return null;
}
foreach (var data in datas)
{
if (!(data is AnimationClip))//如果不是动画文件则跳过
continue;
var newClip = data as AnimationClip;
return newClip;
}
}
else
{
Debug.LogError("没有找到对应的动画FBX:" + motionName);
}
return null;
}
}
OnGUI的模式是采用的Selection方式选择模板和目录。
登录后复制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Windows.Forms;
using System;
using System.IO;
using UnityEditor.Animations;
public class AnimatorEditor : EditorWindow
{
string fileDirectory = string.Empty;
string assetDirectory = string.Empty;
bool recursion = false; //是否是递归模式
AnimatorController animatorControllerTemplate = null;
[UnityEditor.MenuItem("Tools/AnimatorEditor")]
private static void Open()
{
var window = EditorWindow.GetWindow(typeof(AnimatorEditor), true, "动画生成器", true);
window.Show();
}
void OnSelectionChange()
{
if (Selection.activeObject != null)
{
Debug.Log("选择物体:" + Selection.activeObject);
if (Selection.activeObject is AnimatorController)
{
animatorControllerTemplate = Selection.activeObject as AnimatorController;
Debug.Log("选择的物体是:" + animatorControllerTemplate.name);
}
if (Selection.activeObject is DefaultAsset) //选择目录
{
var asset = Selection.activeObject as DefaultAsset;
string[] strs = Selection.assetGUIDs;
string path = AssetDatabase.GUIDToAssetPath(strs[0]);
assetDirectory = path;
fileDirectory = Path.Combine(Environment.CurrentDirectory, path);
Debug.Log("选择的路径:" + path);
}
}
}
void OnGUI()
{
GUILayout.BeginHorizontal();
GUILayout.Label("选择Controller模板:");
GUILayout.Label(animatorControllerTemplate == null ? "" : animatorControllerTemplate.name);
GUILayout.EndHorizontal();
GUILayout.Space(5);
GUILayout.BeginHorizontal();
GUILayout.Label("选择生成的目录:");
GUILayout.Label(assetDirectory);
GUILayout.EndHorizontal();
GUILayout.Space(5);
GUILayout.BeginHorizontal();
GUILayout.Label("是否批量生成:");
recursion = EditorGUILayout.Toggle(recursion);
GUILayout.EndHorizontal();
GUILayout.Space(30);
GUILayout.BeginHorizontal();
if (GUILayout.Button("选择要生成的目录(递归遍历)"))
{
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.RootFolder = Environment.SpecialFolder.MyComputer;
if (fbd.ShowDialog() == DialogResult.OK)
{
fileDirectory = fbd.SelectedPath;
recursion = true;
}
Debug.Log("选择目录:" + fileDirectory);
}
if (GUILayout.Button("选择要生成的目录(单文件目录)"))
{
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.RootFolder = Environment.SpecialFolder.MyComputer;
if (fbd.ShowDialog() == DialogResult.OK)
{
fileDirectory = fbd.SelectedPath;
recursion = false;
}
Debug.Log("选择目录:" + fileDirectory);
}
GUILayout.EndHorizontal();
GUILayout.Space(30);
if (GUILayout.Button("生成AnimatorController"))
{
Debug.Log("生成动画控制器");
CreateAnimatorAssets();
}
}
private void CreateAnimatorAssets()
{
if (!Directory.Exists(fileDirectory))
{
throw new Exception("目录不存在或者路径不存在");
}
if (animatorControllerTemplate == null)
{
Debug.LogError("没有选择动画模板");
return;
}
var animatorFilePath = AssetDatabase.GetAssetPath(animatorControllerTemplate);
var dirArray = fileDirectory.Split('\\');
var pathLastDirectoryName = dirArray[dirArray.Length - 1];
var animatorExtension = Path.GetExtension(animatorFilePath);
if (recursion)
{
var folders = Directory.GetDirectories(fileDirectory);
foreach (var folder in folders)
{
SingleFolderDispose(folder, animatorFilePath);
}
}
else
{
SingleFolderDispose(fileDirectory, animatorFilePath);
}
}
private void SingleFolderDispose(string folder, string animatorFilePath)
{
DirectoryInfo info = new DirectoryInfo(folder);
string folderName = info.Name;
var newAnimatorFilePath = Path.Combine(folder, folderName + Path.GetExtension(animatorFilePath));
File.Copy(animatorFilePath, newAnimatorFilePath, true);
AssetDatabase.Refresh();
AnalyzeAnimController(folder, newAnimatorFilePath);
AssetDatabase.Refresh();
var obj = LoadFbx(folder, newAnimatorFilePath);
PrefabUtility.SaveAsPrefabAsset(obj, string.Format("{0}/{1}.prefab", folder, folderName));
DestroyImmediate(obj);
}
private GameObject LoadFbx(string folder, string animatorFilePath)
{
//找到fbx,找到当前目录下名字不带@符号的fbx 进行实例化
FileInfo tempFile = null;
DirectoryInfo folderDirectoryInfo = new DirectoryInfo(folder);
var files = folderDirectoryInfo.GetFiles();
foreach (var fileInfo in files)
{
if (!fileInfo.Name.Contains("@") && fileInfo.Name.Contains(".fbx") && !fileInfo.Name.Contains(".meta")) //TODO:也可以根据floder名去找对应的fbx
{
tempFile = fileInfo;
break;
}
}
if (tempFile == null)
{
throw new Exception(string.Format("目录:{0} 没有找到不带@的fbx", folder));
}
var obj = Instantiate(AssetDatabase.LoadAssetAtPath<GameObject>(GetAssetPath(tempFile.FullName))) as GameObject;
//找到controller
var controller = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(GetAssetPath(animatorFilePath));
obj.GetComponent<Animator>().runtimeAnimatorController = controller;
return obj;
}
private void AnalyzeAnimController(string floder, string controllerPath)
{
var assetPath = GetAssetPath(controllerPath);
var animatorController = AssetDatabase.LoadAssetAtPath<AnimatorController>(assetPath);
string animationFolder = Path.GetDirectoryName(assetPath) + "\\animations";
animationFolder.Replace("\\", "/");
//animatorController的Parameters不需要修改
//遍历所有的layer
for (int i = 0; i < animatorController.layers.Length; i++)
{
var layer = animatorController.layers[i];
AnimatorStateMachine sm = layer.stateMachine;
RecursionAnalyzeAnimatorStateMachine(sm, animationFolder);
}
}
private string GetAssetPath(string fullPath)
{
var strs = fullPath.Split(new string[] { "Assets" }, StringSplitOptions.None);
var assetPath = "Assets" + strs[strs.Length - 1];
assetPath.Replace("\\", "/");
return assetPath;
}
private void RecursionAnalyzeAnimatorStateMachine(AnimatorStateMachine stateMachine, string animationFlolder)
{
//遍历states
for (int i = 0; i < stateMachine.states.Length; i++)
{
var animatorState = stateMachine.states[i];
var motion = animatorState.state.motion;
if (motion != null)
{
if (motion is BlendTree)
{
BlendTree bt = motion as BlendTree;
ChildMotion[] childMotions = new ChildMotion[bt.children.Length];
for (int j = 0; j < bt.children.Length; j++)
{
var childMotion = bt.children[j];
var motionClip = GetAnimationClip(childMotion.motion.name, animationFlolder);
if (motionClip == null)
{
Debug.LogError("没有找到" + motion.name + "的动画控制器");
}
else
{
Debug.Log(string.Format("Name:{0} Motion:{1}", animatorState.state.name, childMotion.motion)); //根据名字找到对应的prefab 然后找出里面的动画文件加载
//childMotion.motion = (Motion)motionClip;
//var newChildMotion = new ChildMotion() { motion = motionClip, cycleOffset = childMotion.cycleOffset, mirror = childMotion.mirror, directBlendParameter = childMotion.directBlendParameter, position = childMotion.position, threshold = childMotion.threshold, timeScale = childMotion.timeScale };
//childMotion = newChildMotion;
childMotions[j] = new ChildMotion() { motion = (Motion)motionClip, cycleOffset = childMotion.cycleOffset, mirror = childMotion.mirror, directBlendParameter = childMotion.directBlendParameter, position = childMotion.position, threshold = childMotion.threshold, timeScale = childMotion.timeScale };
}
}
//bt.children = childMotions;
BlendTree newBt = new BlendTree()
{
blendParameter = bt.blendParameter,
blendParameterY = bt.blendParameterY,
blendType = bt.blendType,
hideFlags = bt.hideFlags,
maxThreshold = bt.maxThreshold,
minThreshold = bt.minThreshold,
name = bt.name,
useAutomaticThresholds = bt.useAutomaticThresholds,
children = childMotions,
};
animatorState.state.motion = newBt;
}
else
{
animatorState.state.motion = null;
var motionClip = GetAnimationClip(motion.name, animationFlolder);
if (motionClip == null)
{
Debug.LogError("没有找到" + motion.name + "的动画控制器");
}
else
{
animatorState.state.motion = (Motion)motionClip;
Debug.Log(string.Format("Name:{0} Motion:{1}", animatorState.state.name, motion));
}
}
}
}
//遍历substatemachine
for (int j = 0; j < stateMachine.stateMachines.Length; j++)
{
var stateMachines = stateMachine.stateMachines[j];
RecursionAnalyzeAnimatorStateMachine(stateMachines.stateMachine, animationFlolder);
}
}
private AnimationClip GetAnimationClip(string motionName, string animationFolder)
{
var motionNameExt = motionName.Substring(motionName.IndexOf("_"));
DirectoryInfo directoryInfo = new DirectoryInfo(animationFolder);
FileInfo tempFileInfo = null;
var files = directoryInfo.GetFiles("*.FBX", SearchOption.AllDirectories);
for (int i = 0; i < files.Length; i++)
{
if (files[i].Name.EndsWith(motionNameExt + ".FBX")) //有可能是Robert01_gun_jump_start 对应的Robert01@Robert01_gun_jump
{
tempFileInfo = files[i];
break;
}
}
if (tempFileInfo != null)
{
var datas = AssetDatabase.LoadAllAssetsAtPath(GetAssetPath(tempFileInfo.FullName));
if (datas.Length == 0)
{
Debug.Log(string.Format("Can't find clip in {0}", tempFileInfo.FullName));
return null;
}
foreach (var data in datas)
{
if (!(data is AnimationClip))//如果不是动画文件则跳过
continue;
var newClip = data as AnimationClip;
return newClip;
}
}
else
{
Debug.LogError("没有找到对应的动画FBX:" + motionName);
}
return null;
}
}
打开弹框可以用Unity内置的System.Windows.Forms.dll的api来打开,将其放在Plugins下,打开方法:
登录后复制
public void OpenFile()
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "exe files (*.exe)|*.exe"; //过滤文件类型
dialog.InitialDirectory = "D:\\"; //定义打开的默认文件夹位置,可以在显示对话框之前设置好各种属性
if (dialog.ShowDialog() == DialogResult.OK)
{
Debug.Log(dialog.FileName);
}
}
登录后复制
using System;
using UnityEngine;
using System.Collections;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Animations;
using System.Collections.Generic;
public class AnimatorTool : MonoBehaviour
{
private static List<AnimatorState> stateList = new List<AnimatorState>();
/// <summary>
/// 菜单方法,遍历文件夹创建Animation Controller
/// </summary>
[MenuItem("Tools/CreateAnimator")]
static void CreateAnimationAssets()
{
string rootFolder = "Assets/Resources/Fbx/";
if (!Directory.Exists(rootFolder))
{
Directory.CreateDirectory(rootFolder);
return;
}
// 遍历目录,查找生成controller文件
var folders = Directory.GetDirectories(rootFolder);
foreach (var folder in folders)
{
DirectoryInfo info = new DirectoryInfo(folder);
string folderName = info.Name;
// 创建animationController文件
AnimatorController aController =
AnimatorController.CreateAnimatorControllerAtPath(string.Format("{0}/animation.controller", folder)); //在对应目录生成AnimatorController文件
//添加参数
aController.AddParameter("run", AnimatorControllerParameterType.Bool);
aController.AddParameter("attack01", AnimatorControllerParameterType.Bool);
// 得到其layer
var layer = aController.layers[0];//Base Layer
// 绑定动画文件
AddStateTranstion(string.Format("{0}/{1}_model.fbx", folder, folderName), layer);
Debug.Log(string.Format("<color=yellow>{0}</color>", layer));
// 创建预设
GameObject go = LoadFbx(folderName);
PrefabUtility.CreatePrefab(string.Format("{0}/{1}.prefab", folder, folderName), go);
DestroyImmediate(go);
}
}
/// <summary>
/// 添加动画状态机状态
/// </summary>
/// <param name="path"></param>
/// <param name="layer"></param>
private static void AddStateTranstion(string path, AnimatorControllerLayer layer)
{
AnimatorStateMachine sm = layer.stateMachine; //状态机
// 根据动画文件读取它的AnimationClip对象
var datas = AssetDatabase.LoadAllAssetsAtPath(path);
if (datas.Length == 0)
{
Debug.Log(string.Format("Can't find clip in {0}", path));
return;
}
/*
//创建默认state
AnimatorState defaultState = sm.AddState("default", new Vector3(300, 0, 0));
//defaultState.motion=
sm.defaultState = defaultState;
AnimatorStateTransition defaultTransition = sm.AddAnyStateTransition(defaultState);
defaultTransition.AddCondition(AnimatorConditionMode.If, 0, "default");
*/
// 先添加一个默认的空状态
var emptyState = sm.AddState("empty", new Vector3(500, 0, 0));
sm.AddAnyStateTransition(emptyState);
//遍历模型中包含的动画片段,将其加入状态机中
foreach (var data in datas)
{
int index = 0;
if (!(data is AnimationClip)) //如果不是动画文件则跳过
continue;
var newClip = data as AnimationClip; //如果是的话则转化
if (newClip.name.StartsWith("__"))
continue;
// 取出动画名字,添加到state里面
AnimatorState state = sm.AddState(newClip.name, new Vector3(500, sm.states.Length * 60, 0)); //将动画添加到动画控制器
stateList.Add(state);
if (state.name == "walk")
{
sm.defaultState = state; //将walk设置为默认动画
}
Debug.Log(string.Format("<color=red>{0}</color>", state));
index++;
state.motion = newClip; //设置动画状态指定到自己的动画文件
// 把State添加在Layer里面
sm.AddAnyStateTransition(state); //将动画状态连线到AnyState
}
AddTransition(sm, "walk", "run", 1);
AddTransition(sm, "run", "walk", 0);
AddTransition(sm, "walk", "attack01", 1);
AddTransition(sm, "attack01", "walk", 0);
AddSuMechie(sm, 2, path, layer, "sub2Machine");
}
static void AddSuMechie(AnimatorStateMachine machine, int index1, string path, AnimatorControllerLayer layer, string sunStateMachine)
{
创建子状态机
//for (int k = 1; k < index1; k++)
//{
// AnimatorStateMachine sub2Machine = machine.AddStateMachine("sub2Machine", new Vector3(100, 300, 0));
//}
AnimatorStateMachine sub2Machine = machine.AddStateMachine(sunStateMachine, new Vector3(100, 300, 0));
// 根据动画文件读取它的AnimationClip对象
var datas = AssetDatabase.LoadAllAssetsAtPath(path);
if (datas.Length == 0)
{
Debug.Log(string.Format("Can't find clip in {0}", path));
return;
}
foreach (var data in datas)
{
int index = 0;
if (!(data is AnimationClip))
continue;
var newClip = data as AnimationClip;
if (newClip.name.StartsWith("__"))
continue;
// 取出动画名字,添加到state里面
AnimatorState state = sub2Machine.AddState(newClip.name, new Vector3(500, sub2Machine.states.Length * 60, 0));
stateList.Add(state);
if (state.name == "walk")
{
sub2Machine.defaultState = state;
}
Debug.Log(string.Format("<color=red>{0}</color>", state));
index++;
state.motion = newClip;
// 把State添加在Layer里面
sub2Machine.AddAnyStateTransition(state);
}
}
/// <summary>
/// 添加状态之间的连线
/// </summary>
/// <param name="stateM">状态</param>
/// <param name="ani_name"></param>
/// <param name="ani_des"></param>
/// <param name="flag"></param>
static void AddTransition(AnimatorStateMachine stateM, string ani_name, string ani_des, int flag)
{
foreach (var item in stateM.states)
{
if (item.state.name == ani_name)
{
foreach (var des in stateM.states)
{
if (des.state.name == ani_des)
{
AnimatorStateTransition transition = item.state.AddTransition(des.state); //添加连线
transition.hasExitTime = true;
transition.exitTime = 0.8f;
if (flag == 1)
transition.AddCondition(AnimatorConditionMode.If, flag, ani_des); //添加连线状态
else
{
transition.AddCondition(AnimatorConditionMode.IfNot, flag, ani_name);
}
}
}
}
}
Resources.UnloadUnusedAssets(); //卸载资源
}
/// <summary>
/// 生成带动画控制器的对象
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static GameObject LoadFbx(string name)
{
var obj = Instantiate(Resources.Load(string.Format("Fbx/{0}/{0}_model", name))) as GameObject;
obj.GetComponent<Animator>().runtimeAnimatorController =
Resources.Load<RuntimeAnimatorController>(string.Format("fbx/{0}/animation", name));
return obj;
}
}
据反馈,新生成的动画控制器在关闭Unity之后,重新打开会发现新生成的Animaor出问题了,motion丢失,但测试下来直接将Clip赋给Motin没有问题,子状态机这种情况也没有问题,唯独BlendTree有问题,我对比新生成的Animator文件跟模板Animator文件对比发现FileID=0,也就是说BlendTree文件并没有保存下来,但BlendTree又不像动画Clip那样我们能直接看到,经过查看Animator文件的数据会发现BlendTree信息写在Animator中,也就是BT并没有保存下来,那么如何保存代码修改的Animation的BlendTree呢?我看到Unity论坛有人碰到类似的问题, https://forum.unity.com/threads/how-to-save-the-animation-blend-tree-created-by-script.480320/,感谢题主!解决方法就是:
经过这段代码,会将BlendTree的信息写入到Animator中,
这样问题就解决了!
https://github.com/dingxiaowei/AnimatorGenerator
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删