Unity编辑器扩展之Project视图扩展

内容将会持续更新,有错误的地方欢迎指正,谢谢!
 

Unity编辑器扩展之Project视图扩展
     
TechX 坚持将创新的科技带给世界!

拥有更好的学习体验 —— 不断努力,不断进步,不断探索
TechX —— 心探索、心进取!

助力快速掌握 Project视图 编辑器扩展

为初学者节省宝贵的学习时间,避免困惑!




一、EditorApplication.projectChanged 之自动更新资源依赖报告


使用 EditorApplication.projectChanged 可以在项目资源发生变化时自动执行一些功能。下面展示如何使用这个事件在Unity项目中实现一个简单但实用的功能:自动更新资源依赖报告。

当项目资源变化时自动生成一份资源依赖报告,列出哪些资源被哪些其他资源依赖。这在进行资源优化或清理时非常有用。

using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.IO;

[InitializeOnLoad]
public static class DependencyReportGenerator
{
    private const string ReportPath = "Assets/DependencyReport.txt";

    static DependencyReportGenerator()
    {
        EditorApplication.projectChanged += OnProjectChanged;
    }

    private static void OnProjectChanged()
    {
        Debug.Log("项目资源发生变化,正在生成依赖报告...");

        Dictionary<string, List<string>> dependencyMap = GenerateDependencys();

        GenerateReport(dependencyMap);

        Debug.Log("依赖报告生成完毕。");
    }

    /// <summary>
    /// 生成依赖项
    /// </summary>
    private static Dictionary<string, List<string>> GenerateDependencys()
    {
        string[] allAssetGuids = AssetDatabase.FindAssets("");

        Dictionary<string, List<string>> dependencyMap = new Dictionary<string, List<string>>();

        foreach (string guid in allAssetGuids)
        {
            string assetPath = AssetDatabase.GUIDToAssetPath(guid);
            string[] dependencies = AssetDatabase.GetDependencies(assetPath, false);

            foreach (string dependency in dependencies)
            {
                if (!dependencyMap.ContainsKey(dependency))
                {
                    dependencyMap[dependency] = new List<string>();
                }

                dependencyMap[dependency].Add(assetPath);
            }
        }

        return dependencyMap;
    }

    /// <summary>
    /// 生成报告
    /// </summary>
    private static void GenerateReport(Dictionary<string, List<string>> dependencyMap)
    {
        using (StreamWriter writer = new StreamWriter(ReportPath))
        {
            foreach (var kvp in dependencyMap)
            {
                writer.WriteLine($"资源:{kvp.Key}");
                writer.WriteLine("被以下资源依赖:");

                foreach (string dependent in kvp.Value)
                {
                    writer.WriteLine($" - {dependent}");
                }

                writer.WriteLine();
            }
        }
    }
}

资源依赖报告列出了每个资源被哪些其他资源依赖,帮助开发者了解资源之间的关系。

生成的资源依赖报告文件存储在 Assets/DependencyReport.txt 中。

在这里插入图片描述



二、EditorApplication.projectWindowItemOnGUI 之标记资源大小


EditorApplication.projectWindowItemOnGUI 是一个非常实用的 API,它允许你在 Unity 的 Project 窗口中自定义显示项目资源的图标、标签或其他信息。通过这个方法,可以实现一些方便开发者查看和管理资源的功能。

我们可以通过 EditorApplication.projectWindowItemOnGUI 实现一个功能:在 Project 窗口中直接显示每个资源的文件大小。这在管理大型项目时非常有用,可以帮助开发者快速识别占用空间较大的资源并进行优化。

using UnityEditor;
using UnityEngine;
using System.IO;

[InitializeOnLoad]
public static class AssetSizeDisplay
{
    static AssetSizeDisplay()
    {
        EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemOnGUI;
    }

    private static void OnProjectWindowItemOnGUI(string guid, Rect selectionRect)
    {
        // 获取资源路径
        string assetPath = AssetDatabase.GUIDToAssetPath(guid);

        // 仅处理文件,不处理文件夹
        if (File.Exists(assetPath))
        {
            // 获取文件大小
            long fileSize = new FileInfo(assetPath).Length;
            string fileSizeStr = GetReadableFileSize(fileSize);

            // 根据文件大小设置颜色
            Color labelColor = GetColorByFileSize(fileSize);

            // 在资源右侧显示文件大小
            Rect sizeRect = new Rect(selectionRect.xMax - 50, selectionRect.y, 75, selectionRect.height);
            EditorGUI.DrawRect(selectionRect, labelColor * 0.2f); // 背景色(较浅)
            EditorGUI.LabelField(sizeRect, fileSizeStr, new GUIStyle(EditorStyles.label) { normal = { textColor = labelColor } });
        }
    }

    /// <summary>
    /// 将文件大小转换为易读的格式
    /// </summary>
    /// <param name="size"></param>
    /// <returns></returns>
    private static string GetReadableFileSize(long size)
    {
        string[] sizes = { "B", "KB", "MB", "GB" };
        int order = 0;
        while (size >= 1024 && order < sizes.Length - 1)
        {
            order++;
            size = size / 1024;
        }
        return $"{size:0.#} {sizes[order]}";
    }

    /// <summary>
    /// 根据文件大小返回不同的颜色
    /// </summary>
    /// <param name="size"></param>
    /// <returns></returns>
    private static Color GetColorByFileSize(long size)
    {
        if (size > 100 * 1024 * 1024) // 大于 100MB
            return Color.red;
        if (size > 10 * 1024 * 1024)  // 大于 10MB
            return new Color(1f, 0.5f, 0f); // 橙色
        if (size > 1 * 1024 * 1024)   // 大于 1MB
            return Color.yellow;

        return Color.green;            // 小于 1MB
    }
}

在 Project 窗口中,每个文件资源的右侧都会显示其文件大小。这样,开发者可以在不打开资源详情的情况下,快速了解各资源的占用空间,从而有助于进行优化和管理。并且根据文件大小用不同颜色标记资源。

在这里插入图片描述



三、UnityEditor.AssetModificationProcessor 之资源修改处理器


UnityEditor.AssetModificationProcessor 是 Unity 编辑器提供的一个抽象类,它允许开发者在编辑器中拦截和处理与资源(Assets)相关的事件。

通过继承和实现这个类,开发者可以编写自定义逻辑来响应资源的创建、保存、移动和删除等操作。此类在需要对资源操作进行管理、限制或自动化时非常有用。

public class AssetEventEditor : UnityEditor.AssetModificationProcessor
{
    //监听"资源即将被创建"事件
    public static void OnWillCreateEdit(string path)
    {
        Debug.LogFormat("创建资源的路径:{0}", path);
    }

    //监听"资源即将被保存"事件
    public static string[] OnWillSaveAssets(string[] paths) {
    
        Debug.LogFormat("保存资源的路径:{0}",string.Join(",",paths));
        return paths;
    }

    //监听"资源即将被移动"事件  DidNotMove表示可以移动,DidMove表示不可以移动
    public static AssetMoveResult OnWillMoveAsset(string oldPath,string newPath) {
      
        Debug.LogFormat("资源从路径{0}移动到路径{1}", oldPath,newPath);
        return AssetMoveResult.DidNotMove;
    }
    
    //监听"资源即将被删除"事件 DidNotDelete表示可以移动,DidDelete表示不可以移动
    public static AssetDeleteResult OnWillDeleteAsset(string assetPath) {
      
        Debug.LogFormat("资源从路径{0}删除", assetPath);
        return AssetDeleteResult.DidNotDelete;
    }

3.1、OnWillCreateAsset 之自动添加资源模板


OnWillCreateAsset 方法在 Unity 中用于拦截和处理即将创建的资源事件。

当一个新的资源(如脚本、预制件、纹理等)被创建时,OnWillCreateAsset 方法会被调用。

这个方法可以让你在资源创建之前执行一些自定义操作。

public static void OnWillCreateAsset(string path)
{
    path = path.Replace(".meta", "");
    if (path.EndsWith(".cs"))
    {
        string fullPath = Path.Combine(Directory.GetCurrentDirectory(), path);
        string fileContent = File.ReadAllText(fullPath);

        // 插入默认命名空间
        fileContent = fileContent.Replace("#SCRIPTNAME#", Path.GetFileNameWithoutExtension(path));

        fileContent = string.Join("\n", fileContent.Split('\n').Select(item => "\t" + item));

        fileContent = "using UnityEngine;\n\nnamespace MyNamespace\n{\n" + fileContent + "\n}".TrimStart('\t');

        File.WriteAllText(fullPath, fileContent);
        AssetDatabase.Refresh();
        Debug.LogFormat("创建脚本时自动添加了命名空间:{0}", path);
    }
}

在这个例子中,OnWillCreateAsset 方法在创建脚本的时候被调用,它被用来为新创建的 C# 脚本文件添加命名空间并格式化代码。这种自动化操作可以提高代码的一致性和开发效率。

在这里插入图片描述


3.2、OnWillSaveAssets 之检查引用丢失


OnWillSaveAssets 在Unity中用于在保存资产(例如场景、预制体)之前执行特定的操作。

这个方法可以帮助你在资产保存之前进行自定义的检查和处理。通过在这个方法中执行适当的逻辑,可以确保资产的质量和一致性,从而提高开发效率并减少潜在问题。

public static string[] OnWillSaveAssets(string[] paths)
    {
        foreach (string path in paths)
        {
            if (path.EndsWith(".prefab") || path.EndsWith(".unity"))
            {
                CheckForMissingReferences(path);
            }
        }
        return paths;
    }

    private static void CheckForMissingReferences(string path)
    {
        // 加载资源对象,处理预制件或场景文件
        Object asset = AssetDatabase.LoadAssetAtPath<Object>(path);

        // 检查场景文件
        if (asset is SceneAsset)
        {
            Scene scene = EditorSceneManager.OpenScene(path, OpenSceneMode.Additive);
            CheckSceneForMissingReferences(scene);
        }
        // 检查预制件
        else if (asset is GameObject go)
        {
            CheckGameObjectForMissingReferences(go, path);
        }
    }

    private static void CheckSceneForMissingReferences(Scene scene)
    {
        foreach (GameObject rootObj in scene.GetRootGameObjects())
        {
            CheckGameObjectForMissingReferences(rootObj, scene.path);
        }
    }

    private static void CheckGameObjectForMissingReferences(GameObject go, string path)
    {
        if (PrefabUtility.IsPartOfAnyPrefab(go) && PrefabUtility.GetCorrespondingObjectFromSource(go) == null)
        {
            Debug.LogError($"预制体引用丢失 对象:{GetGameObjectPath(go)},路径: {path}", go);
        }

        foreach (Component component in go.GetComponentsInChildren<Component>(true))
        {
            if (component == null)
            {
                Debug.LogError($"脚本引用丢失 对象:{GetGameObjectPath(go)},路径: {path}", go);
            }
        }
    }

    private static string GetGameObjectPath(GameObject go)
    {
        if (go.transform.parent == null) return go.name;
      
        return GetGameObjectPath(go.transform.parent.gameObject) + "/" + go.name;
    }

在这个例子中,OnWillSaveAssets方法在在保存场景和预制体时检查是否存在丢失的脚本和预制体引用,并在控制台输出相关警告。

3.3、OnWillMoveAsset 之更新脚本中路径引用


OnWillMoveAsset 在 Unity 中用于当资源(如脚本、预制件等)即将被移动到新路径时调用。

它允许开发者在移动资源前执行一些自定义逻辑,并决定是否允许资源移动。

//监听"资源即将被移动"事件
    public static AssetMoveResult OnWillMoveAsset(string oldPath, string newPath)
{
    UpdateScriptPaths(oldPath, newPath);
    return AssetMoveResult.DidNotMove; // 允许移动
}
private static void UpdateScriptPaths(string oldPath, string newPath)
{
    // 查找所有脚本文件并更新路径
    string[] scripts = AssetDatabase.FindAssets("t:Script");
    foreach (string scriptGUID in scripts)
    {
        string scriptPath = AssetDatabase.GUIDToAssetPath(scriptGUID);
        string scriptContent = File.ReadAllText(scriptPath);
        
        if (scriptContent.Contains(oldPath))
        {
            scriptContent = scriptContent.Replace(oldPath, newPath);
            File.WriteAllText(scriptPath, scriptContent);
            AssetDatabase.Refresh();
            Debug.LogFormat("更新脚本中的资源路径: {0}", scriptPath);
        }
    }
}

当资源移动到新位置时,项目中的脚本可能依赖于该资源的路径。通过在 OnWillMoveAsset 方法中更新脚本中的路径引用,可以确保项目正常运行,避免路径失效导致的错误。

3.4、OnWillDeleteAsset 之删除前确认


OnWillDeleteAsset 在 Unity 中用于在资源(如脚本、预制件、材质等)即将被删除时进行拦截和处理。

通过这个方法,开发者可以在资源被删除前执行自定义逻辑,比如弹出确认对话框、记录日志,甚至阻止资源删除。

public static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions assetOptions)
{
    if (assetPath.StartsWith("Assets/ImportantAssets/"))
    {
        Debug.Log(assetPath);
        if (!EditorUtility.DisplayDialog("警告", $"你确定要删除重要资源 {assetPath} 吗?", "确定", "取消"))
        {
            Debug.LogWarning($"已取消删除重要资源:{assetPath}");
            return AssetDeleteResult.DidDelete; // 阻止删除
        }
    }

    Debug.LogFormat("资源 {0} 被删除", assetPath);
    return AssetDeleteResult.DidNotDelete; // 允许删除
}

通过自定义逻辑,开发者可以确保重要资源不会被轻易删除,并在删除前执行必要的检查和确认。这个方法对于项目管理和资源保护非常有用。

在这里插入图片描述





TechX —— 心探索、心进取!

每一次跌倒都是一次成长

每一次努力都是一次进步


END
感谢您阅读本篇博客!希望这篇内容对您有所帮助。如果您有任何问题或意见,或者想要了解更多关于本主题的信息,欢迎在评论区留言与我交流。我会非常乐意与大家讨论和分享更多有趣的内容。
如果您喜欢本博客,请点赞和分享给更多的朋友,让更多人受益。同时,您也可以关注我的博客,以便及时获取最新的更新和文章。
在未来的写作中,我将继续努力,分享更多有趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐沐森的故事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值