最近在開發項目時,需要處理大量的動畫,於是就網上查找資料,然后寫了這么編輯器工具:
就是在模型導入時,根據配置文件自動切割動畫。
首先我們需要封裝兩個類:一個模型類和一個動畫類
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>