Unity自动切割动画
最近在开发项目时,需要处理大量的动画,于是就网上查找资料,然后写了这么编辑器工具:
就是在模型导入时,根据配置文件自动切割动画。
首先我们需要封装两个类:一个模型类和一个动画类
1 public class ModelFbx 2 { 3 #region -- 变量定义 4 public string modelName; //模型名称 5 public Clip[] clips; //模型所包含的动画 6 #endregion 7 } 8 9 public class Clip 10 { 11 #region -- 变量定义 12 public string clipName; //动画名称 13 public int firstFrame; //动画第一帧 14 public int lastFrame; //动画最后一帧 15 public bool isLoop; //是否循环 16 #endregion 17 18 #region -- 自定义函数 19 /// <summary> 20 /// 构造动画信息 21 /// </summary> 22 /// <param name="_clipName">动画名称</param> 23 /// <param name="_firstFrame">动画第一帧</param> 24 /// <param name="_lastFrame">动画最后一帧</param> 25 /// <param name="_isLoop">动画是否循环</param> 26 public Clip(string _clipName, int _firstFrame, int _lastFrame, bool _isLoop) 27 { 28 clipName = _clipName; 29 firstFrame = _firstFrame; 30 lastFrame = _lastFrame; 31 isLoop = _isLoop; 32 } 33 #endregion 34 35 }
然后我们封装一个类,来读取配置文件并初始化切割信息的类
1 using UnityEngine; 2 using System.Collections.Generic; 3 using System.Xml; 4 using System.IO; 5 6 public static class AnimationClipConfig 7 { 8 #region -- 变量定义 9 public static string ConfigPath = "/_Scripts/Editor/CutAnimation/Config.xml"; 10 private static bool mIsInit = false;//配置信息是否初始化 11 public static List<ModelFbx> modelList = new List<ModelFbx>(); 12 #endregion 13 14 #region -- 自定义函数 15 public static bool Enable 16 { 17 get 18 { 19 string _enable = Read(Application.dataPath + ConfigPath, new string[] { "Root", "Enable" }); 20 return bool.Parse(_enable); 21 } 22 } 23 /// <summary> 24 /// 初始化配置信息 25 /// </summary> 26 /// <returns>返回初始化是否成功</returns> 27 public static bool Init() 28 { 29 if (mIsInit) 30 { 31 return true; 32 } 33 mIsInit = true; 34 35 return InitModeList(); 36 } 37 private static bool InitModeList() 38 { 39 string _path = Application.dataPath + ConfigPath; 40 if (File.Exists(_path)) 41 { 42 XmlDocument _xmlDoc = new XmlDocument(); 43 XmlReaderSettings _set = new XmlReaderSettings(); 44 _set.IgnoreComments = true; 45 XmlReader _reader = XmlReader.Create(_path, _set); 46 _xmlDoc.Load(_reader); 47 _reader.Close(); 48 XmlNodeList _nodeList = _xmlDoc.SelectSingleNode("Root").ChildNodes; 49 foreach (XmlElement _xe in _nodeList) 50 { 51 if (_xe.Name == "ModelFbx") 52 { 53 ModelFbx _modelFbx = new ModelFbx(); 54 foreach (XmlElement _x1 in _xe.ChildNodes) 55 { 56 if (_x1.Name == "ModelName") 57 { 58 _modelFbx.modelName = _x1.InnerText; 59 } 60 if (_x1.Name == "Clips") 61 { 62 _modelFbx.clips = new Clip[_x1.ChildNodes.Count]; 63 int _index = 0; 64 foreach (XmlElement _x2 in _x1.ChildNodes) 65 { 66 string _clipName = ""; 67 int _firstFrame = 0; 68 int _lastFrame = 0; 69 bool _isLoop = false; 70 foreach (XmlElement _x3 in _x2.ChildNodes) 71 { 72 if (_x3.Name == "ClipName") 73 { 74 _clipName = _x3.InnerText; 75 } 76 if (_x3.Name == "FirstFrame") 77 { 78 _firstFrame = int.Parse(_x3.InnerText); 79 } 80 if (_x3.Name == "LastFrame") 81 { 82 _lastFrame = int.Parse(_x3.InnerText); 83 } 84 if (_x3.Name == "IsLoop") 85 { 86 _isLoop = bool.Parse(_x3.InnerText); 87 } 88 } 89 _modelFbx.clips[_index] = new Clip(_clipName, _firstFrame, _lastFrame, _isLoop); 90 _index++; 91 } 92 } 93 } 94 modelList.Add(_modelFbx); 95 } 96 } 97 return true; 98 } 99 else 100 { 101 Debug.LogError("无法找打" + _path + "文件,请检查配置文件路径(ConfigPath)是否正确"); 102 return false; 103 } 104 } 105 106 /// <summary> 107 /// 读取 XML 文件指定子节点数据 108 /// </summary> 109 /// <param name="_path">文件读取路径</param> 110 /// <param name="_xmlNodes">节点数组</param> 111 /// <returns></returns> 112 private static string Read(string _path, string[] _xmlNodes) 113 { 114 string _innerstr = "";//返回值 115 XmlDocument _xmlDoc = new XmlDocument(); 116 XmlReaderSettings _set = new XmlReaderSettings(); 117 _set.IgnoreComments = true; 118 //判断文件是否存在 119 if (File.Exists(_path)) 120 { 121 _xmlDoc.Load(XmlReader.Create(_path, _set)); 122 } 123 else 124 { 125 Debug.LogError("目标文件不存在,请检查路径是否有误"); 126 return null; 127 } 128 int _nodeNum = _xmlNodes.Length; 129 int _counter = 1; 130 XmlNodeList _nodelist = _xmlDoc.SelectSingleNode(_xmlNodes[0]).ChildNodes; 131 if (_nodelist == null) 132 { 133 return _innerstr; 134 } 135 while (_counter < _nodeNum) 136 { 137 bool _checkok = false; 138 foreach (XmlNode _element in _nodelist) 139 { 140 if (_element.Name == _xmlNodes[_counter]) 141 { 142 _checkok = true; 143 _counter++; 144 if (_counter >= _nodeNum) 145 { 146 _innerstr = _element.InnerText; 147 _nodelist = null; 148 break; 149 } 150 _nodelist = _element.ChildNodes; 151 break; 152 } 153 } 154 if (!_checkok) 155 { 156 break; 157 } 158 } 159 return _innerstr; 160 } 161 #endregion 162 163 }
接下来就是切割动画了
using UnityEngine; using UnityEditor; public class CutAnimation : AssetPostprocessor { #region -- 系统函数 /// <summary> /// 模型导入之前调用 /// </summary> public void OnPreprocessModel() { //是否启用动画切割 if (!AnimationClipConfig.Enable) { return; } //当前正在导入的模型 ModelImporter _modelImporter = (ModelImporter)assetImporter; if (AnimationClipConfig.Init()) { foreach (ModelFbx item in AnimationClipConfig.modelList) { //当前导入模型的路径包含AnimationClipConfig.modelList数据表中的模型名字,那就要对这个模型的动画进行切割 if (assetPath.Contains(item.modelName)) { _modelImporter.animationType = ModelImporterAnimationType.Generic; _modelImporter.generateAnimations = ModelImporterGenerateAnimations.GenerateAnimations; ModelImporterClipAnimation[] _animations = new ModelImporterClipAnimation[item.clips.Length]; for (int i = 0; i < item.clips.Length; i++) { _animations[i] = SetClipAnimation(item.clips[i].clipName, item.clips[i].firstFrame, item.clips[i].lastFrame, item.clips[i].isLoop); } _modelImporter.clipAnimations = _animations; } } } else { Debug.LogError("无法找打" + Application.dataPath+AnimationClipConfig.ConfigPath + "文件,请检查配置文件路径(mCofigPath)是否正确"); } } #endregion #region -- 自定义函数 private ModelImporterClipAnimation SetClipAnimation(string _clipName, int _firstFrame, int _lastFrame, bool _isLoop) { ModelImporterClipAnimation _clip = new ModelImporterClipAnimation(); _clip.name = _clipName; _clip.firstFrame =_firstFrame; _clip.lastFrame = _lastFrame; _clip.loop = _isLoop; if (_isLoop) { _clip.wrapMode = WrapMode.Loop; } else { _clip.wrapMode = WrapMode.Default; } return _clip; } #endregion }
感觉也没啥好说的,就直接分享代码了。而且我觉得这个功能比较鸡肋,适用性不是很广,只能在一些特定情况下用到。
下面是我的配置文件和文件结构:
<?xml version="1.0" encoding="utf-8"?> <Root> <Enable>True</Enable><!--是否启用。启用:True;禁用:False--> <ModelFbx><!--模型--> <ModelName>1</ModelName><!--模型名称(string)--> <Clips><!--动画集--> <Clip><!--动画--> <ClipName>Start</ClipName><!--动画名称(string)--> <FirstFrame>0</FirstFrame><!--动画第一帧(int)--> <LastFrame>1</LastFrame><!--动画最后一帧(int)--> <IsLoop>False</IsLoop><!--是否循环(bool)--> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> <ModelFbx> <ModelName>2</ModelName> <Clips> <Clip> <ClipName>Start</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>1</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> <ModelFbx> <ModelName>3</ModelName> <Clips> <Clip> <ClipName>Start</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>1</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> <ModelFbx> <ModelName>4</ModelName> <Clips> <Clip> <ClipName>Start</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>1</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> <ModelFbx> <ModelName>5</ModelName> <Clips> <Clip> <ClipName>Start</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>1</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> <ModelFbx> <ModelName>6</ModelName> <Clips> <Clip> <ClipName>Start</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>1</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> <ModelFbx> <ModelName>7</ModelName> <Clips> <Clip> <ClipName>Start</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>1</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> <ModelFbx> <ModelName>8</ModelName> <Clips> <Clip> <ClipName>Start</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>1</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> <ModelFbx> <ModelName>9</ModelName> <Clips> <Clip> <ClipName>Start</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>1</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> <ModelFbx> <ModelName>10</ModelName> <Clips> <Clip> <ClipName>Start</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>1</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> <ModelFbx> <ModelName>11</ModelName> <Clips> <Clip> <ClipName>Start</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>1</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>Take</ClipName> <FirstFrame>0</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> <Clip> <ClipName>End</ClipName> <FirstFrame>149</FirstFrame> <LastFrame>150</LastFrame> <IsLoop>False</IsLoop> </Clip> </Clips> </ModelFbx> </Root>