关于GUID的具体理解可以参考这篇文章: https://www.cnblogs.com/zhaoqingqing/p/5823927.html
这里我们就简单理解为表示Unity文件的一个唯一标识符
使用例(代码均为其他人的代码,这里引用):
- 引用查找:
应用场景:项目资源多得一匹,代码冗余复杂很多都没有用,想知道哪些没有用过,哪些还有用的情况下,怎么知道哪些地方用到了这些资源。
既然GUID是标识一个资源文件的唯一标识符,那么我们当然可以去通过这个GUID查找,而GUID能够存在的文件类型一般就只有.prefab(预制体)、.unity(场景)、.mat(材质)、.asset(scriptable资源)文件,所以查找也就只能是从这些文件里面查找(几乎已经是所有Unity里面会用到的地方了,足够了)所以接下来就放一段查找GUID引用的代码:
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Reflection;
public class FindReferences
{
[MenuItem("Assets/查找引用/同时清空控制台", false, 10)]
static private void FindNot()
{
ClearConsole();
Find();
}
[MenuItem("Assets/查找引用/不清空控制台", false, 10)]
static private void Find()
{
EditorSettings.serializationMode = SerializationMode.ForceText;
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (!string.IsNullOrEmpty(path))
{
string guid = AssetDatabase.AssetPathToGUID(path);
List<string> withoutExtensions = new List<string>() { ".prefab", ".unity", ".mat", ".asset" };
string[] files = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories)
.Where(s => withoutExtensions.Contains(Path.GetExtension(s).ToLower())).ToArray();
int startIndex = 0;
EditorApplication.update = delegate ()
{
string file = files[startIndex];
bool isCancel = EditorUtility.DisplayCancelableProgressBar("匹配资源中", file, (float)startIndex / (float)files.Length);
if (Regex.IsMatch(File.ReadAllText(file), guid))
{
Debug.Log(file, AssetDatabase.LoadAssetAtPath<Object>(GetRelativeAssetsPath(file)));
}
startIndex++;
if (isCancel || startIndex >= files.Length)
{
EditorUtility.ClearProgressBar();
EditorApplication.update = null;
startIndex = 0;
Debug.Log("匹配结束");
}
};
}
}
[MenuItem("Assets/查找引用/同时清空控制台", true)]
static private bool VFind()
{
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
return (!string.IsNullOrEmpty(path));
}
static private string GetRelativeAssetsPath(string path)
{
return "Assets" + Path.GetFullPath(path).Replace(Path.GetFullPath(Application.dataPath), "").Replace('\\', '/');
}
static void ClearConsole()
{
Assembly assembly = Assembly.GetAssembly(typeof(SceneView));
System.Type logEntries = assembly.GetType("UnityEditor.LogEntries");
MethodInfo clearConsoleMethod = logEntries.GetMethod("Clear");
clearConsoleMethod.Invoke(new object(), null);
}
}
这里解释一下代码:
EditorSettings.serializationMode = SerializationMode.ForceText; 这段是设置当前的序列化模式,具体的原因我忘了,好像是说不这么设置就无法去找到确实的使用了资源的文件,这个也可以手动在Unity里面设置。
string path = AssetDatabase.GetAssetPath(Selection.activeObject);这一句是获取你选择的物体的路径的,毕竟查找引用肯定要有查找的对象啊。
string guid = AssetDatabase.AssetPathToGUID(path);一看就知道是在拿GUID了,这个应该不难理解。
然后是查找的逻辑,首先查找Application.dataPath(这个不用多解释了,不懂,百度)下的所有文件,排除非".prefab", “.unity”, “.mat”, ".asset"的文件,EditorApplication.update这段你就理解为时Editor自己的update函数就行,它后面那段委托就看做是在Update里面执行的,然后就很简单了,就是去匹配了,File.ReadAllText(file)这里并不是说真的去读file的文本文件(也可以这么说啦),这里看过上面的文章应该能懂,本质上涉及到Unity的序列化格式,反正这里你知道他能够匹配到引用的GUID就行,如果里面包含了目标的GUID,则通过AssetDatabase.LoadAssetAtPath(GetRelativeAssetsPath(file))打印出来,这里也可以直接打印文本,但是那样没办法通过点路径就找到位置,所以用这种方式更好。
可以学习的代码:
EditorSettings.serializationMode = SerializationMode.ForceText; 设置序列化模式
string.IsNullOrEmpty(path) 常用的来判断是否为空的字符串的方式(比你直接判断空或无字符来得快捷安全)
AssetDatabase.GetAssetPath(Selection.activeObject); 获得被选中物体的路径
AssetDatabase.AssetPathToGUID(path); 查找GUID
EditorApplication.update UnityEditor的Update的一个委托函数,可以在不继承自Editor的情况下去应用EditorUpdate(继承了就直接Update了,没这么多事儿)
EditorUtility.DisplayCancelableProgressBar 进度条
AssetDatabase.LoadAssetAtPath 将路径与文件所在位置对应
- 资源替换:
应用场景:资源重复,或者想批量替换某一种UI资源的时候(这个UI资源的引用全是拖上去的时候)就可以采用直接替换GUID的形式去修改(有个我还未验证的简单方式:如果只是替换同名的资源,可以直接把原资源文件删掉,不要删除.meta,然后把新的同名资源放上去就行了,本质上应该就是GUID没有变,只是目标文件的内容变了而已)
原理应该不难理解,就是将已经用到了这个GUID的地方换成另外一个GUID就行,这里就算不贴代码大家应该都能有个思路,这里给一个我从网上一个大神哪里搜到的代码,我忘了链接,所以有知道的人可以在评论里面贴出来让大家都膜拜一下
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Linq;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections.Generic;
/// <summary>
/// 解决项目中 一样的资源(名字或者路径不同)存在两份的问题 (多人做UI出现的问题, 或者美术没有管理好资源)
/// 如果是要替换资源的话, 那就直接替换好了
///
/// 以上可以这么操作的基础是,你的Unity项目内的.prefab .Unity 都可以直接用文本开看到数据,而不是乱码(二进制)。这一步很关键,怎么设置呢?
/// 打开项目Unity编辑器:Edit —-> Project Settings —-> Editor 这样就会调到你的Inspector面板的Editor Settings
/// 设置 Asset Serialization 的Mode类型为:Force Text(默认是Mixed); 这样你就能看到你的prefab文件引用了哪些贴图,字体,prefab 等资源了
/// </summary>
public class ResourcesReplace : EditorWindow
{
private static ResourcesReplace _window;
private Object _sourceOld;
private Object _sourceNew;
private string _oldGuid;
private string _newGuid;
private bool isContainScene = true;
private bool isContainPrefab = true;
private bool isContainMat = true;
private bool isContainAsset = false;
private List<string> withoutExtensions = new List<string>();
[MenuItem("Tools/资源替换")] // 菜单开启并点击的 处理
public static void GUIDRefReplaceWin()
{
// true 表示不能停靠的
_window = (ResourcesReplace)EditorWindow.GetWindow(typeof(ResourcesReplace), true, "引用替换 (●'◡'●)");
_window.Show();
}
void OnGUI()
{
// 要被替换的(需要移除的)
GUILayout.Space(20);
_sourceOld = EditorGUILayout.ObjectField("旧的资源", _sourceOld, typeof(Object), true);
_sourceNew = EditorGUILayout.ObjectField("新的资源", _sourceNew, typeof(Object), true);
// 在那些类型中查找(.unity\.prefab\.mat)
GUILayout.Space(20);
GUILayout.Label("要在哪些类型中查找替换:");
EditorGUILayout.BeginHorizontal();
isContainScene = GUILayout.Toggle(isContainScene, ".unity");
isContainPrefab = GUILayout.Toggle(isContainPrefab, ".prefab");
isContainMat = GUILayout.Toggle(isContainMat, ".mat");
isContainAsset = GUILayout.Toggle(isContainAsset, ".asset");
EditorGUILayout.EndHorizontal();
GUILayout.Space(20);
if (GUILayout.Button("开始替换!"))
{
if (EditorSettings.serializationMode != SerializationMode.ForceText)
{
Debug.LogError("需要设置序列化模式为 SerializationMode.ForceText");
ShowNotification(new GUIContent("需要设置序列化模式为 SerializationMode.ForceText"));
}
else if (_sourceNew == null || _sourceOld == null)
{
Debug.LogError("不能为空!");
ShowNotification(new GUIContent("不能为空!"));
}
else if (_sourceNew.GetType() != _sourceOld.GetType())
{
Debug.LogError("两种资源类型不一致!");
ShowNotification(new GUIContent("两种资源类型不一致!"));
}
else if (!isContainScene && !isContainPrefab && !isContainMat && !isContainAsset)
{
Debug.LogError("要选择一种 查找替换的类型");
ShowNotification(new GUIContent("要选择一种 查找替换的类型"));
}
else // 执行替换逻辑
{
StartReplace();
}
}
}
private void StartReplace()
{
var path = AssetDatabase.GetAssetPath(_sourceOld);
_oldGuid = AssetDatabase.AssetPathToGUID(path);
_newGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(_sourceNew));
Debug.Log("oldGUID = " + _oldGuid + " " + "_newGuid = " + _newGuid);
withoutExtensions = new List<string>();
if (isContainScene)
{
withoutExtensions.Add(".unity");
}
if (isContainPrefab)
{
withoutExtensions.Add(".prefab");
}
if (isContainMat)
{
withoutExtensions.Add(".mat");
}
if (isContainAsset)
{
withoutExtensions.Add(".asset");
}
Find();
}
/// <summary>
/// 查找 并 替换
/// </summary>
private void Find()
{
if (withoutExtensions == null || withoutExtensions.Count == 0)
{
withoutExtensions = new List<string>() { ".prefab", ".unity", ".mat", ".asset" };
}
string[] files = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories)
.Where(s => withoutExtensions.Contains(Path.GetExtension(s).ToLower())).ToArray();
int startIndex = 0;
if (files == null || files.Length == 0)
{
Debug.Log("没有找到 筛选的引用");
return;
}
EditorApplication.update = delegate ()
{
string file = files[startIndex];
bool isCancel = EditorUtility.DisplayCancelableProgressBar("匹配资源中", file, (float)startIndex / (float)files.Length);
var content = File.ReadAllText(file);
if (Regex.IsMatch(content, _oldGuid))
{
Debug.Log("替换了资源的路径:" + file, AssetDatabase.LoadAssetAtPath<Object>(GetRelativeAssetsPath(file)));
content = content.Replace(_oldGuid, _newGuid);
File.WriteAllText(file, content);
}
else
{
Debug.Log("查看了的路径:" + file);
}
startIndex++;
if (isCancel || startIndex >= files.Length)
{
EditorUtility.ClearProgressBar();
EditorApplication.update = null;
startIndex = 0;
AssetDatabase.Refresh();
Debug.Log("替换结束");
}
};
}
private string GetRelativeAssetsPath(string path)
{
return "Assets" + Path.GetFullPath(path).Replace(Path.GetFullPath(Application.dataPath), "").Replace('\\', '/');
}
}
同样,这里解释一下代码:
首先这里是制作了一个Eidtor窗口,涉及到Unity编辑器扩展,不懂的直接百度这个关键字就行,这里不在赘述涉及到的简单代码和逻辑。
主要代码就是Find,其他都是简单的逻辑,不难看懂,其实可以发现很多东西都和第一段代码相同,这里就挑一些不同的地方:content = content.Replace(_oldGuid, _newGuid);这里就是替换了,就如大家可能想象的那样直接replace,但是这只是改了缓存上的文本,我们还要写回去,所以File.WriteAllText(file, content);这里把所有的文本都写回原文件就行。