Unity 的 Animator Controller 在游戏中一般用来做动画状态机,存放一个游戏中人物或物体的各种动画,便于程序调用。
由于很多人物的动画状态机结构基本一致,我们在使用时常常直接复制 Animator Controller 或者复制其中的节点到另外一个 Animator Controller 中。
但实际操作中,这样会产生冗余数据和不必要的引用关系。
问题
如图,在导出名为12100的Animator Controller 时,选择包含引用时,会同时导出名为17001的Animator Controller。
然而实际上打开12100,视图里面并没有17001的animation节点,即使把所有节点都删除再保存,重新导出依然有17001的引用。
查找guid
打开17001的meta文件,找到它的guid,然后在12100的Controller文件中搜索,可以发现一个AnimatorStateTransition关联了这个guid。
关联的内容是:
1 2
| m_DstState: {fileID: 1102131212858548580, guid: df538bb3c58384d4b83ab1cdbb708854, type: 2}
|
改成:
这样再保存,引用关系就解除了。
再次导出就没有多余的引用了。
原因
这个问题产生的原因是复制17001,在此基础上制作了12001,因此12001包含了17001的引用。
上面是大概的原因,下面讲一下冗余依赖产生的具体原因。
假如有两个Animator Controller 文件,分别名为a.controller
和b.controller
。
a.controller
中的状态如下:
可以看到Attack01指向Attack02。
当拷贝了包含Transitions并且该Transitions的Dst State不存在的Animator State到另一个Animator Controller时,就会出现游离依赖数据。以a.controller
为例,查看该文件能够发现Attack01节点包含的Transitions数据:
该Transition的Dst State为Attack02。如果我们拷贝Attack01但没有拷贝Attack02到b.controller
,那么b.controller
中就出现了游离依赖数据m_DstState。
并且这个数据我们在视图中看不到,也删不掉,随着拷贝次数增加,冗余数据会越来越多,甚至会造成循环依赖。如果这两个Controller都是AssetBundle的话,就会产生无限依赖加载。
解决办法
手工
不使用直接复制来制作新的Animator Controller ,只通过工具生成空的Animator Controller 。
在遇到冗余引用时,暂时只能找到荣誉动画的meta,找到guid然后再有冗余的Controller中查找删除。
如果是引用了多余的Animator Controller:
如果是引用了多余的Animation:
脚本
通过之前的游离依赖数据分析可知他们的共性为m_DstState项包含了所依赖的.controller文件的guid,因此我们可以通过读取.controller文件将这些游离依赖数据删除。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| using System.IO; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEditor.Animations;
public class AnimatorChecker { [MenuItem("美术工具/Animator Controller/清除冗余引用数据")] private static void CorrectData() { string block = null; bool isDependOtherAnimatorController = false; AnimatorController animatorController = Selection.activeObject as AnimatorController; string filePathName = Path.GetFullPath(AssetDatabase.GetAssetPath(animatorController)); string tempFilePathName = Application.dataPath + "/" + System.DateTime.Now.Ticks.ToString() + ".controller"; using (StreamWriter writer = File.CreateText(tempFilePathName)) { using (StreamReader reader = File.OpenText(filePathName)) { string content; while (null != (content = reader.ReadLine())) { if (content.StartsWith("--- !u")) { if (!string.IsNullOrEmpty(block)) writer.Write(block);
block = content + System.Environment.NewLine; isDependOtherAnimatorController = false; } else { if (isDependOtherAnimatorController) continue;
if (string.IsNullOrEmpty(block)) writer.WriteLine(content); else { block += (content + System.Environment.NewLine);
if (content.Contains("m_DstState:") && content.Contains("guid")) { block = null; isDependOtherAnimatorController = true; } } } }
if (!string.IsNullOrEmpty(block)) writer.Write(block); } }
FileUtil.ReplaceFile(tempFilePathName, filePathName); AssetDatabase.Refresh(); }
[MenuItem("美术工具/Animator Controller/查找Animator Controller引用")] private static void CollectAnimatorControllerDependencies() { AnimatorController animatorController = Selection.activeObject as AnimatorController; string[] dependencyArray = AssetDatabase.GetDependencies(AssetDatabase.GetAssetPath(animatorController));
Debug.Log("************************* Animator Controller Dependencies (" + animatorController.name + ") *************************"); foreach (string dependency in dependencyArray) { if (dependency.EndsWith(".controller")) Debug.Log(dependency); } Debug.Log("************************************************* End *************************************************"); }
[MenuItem("美术工具/Animator Controller/查找所有Animator Controller引用")] private static void CheckAnimatorControllerDependencies() { List<string> dependencyCheckNameList = new List<string>(); string[] filePathNameArray = Directory.GetFiles(Application.dataPath + "/Content/Animator", "*.controller", SearchOption.TopDirectoryOnly); foreach (string filePathName in filePathNameArray) { string[] dependencyArray = AssetDatabase.GetDependencies(filePathName.Substring(filePathName.IndexOf("/Assets/") + 1)); foreach (string dependency in dependencyArray) { if (dependency.EndsWith(".controller")) { string assetName = Path.GetFileNameWithoutExtension(filePathName); string dependencyName = Path.GetFileNameWithoutExtension(dependency);
string checkName = dependencyName + "_" + assetName; if (dependencyCheckNameList.Contains(checkName)) Debug.Log(Path.GetFileName(filePathName) + " and " + Path.GetFileName(dependency) + " depend each other");
dependencyCheckNameList.Add(assetName + "_" + dependencyName); } } } }
[MenuItem("美术工具/Animator Controller/清除冗余引用数据", true)] [MenuItem("美术工具/Animator Controller/查找Animator Controller引用", true)] private static bool ValidateCorrectData() { return Selection.activeObject is AnimatorController; } }
|
参考
https://www.cnblogs.com/twjcnblog/p/7663048.html