QQ扫一扫联系
在Unity项目开发中,如果特定的文件夹中的资源发生了变更,需要及时得到通知,以便及时进行处理。这个功能在Unity编辑器中默认是不支持的,需要使用代码实现。
具体实现方法是定义一个Editor脚本,在OnPostprocessAllAssets方法中检测指定文件夹中的资源变更情况,并给出相应的提示。通过设置AssetPostprocessor的静态成员变量assetPath,可以指定待检测的文件夹。
当资源变化时,OnPostprocessAllAssets方法就会被调用,在方法中遍历所有被修改的资源,并判断它们是否存在于assetPath所指定的文件夹中。如果有,就表示这个文件夹中的资源已经发生了变更,可以进行相应的提示操作,比如弹出对话框、打印日志等。
由于资源变更可能比较频繁,因此需要保证检测的效率和准确性。为了减少不必要的遍历和检测,可以对资源的修改时间进行记录,只有当资源的修改时间晚于上次检测时间时,才进行检测并提示。
总之,通过自定义Editor脚本实现特定文件夹资源变更提醒功能,可以帮助开发者更加高效地管理和维护Unity项目。
//**************************************************** // *------------------------------------------------------------------------ // * 文件:ResourceChangeDetectorSettings.cs // * 作者:Admin (WangYin) // * 日期:Created by 2023/4/4 10:7:58 // * 功能:记录每个资源的路径和上一次修改时间 // ********** #if UNITY_EDITOR using UnityEngine; using System.Collections.Generic; using UnityEditor; using Sirenix.OdinInspector; using System.IO; using System; [CreateAssetMenu(fileName = "ResourceChangeDetectorSettings", menuName = "Config/ResourceChangeDetectorSettings")] public class ResourceChangeDetectorSettings : ScriptableObject { public List<ResourceInfo> resourceInfos = new List<ResourceInfo>(); private const string folderPath = "Assets/Art/Common"; private int totalFileCount; [Button("扫描文件夹下的资源,记录修改时间")] public void ScanResourceInfo() { resourceInfos.Clear(); totalFileCount = CountFiles(folderPath); int currentFileIndex = 0; ScanFolder(folderPath, ref currentFileIndex); EditorUtility.SetDirty(this); AssetDatabase.SaveAssets(); } [Button("清空resourceInfos")] public void Clear() { resourceInfos.Clear(); EditorUtility.SetDirty(this); AssetDatabase.SaveAssets(); } public void ScanFolder(string folderPath, ref int currentFileIndex) { string[] subFolders = Directory.GetDirectories(folderPath); // 遍历当前文件夹下的所有子文件夹 foreach (string subFolder in subFolders) { ScanFolder(subFolder, ref currentFileIndex); } // 遍历当前文件夹下的所有文件,并将它们的资源路径和最后修改时间记录到 resourceInfos string[] files = Directory.GetFiles(folderPath); foreach (string file in files) { currentFileIndex++; float progress = (float)currentFileIndex / totalFileCount; EditorUtility.DisplayProgressBar("Traversing Folder...", "", progress); if (file.EndsWith(".meta")) continue; // 忽略 .meta 文件 ResourceInfo info = new ResourceInfo(); string path = file.Replace('\\', '/'); info.assetPath = path; // 将物理路径转换为 Asset 路径 long ticks = new FileInfo(file).LastWriteTime.Ticks; DateTime lastWriteTime = new DateTime(ticks, DateTimeKind.Utc); info.lastWriteTime = lastWriteTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); info.Author = Tools.GetFirstAuthor(path); info.LastAuthor = Tools.GetLastAuthor(path); resourceInfos.Add(info); } EditorUtility.ClearProgressBar(); } private int CountFiles(string folderPath) { int fileCount = 0; foreach (string filePath in Directory.GetFiles(folderPath)) { fileCount++; } foreach (string subFolderPath in Directory.GetDirectories(folderPath)) { fileCount += CountFiles(subFolderPath); } return fileCount; } public ResourceInfo GetResourceInfo(string assetPath) { foreach (ResourceInfo resourceInfo in resourceInfos) { if (resourceInfo.assetPath.Contains(assetPath)) { return resourceInfo; } } return null; } } [System.Serializable] public class ResourceInfo { //文件路径 public string assetPath; //最后修改的时间 public string lastWriteTime; //最后一次修改的作者 public string LastAuthor; //第一次提交的作者 public string Author; public ResourceInfo(string assetPath) { this.assetPath = assetPath; } public ResourceInfo() { } } #endif
//**************************************************** // *------------------------------------------------------------------------ // * 文件:FolderChangeDetector .cs // * 作者:Admin (WangYin) // * 日期:Created by 2023/4/4 10:11:42 // * 功能:FolderChangeDetector 特定文件夹资源变更提醒 // *****************************************************/ using UnityEngine; using UnityEditor; using System.IO; using System; using Editor.DCarUtil; public class FolderChangeDetector : AssetPostprocessor { private static ResourceChangeDetectorSettings settings; // 配置文件对象 // 指定要监视的文件夹路径 private static string FOLDER_TO_MONITOR = "Assets/Art/Common"; // 用于记录工程里要监视的文件夹里每个文件的路径和上次修改的时间 private static string ConfigPath = "Assets/Scripts/Config/ResourceChangeDetectorSettings.asset"; public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { if (importedAssets.Length > 0) { foreach (string assetPath in importedAssets) { if (assetPath == ConfigPath) { return; } Debug.Log($"OnPostprocessAllAssets: importedAssets {assetPath} "); CheckAssetChange(assetPath); } } if (deletedAssets.Length > 0) { foreach (string assetPath in deletedAssets) { Debug.Log($"OnPostprocessAllAssets: deletedAssets {assetPath} "); RemoveAsset(assetPath); } } if (movedAssets.Length > 0) { for (int i = 0; i < movedAssets.Length; i++) { Debug.Log($"OnPostprocessAllAssets: movedAssets {movedAssets[i]} movedFromAssetPaths: {movedFromAssetPaths[i]} "); RemoveAsset(movedFromAssetPaths[i]); CheckAssetChange(movedAssets[i]); } // 如果有资源被修改,保存最后修改时间到配置文件中,并输出相应信息 foreach (string assetPath in movedAssets) { Debug.Log($"OnPostprocessAllAssets: movedAssets {assetPath} "); CheckAssetChange(assetPath); } } if (importedAssets.Length > 0 || deletedAssets.Length > 0 || movedAssets.Length > 0) { if (settings) { EditorUtility.SetDirty(settings); AssetDatabase.SaveAssets(); } } } private static void LoadSettings() { settings = AssetDatabase.LoadAssetAtPath<ResourceChangeDetectorSettings>(ConfigPath); if (settings == null) { // 如果配置文件不存在,则创建一个新的配置文件 settings = ScriptableObject.CreateInstance<ResourceChangeDetectorSettings>(); AssetDatabase.CreateAsset(settings, ConfigPath); EditorUtility.SetDirty(settings); AssetDatabase.SaveAssets(); } } private static void CheckAssetChange(string assetPath) { if (!assetPath.Contains(FOLDER_TO_MONITOR) || assetPath == ConfigPath) { return; } string fullPath = Application.dataPath + assetPath; FileInfo fileInfo = new FileInfo(fullPath); long ticks = DateTime.UtcNow.Ticks; DateTime lastWriteTime = new DateTime(ticks, DateTimeKind.Utc); string lastWriteTimeStr = lastWriteTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); if (settings == null) { LoadSettings(); } // 更新配置文件中保存的最后修改时间 ResourceInfo resourceInfo = settings.GetResourceInfo(assetPath); if (resourceInfo == null) { resourceInfo = new ResourceInfo(assetPath); resourceInfo.Author = Tools.GetUserName(); resourceInfo.lastWriteTime = lastWriteTimeStr; settings.resourceInfos.Add(resourceInfo); Debug.Log($"{fileInfo.Name} 由 {resourceInfo.Author} 创建,创建时间:{resourceInfo.lastWriteTime}"); } else { string str = $"公共资源变更提醒:\n\n{fileInfo.Name} 由 {resourceInfo.Author} 创建,\n\n上次修改时间:{resourceInfo.lastWriteTime}\n\n\n现在修改时间:{lastWriteTimeStr}"; DialogUtil.Alert(str); resourceInfo.lastWriteTime = lastWriteTimeStr; resourceInfo.LastAuthor = Tools.GetUserName(); EditorUtility.SetDirty(settings); AssetDatabase.SaveAssets(); } } private static void RemoveAsset(string assetPath) { if (!assetPath.Contains(FOLDER_TO_MONITOR)) { return; } // 更新配置文件中保存的最后修改时间 ResourceInfo resourceInfo = settings.GetResourceInfo(assetPath); // 移除配置文件中的对应信息 if (resourceInfo != null) { settings.resourceInfos.Remove(resourceInfo); EditorUtility.SetDirty(settings); AssetDatabase.SaveAssets(); } } }
//用CMD获取GIt里的用户名 public static string GetUserName() { Process process = new Process(); process.StartInfo.FileName = "git"; process.StartInfo.Arguments = "config user.name"; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.Start(); string output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); return output.Trim(); } //获取最后一次提交这个文件的作者 public static string GetLastAuthor(string filePath) { ProcessStartInfo psi = new ProcessStartInfo("git", $"log -n 1 --pretty=format:%an -- {filePath}") { RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }; Process process = new Process { StartInfo = psi }; process.Start(); string output = process.StandardOutput.ReadToEnd().Trim(); process.WaitForExit(); return output; } //获取第一次提交这个文件的作者 public static string GetFirstAuthor(string filePath) { ProcessStartInfo psi = new ProcessStartInfo("git", $"log --reverse --format=%an -- {filePath} | head -n 1") { RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }; Process process = new Process { StartInfo = psi }; process.Start(); string output = process.StandardOutput.ReadToEnd().Trim(); process.WaitForExit(); return output; }