Unity 关于GUID的一些实用例子

关于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);这里把所有的文本都写回原文件就行。

  • 12
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值