Unity使用Odin插件创建自定义窗口

游戏开发过程中我们经常要写一些小工具来提升我们的开发效率,最近做了一个基于Odin插件的Prefab批处理小工具,现在跟大家分享一下开发过程。首先看一下成品图。

打开这个窗口的时候可以把Prefab拖动到Prefabs里面,需要处理的时候点击开始批处理即可。

列表的内容会保存到一个json文件里,以便下次打开的时候还原。修改过的资源会记录MD5,如果资源没改动,批处理的时候会自动跳过该资源。

因为处理后会产生新的加后缀的Prefab,所以可以加个开关控制是否使用新资源,在窗口关闭的时候会使用字符串匹配来修改代码进行保存。

代码如下:

using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;

public class PrefabCollectorWindow : OdinEditorWindow
{
    [MenuItem("Tools/Prefab Collector")]
    public static void ShowWindow()
    {
        var window = GetWindow<PrefabCollectorWindow>();
        window.position = GUIHelper.GetEditorWindowRect().AlignCenter(600, 800);
        window.titleContent = new GUIContent("Prefab Collector");
        window.Initialize();
    }
    
    private JObject ReadSettings()
    {
        string configPath = string.Concat(Application.dataPath, "/Configs/PrefabCollectorConfig.json");
        if (!File.Exists(configPath))
        {
            Debug.LogError("Prefab Collector Config File Missing!");
            return null;
        }
        using (StreamReader file = File.OpenText(configPath))
        {
            using (JsonTextReader reader = new JsonTextReader(file))
            {
                JObject o = (JObject)JToken.ReadFrom(reader);
                return o;
            }
        }
    }
    
    private void WriteSettings(JObject settings)
    {
        string configPath = string.Concat(Application.dataPath, "/Configs/PrefabCollectorConfig.json");
        if (!File.Exists(configPath))
        {
            Debug.LogError("Prefab Collector Config File Missing!");
            return;
        }
        File.WriteAllText(configPath, settings.ToString());
    }
    
    private void SaveUseEditedResource()
    {
        var path = Path.Combine(Application.dataPath,"/Configs/PrefabCollector.cs");
        var key = "USE_EDITED_RESOURCE";
        var value = useEditedResource.ToString().ToLower();

        if (File.Exists(path))
        {
            // 读取所有行
            string[] lines = File.ReadAllLines(path);
            bool keyFound = false;
            string pattern = $@"^{key} = (true|false)$";
            Regex regex = new Regex(pattern);

            // 遍历所有行,查找并修改目标行
            for (int i = 0; i < lines.Length; i++)
            {
                if (regex.IsMatch(lines[i]))
                {
                    lines[i] = $"{key} = {value}";
                    keyFound = true;
                    break;
                }
            }

            if (!keyFound)
            {
                Debug.LogWarning("USE_EDITED_RESOURCE not found");
            }
            else
            {
                File.WriteAllLines(path, lines);
            }

            Debug.Log("Configuration updated.");
        }
        else
        {
            Debug.LogWarning("File not found: " + path);
        }
    }
    
    [Title("是否使用修改后的资源", titleAlignment: TitleAlignments.Left)] 
    public bool useEditedResource = false;
    
    [Title("预制体列表", titleAlignment: TitleAlignments.Left)]
    [TableList]
    [ListDrawerSettings(Expanded = true)]
    public List<PrefabEntry> prefabs = new List<PrefabEntry>();

    [Button("开始批处理",ButtonSizes.Large)]
    private void ProcessPrefabs()
    {
        foreach (var entry in prefabs)
        {
            if (entry.Prefab != null)
            {
                var md5 = Hash.FileToMD5(entry.Path);
                if (md5 != entry.Hash)
                {
                    entry.Hash = md5;
                    // 根据自己的需求进行处理
                    AssetDatabase.SaveAssets();
                    AssetDatabase.Refresh();
                }
            }
        }
        SaveSettings();
        Debug.Log("全部Prefab处理成功");
    }

    protected override void OnImGUI()
    {
        base.OnImGUI();
        Event evt = Event.current;
        if (evt.type == EventType.DragUpdated || evt.type == EventType.DragPerform)
        {
            DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
            if (evt.type == EventType.DragPerform)
            {
                DragAndDrop.AcceptDrag();
                foreach (Object draggedObject in DragAndDrop.objectReferences)
                {
                    if (draggedObject is GameObject gameObject)
                    {
                        string path = AssetDatabase.GetAssetPath(gameObject);
                        if (!string.IsNullOrEmpty(path))
                        {
                            var prefabEntry = new PrefabEntry
                            {
                                Prefab = gameObject,
                                Path = path,
                                Hash = "?"
                            };
                            if (!HasPrefab(gameObject))
                            {
                                prefabs.Add(prefabEntry);
                            }
                        }
                    }
                }
            }
            evt.Use();
        }
    }
    
    private bool HasPrefab(GameObject gameObject)
    {
        foreach (var entry in prefabs)
        {
            if (entry.Prefab == gameObject)
                return true;
        }
        return false;
    }
    
    private void SaveSettings()
    {
        var settings = new JArray();
        foreach (var entry in prefabs)
        {
            settings.Add(entry.Path + ":" + entry.Hash);
        }
        var settingObj = new JObject();
        settingObj["prefabs"] = settings;
        settingObj["useEditedResource"] = useEditedResource.ToString();
        WriteSettings(settingObj);
    }
    
    protected override void OnEnable()
    {
        base.OnEnable();
        var settingObj = ReadSettings();
        var settings = (JArray)settingObj["prefabs"];
        useEditedResource = (string)settingObj["useEditedResource"] == "True";
        prefabs.Clear();
        foreach (var path in settings)
        {
            var pathStr = (string)path;
            var pathArr = pathStr.Split(':');
            GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(pathArr[0]);
            if (prefab != null)
            {
                prefabs.Add(new PrefabEntry { Prefab = prefab, Path = pathArr[0], Hash = pathArr[1]});
            }
        }
    }
    
    protected override void OnDestroy()
    {
        SaveSettings();
        SaveUseEditedResource()
        base.OnDestroy();
    }

    [System.Serializable]
    public class PrefabEntry
    {
        [HideInInspector]
        public GameObject Prefab;
        [HideInInspector]
        public string Hash;

        [ReadOnly]
        public string Path;
        
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值