前言
开始工作一段时间后,发现有时经常会出现删了脚本缺漏删预制体上的引用,导致项目内出现了含有missing脚本的预制体,造成了一些影响,因此就做了一个小功能,一键清除预制体上的minssing脚本
整理与尝试过后的思路:
拿到所有的预制体进行遍历
将预制体生成至场景中
将预制体序列化得到身上的component序列化信息 (尝试过正常的销毁组件方法不可行,至于原因,还在探寻)
遍历检查,移除为空的component对应的序列化信息
保存修改过的序列化属性 (这个很重要,一定要记得保存)
将修改后的物体覆盖替换原预制体
最后记得删除场景中生成的物体 (否则,如果你的项目够大,你会看到很美妙的场景)
接下来是正题内容
第一步: 拿到所有的预制体进行遍历:这个很简单,有两种方法
通过IO去读取项目文件夹内的信息
Directory是IO中关于目录操作的静态类
GetFiles方法参数:(搜索起始路径,匹配内容,SearchOption枚举有两个类型:只搜索顶级目录和搜索全目录)
备注:通过这个方法得到的路径是绝对路径,在下面使用时要转换成相对路径,可直接使用这个方法转换: filePaths.Replace(Application.dataPath, “Assets”)
string[] filePaths =
Directory.GetFiles(Application.dataPath, "*.prefab", SearchOption.AllDirectories);
通过Unity的编辑器管理类AssetDatabase
要注意,AssetDataBase.FindAssets方法返回的是一串guid,因此还需要使用GUIDToAssetPath得到对应的路径
备注:这里的路径就是相对路径,可以直接使用
string[] fileGuids = AssetDatabase.FindAssets("t:Prefab");
for(int i = 0 ; i < fileGuids .Length ; i++)
{
string path = AssetDatabase.GUIDToAssetPath(fileGuids[i]);
//后续操作
//....
}
之后用AssetDatabase.LoadAssetAtPath(path);方法拿到预制体的引用就可以啦。
第二步: 将预制生成至场景中
以下两种方法都可以实现所需效果
他们的区别在于PrefabUtility生成的物体会与预制体保持关联,而GameObject不会。后者一般用于运行时
PrefabUtility.InstantiatePrefab(prefab);
GameObject.Instantiate(prefab);
第三步: 序列化生成的物体得到component的序列化信息数组 并进行判空删除
遍历物体上的Component,当遇到空Component时,删除对应下标的序列化信息
SerializedObject .FindProperty 这个实例方法返回的是一个序列化数据中的某个数据的引用
SerializedObject.DeleteArrayElementAtIndex 这个就是根据下标删除序列化属性中对应内容的实例方法
注意:每次删除的下标要减去一个已删除个数的偏移,因为删除序列化数组的某个信息后,后面的会自动前移,他们的下标就都会减小
提示:删除的时候加个Log信息打印处理的内容,对使用者更友好
var components = obj.GetComponents<Component>();
SerializedObject so=new SerializedObject(obj);
var soProperties = so.FindProperty("m_Component");
int r = 0;
for (int j = 0; j < components.Length; j++)
{
if (components[j] == null)
{
soProperties.DeleteArrayElementAtIndex(j-r);
Debug.LogError("清除了物体:"+obj.name +" 的一个missing脚本");
r++;
}
}
第四步: 保存修改后的序列化信息,覆盖原预制体,删除场景中的物体
SerializedObject .ApplyModifiedProperties()这个方法必须记得使用,他会将修改后的序列化数据保存回原物体中
在Unity2018.4版本以前并没有PrefabUtility.SaveAsPrefabAssetAndConnect 而是用PrefabUtility.ReplacePrefab 来实现类似的功能
so.ApplyModifiedProperties();
PrefabUtility.SaveAsPrefabAssetAndConnect(obj, path, InteractionMode.AutomatedAction);
1
2
最后保存完毕记得删除场景上的物体
以下是完整代码
[MenuItem("Test/CleanMissingScript")]
public static void Begin()
{
string[] filePaths = Directory.GetFiles(Application.dataPath, "*.prefab", SearchOption.AllDirectories);
int sum = 0;
for (int i = 0; i < filePaths.Length; i++)
{
string path = filePaths[i].Replace(Application.dataPath, "Assets");
GameObject objPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
GameObject obj = PrefabUtility.InstantiatePrefab(objPrefab) as GameObject;
//判断是否存在于Hierarchy面板上
if (obj.hideFlags == HideFlags.None)
{
var components = obj.GetComponents<Component>();
SerializedObject so=new SerializedObject(obj);
var soProperties = so.FindProperty("m_Component");
int r = 0;
for (int j = 0; j < components.Length; j++)
{
if (components[j] == null)
{
soProperties.DeleteArrayElementAtIndex(j-r);
Debug.LogError("清除了物体:"+obj.name +" 的一个missing脚本");
r++;
}
}
if (r > 0)
{
so.ApplyModifiedProperties();
PrefabUtility.SaveAsPrefabAssetAndConnect(obj, path, InteractionMode.AutomatedAction);
AssetDatabase.Refresh();
}
sum += r;
UnityEngine.Object.DestroyImmediate(obj);
}
}
Debug.LogError("清除完成,清理个数:"+sum);
}
最后
在最新的2019.2.3版本中,DeleteArrayElementAtIndex方法删除序列化数据似乎出现了问题,无论如何都不允许删除数据,会给出错误提醒。2018版本是没有问题的
可能是2019版本哪里发现了变动,我需要做对应操作才能解锁使允许删除数据,我还在琢磨
可能是2019版本这部分出现了bug?我觉得这个可能性比较小
最后要是有知道的好心人士希望可以告知一下,谢过~
————————————————
版权声明:本文为CSDN博主「小温小温美梦成真」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35207836/article/details/100165414