using Snxxz.UI; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using UnityEditor; using UnityEngine; using UnityEngine.UI; public class SpriteManageTool : EditorWindow { enum SpriteManageType { RepeatSprite, UnUseResource, Reference, PrefabEnglishLabel, FormatPrefab, ButtonArea, ExtractChsLabel, ImageMiss, UIStyle, } private Vector2 m_ScrollPosition; private Texture2D m_DeleteTexture; private List m_IconCfgs; [SerializeField] SpriteManageType m_SpriteManageType; private static readonly Regex Directory_Regex = new Regex(@"/([0-9a-zA-Z_]+)$"); private static readonly Regex Name_Regex = new Regex(@"\\([0-9a-zA-Z_\.]+)"); private static bool m_FindReference = false; #region 风格 private GUIStyle m_ButtonStyle; private void InitStyle() { m_ButtonStyle = new GUIStyle(); } #endregion [MenuItem("策划工具/图片资源")] public static void OpenWindow() { m_FindReference = false; SpriteManageTool _window = GetWindow(typeof(SpriteManageTool), false, "图片资源") as SpriteManageTool; _window.Show(); _window.autoRepaintOnSceneChange = true; } [MenuItem("Assets/查找引用")] private static void FindReference() { m_FindReference = true; SpriteManageTool _window = GetWindow(typeof(SpriteManageTool), false, "图片资源") as SpriteManageTool; _window.Show(); _window.autoRepaintOnSceneChange = true; } [MenuItem("Assets/查找引用", true)] private static bool FindReferenceValid() { string _path = AssetDatabase.GetAssetPath(Selection.activeObject); return (!string.IsNullOrEmpty(_path)); } private void OnGUI() { GUI.skin.button.normal.textColor = Color.white; DisplayFuncToggle(); UnityEngine.Object[] _objectArray = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets); var _selectPath = (_objectArray == null || _objectArray.Length == 0) ? string.Empty : Application.dataPath.Substring(0, Application.dataPath.LastIndexOf('/')) + "/" + AssetDatabase.GetAssetPath(_objectArray[0]); if (!ToolsHelper.lockFolderPath) { ToolsHelper.folderPath = _selectPath; } if (!m_LockPrefabPath) { m_ReplacePrefabPath = _selectPath; } GUI.skin.button.normal.textColor = m_ButtonTxtColor; switch (m_SpriteManageType) { case SpriteManageType.RepeatSprite: DisplayRepeat(); break; case SpriteManageType.UnUseResource: DisplayUnUse(); break; case SpriteManageType.Reference: DisplayReference(); break; case SpriteManageType.PrefabEnglishLabel: DisplayFindPrefabLabel(); break; case SpriteManageType.FormatPrefab: FormatPrefabTool.OnGUI(); break; case SpriteManageType.ButtonArea: ButtonAreaTool.OnGUI(); break; case SpriteManageType.ExtractChsLabel: DisplayExtractChsLabel(); break; case SpriteManageType.ImageMiss: ImageMissTool.OnGUI(); break; case SpriteManageType.UIStyle: DisplayUIStyle(); break; } } private void OnEnable() { ConfigInitiator.EditorLoad(); m_IconCfgs = IconConfig.GetValues(); m_DeleteTexture = EditorGUIUtility.FindTexture("TreeEditor.Trash"); InitStyle(); m_SpriteManageType = m_FindReference ? SpriteManageType.Reference : SpriteManageType.RepeatSprite; if (m_FindReference) { m_ReferenceSource = Selection.activeObject; } } private void DisplayFuncToggle() { GUILayout.BeginHorizontal(); string[] labels = new string[9] { "重复图片资源", "未引用的资源(图片)", "查找引用", "界面英文", "格式化预制体", "按钮监听区域", "提取界面中文","缺失图片组件", "UI风格" }; m_SpriteManageType = (SpriteManageType)GUILayout.Toolbar((int)m_SpriteManageType, labels, "LargeButton", GUILayout.Width(labels.Length * 130)); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } #region 重复图片资源 private Dictionary m_RepeatSprites = new Dictionary(); private Dictionary m_PrefabDict = new Dictionary(); private string m_ReplacePrefabPath = string.Empty; private bool m_LockPrefabPath = false; private void DisplayRepeat() { ToolsHelper.DisplayFolderPath(); DisplayPrefabPath(); GUILayout.BeginHorizontal(); if (GUILayout.Button("查找", GUILayout.Width(50), GUILayout.Height(25))) { StartFindRepeat(); StartFindPrefabsGuid(); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition); foreach (var _key in m_RepeatSprites.Keys) { GUILayout.BeginHorizontal(); GUILayout.Label(StringUtility.Contact("MD5:", _key)); if (GUILayout.Button("合并")) { MergeSprite(m_RepeatSprites[_key]); } if (GUILayout.Button("合并并删除")) { MergeSprite(m_RepeatSprites[_key]); m_RepeatSprites[_key].DeleteOther(); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); var _repeat = m_RepeatSprites[_key]; for (int i = 0; i < _repeat.repeats.Count; i++) { GUILayout.BeginHorizontal(); EditorGUILayout.ObjectField(_repeat.repeats[i], typeof(Sprite), false); if (GUILayout.Toggle(_repeat.select == i, "")) { _repeat.select = i; } if (_repeat.exitInCfgs[i] != -1) { var _index = _repeat.exitInCfgs[i]; GUILayout.Label(StringUtility.Contact("key:", m_IconCfgs[_index].id, " folder:", m_IconCfgs[_index].folder)); } if (_repeat.select != i && GUILayout.Button(m_DeleteTexture)) { _repeat.Delete(i); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Space(10); } GUILayout.EndScrollView(); } private void StartFindRepeat() { if (Regex.IsMatch(ToolsHelper.folderPath, @".*\..*")) { Debug.LogError("该路径不是文件夹路径"); return; } var _files = Directory.GetFiles(ToolsHelper.folderPath, "*.png", SearchOption.AllDirectories); if (_files == null || _files.Length == 0) { Debug.LogError("没找到图片"); return; } m_RepeatSprites.Clear(); int _index = 0; EditorApplication.update = () => { var _md5 = GetFileMD5(_files[_index]); if (m_RepeatSprites.ContainsKey(_md5)) { var _sprite = GetSpriteByAssetPath(_files[_index]); if (_sprite != null) { var _existIndex = m_IconCfgs.FindIndex((x) => { return x.sprite == _sprite.name; }); m_RepeatSprites[_md5].Add(_sprite, _existIndex); } } else { var _repeat = new RepeatSprite(); m_RepeatSprites.Add(_md5, _repeat); var _sprite = GetSpriteByAssetPath(_files[_index]); if (_sprite != null) { var _existIndex = m_IconCfgs.FindIndex((x) => { return x.sprite == _sprite.name; }); _repeat.Add(_sprite, _existIndex); } } bool isCancel = EditorUtility.DisplayCancelableProgressBar("查找重复资源", "", (float)_index / (float)_files.Length); _index++; if (isCancel || _index >= _files.Length) { EditorUtility.ClearProgressBar(); EditorApplication.update = null; _index = 0; var _keys = m_RepeatSprites.Keys.ToList(); if (_keys != null) { for (int i = 0; i < _keys.Count; i++) { if (m_RepeatSprites[_keys[i]].Count < 2) { m_RepeatSprites.Remove(_keys[i]); } } } _keys = null; } }; } private void StartFindPrefabsGuid() { if (Regex.IsMatch(m_ReplacePrefabPath, @".*\..*")) { Debug.LogError("该路径不是文件夹路径"); return; } m_PrefabDict.Clear(); string[] _prefabFiles = Directory.GetFiles(m_ReplacePrefabPath, "*.prefab", SearchOption.AllDirectories); if (_prefabFiles == null || _prefabFiles.Length == 0) { return; } for (int i = 0; i < _prefabFiles.Length; i++) { m_PrefabDict.Add(_prefabFiles[i], File.ReadAllText(_prefabFiles[i])); } } private void MergeSprite(RepeatSprite _repeat) { if (m_PrefabDict.Count == 0) { Debug.LogError("没找到预制体"); return; } var _nGuid = GetGUIDByAssets(_repeat.repeats[_repeat.select]); for (int i = 0; i < _repeat.repeats.Count; i++) { if (i == _repeat.select) { continue; } var _guid = GetGUIDByAssets(_repeat.repeats[i]); var _list = m_PrefabDict.Keys.ToList(); for (int j = 0; j < _list.Count; j++) { var _key = _list[j]; if (Regex.IsMatch(m_PrefabDict[_key], _guid)) { string _file = Regex.Replace(m_PrefabDict[_key], _guid, _nGuid); File.WriteAllText(_key, _file); m_PrefabDict[_key] = _file; AssetDatabase.SaveAssets(); } } } AssetDatabase.Refresh(); } private string GetFileMD5(string _filePath) { var _fileMd5 = string.Empty; using (FileStream fs = new FileStream(_filePath, FileMode.Open)) { System.Security.Cryptography.MD5 _md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); byte[] _bytes = _md5.ComputeHash(fs); for (int i = 0; i < _bytes.Length; i++) { _fileMd5 += _bytes[i].ToString("x2"); } } return _fileMd5; } public class RepeatSprite { public List repeats = new List(); public List exitInCfgs = new List(); public int select = 0; public void Add(Sprite _sprite, int _index) { repeats.Add(_sprite); exitInCfgs.Add(_index); } public void Delete(int _index) { var _sprite = repeats[_index]; if (select > _index) { select--; } repeats.RemoveAt(_index); exitInCfgs.RemoveAt(_index); AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(_sprite)); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } public void DeleteOther() { while (repeats.Count > 1) { Delete(select == 0 ? 1 : 0); } } public int Count { get { return repeats.Count; } } } #endregion #region 未引用资源 private Dictionary> m_UnUseSpriteDict = new Dictionary>(); private List m_UnUseSpritePaths = new List(); [SerializeField] string m_RemoveSpritePath = string.Empty; private void DisplayUnUse() { ToolsHelper.DisplayFolderPath(); DisplayPrefabPath(); GUILayout.BeginHorizontal(); if (GUILayout.Button("查找", GUILayout.Width(50), GUILayout.Height(25))) { StartFindPrefabsGuid(); StartFindUnUse(); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition); foreach (var _key in m_UnUseSpriteDict.Keys) { GUILayout.BeginHorizontal(); GUILayout.Label(StringUtility.Contact("Folder:", _key)); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); var _sprites = m_UnUseSpriteDict[_key]; for (int i = 0; i < _sprites.Count; i++) { GUILayout.BeginHorizontal(); EditorGUILayout.ObjectField(_sprites[i], typeof(Sprite), false); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Space(10); } GUILayout.EndScrollView(); GUILayout.BeginHorizontal(); GUILayout.Label(StringUtility.Contact("移除路径:", m_RemoveSpritePath)); if (GUILayout.Button("选择移出的文件根路径")) { var _path = EditorUtility.OpenFolderPanel("根路径", "", ""); m_RemoveSpritePath = _path; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (GUILayout.Button("开始移除")) { StartRemoveSprite(); } GUILayout.Space(20); } private void StartFindUnUse() { m_UnUseSpriteDict.Clear(); m_UnUseSpritePaths.Clear(); if (Regex.IsMatch(ToolsHelper.folderPath, @".*\..*")) { Debug.LogError("该路径不是文件夹路径"); return; } var _files = Directory.GetFiles(ToolsHelper.folderPath, "*.png", SearchOption.AllDirectories); if (_files == null || _files.Length == 0) { Debug.LogError("没找到图片"); return; } var _list = m_PrefabDict.Values.ToList(); int i = 0; EditorApplication.update = delegate () { var _guid = AssetDatabase.AssetPathToGUID(GetRelativeAssetsPath(_files[i])); FileInfo _fileInfo = new FileInfo(_files[i]); bool isCancel = EditorUtility.DisplayCancelableProgressBar("查找未引用资源", StringUtility.Contact(i, "/", _files.Length), (float)i / _files.Length); var _spriteName = Regex.Replace(_fileInfo.Name, @"\.png", string.Empty, RegexOptions.IgnoreCase); var _index = m_IconCfgs.FindIndex((x) => { return x.sprite.ToLower().Equals(_spriteName.ToLower()) && x.folder == _fileInfo.Directory.Name; }); if (_index == -1) { bool _unUse = true; for (int k = 0; k < _list.Count; k++) { if (Regex.IsMatch(_list[k], _guid)) { _unUse = false; break; } } if (_unUse) { var _sprite = GetSpriteByAssetPath(_files[i]); List _sprites = null; if (!m_UnUseSpriteDict.TryGetValue(_fileInfo.Directory.Name, out _sprites)) { _sprites = new List(); m_UnUseSpriteDict.Add(_fileInfo.Directory.Name, _sprites); } _sprites.Add(_sprite); m_UnUseSpritePaths.Add(_files[i]); } } i++; if (isCancel || i >= _files.Length) { EditorUtility.ClearProgressBar(); EditorApplication.update = null; i = 0; } }; } private void StartRemoveSprite() { if (m_UnUseSpriteDict.Count == 0) { return; } if (!Directory_Regex.IsMatch(m_RemoveSpritePath)) { return; } var _directory = Directory_Regex.Match(m_RemoveSpritePath).Groups[1].Value; var _path = StringUtility.Contact(m_RemoveSpritePath, "/", _directory); if (!Directory.Exists(_path)) { Directory.CreateDirectory(_path); } StartRecordRemoveSprite(_path); for (int i = 0; i < m_UnUseSpritePaths.Count; i++) { var _extention = Name_Regex.Match(m_UnUseSpritePaths[i]).Groups[1].Value; File.Move(m_UnUseSpritePaths[i], StringUtility.Contact(_path, @"\", _extention)); } m_UnUseSpriteDict.Clear(); m_UnUseSpritePaths.Clear(); AssetDatabase.Refresh(); } private void StartRecordRemoveSprite(string _path) { using (FileStream fs = new FileStream(StringUtility.Contact(_path, "/logger.txt"), FileMode.Create, FileAccess.Write)) { using (StreamWriter sw = new StreamWriter(fs)) { for (int i = 0; i < m_UnUseSpritePaths.Count; i++) { sw.WriteLine(m_UnUseSpritePaths[i]); } } } } #endregion #region 引用资源 private UnityEngine.Object m_ReferenceSource; private UnityEngine.Object m_ReferenceReplace; private static string[] m_ReferenceDisplays = new string[] { "Material", "Prefab", "Asset", "Scene" }; private Dictionary m_ReferenceObjectDict = new Dictionary(); private Dictionary m_ReferenceReplaceDict = new Dictionary(); private Dictionary> m_ReferenceDetailDict = new Dictionary>(); private List m_PrefabDetails = new List(); private string m_ReferenceGUID = string.Empty; [SerializeField] bool m_GlobalSearch = false; [SerializeField] int m_ReferenceType = 1 << (int)ReferenceType.Prefab; public enum ReferenceType { Material, Prefab, Asset, Scene, } private void DisplayReference() { GUILayout.BeginHorizontal(); GUILayout.Label("被引用的资源"); m_ReferenceSource = EditorGUILayout.ObjectField(m_ReferenceSource, typeof(UnityEngine.Object), false); m_GlobalSearch = GUILayout.Toggle(m_GlobalSearch, "全局搜索"); if (m_ReferenceSource != null && GUILayout.Button("显示GUID")) { m_ReferenceGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_ReferenceSource)); } if (m_ReferenceSource != null && m_ReferenceSource is GameObject && GUILayout.Button("分析Prefab")) { m_PrefabDetails.Clear(); GetReferenceDetail(File.ReadAllText(AssetDatabase.GetAssetPath(m_ReferenceSource)), string.Empty, m_PrefabDetails); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("GUID:"); EditorGUILayout.TextField(m_ReferenceGUID); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (m_ReferenceSource != null && m_ReferenceSource is GameObject) { DisplayPrefabDetail(); return; } if (!m_GlobalSearch) { ToolsHelper.DisplayFolderPath(); } GUILayout.BeginHorizontal(); GUILayout.Label("引用资源的类型"); m_ReferenceType = EditorGUILayout.MaskField((int)m_ReferenceType, m_ReferenceDisplays); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (m_ReferenceSource != null && m_ReferenceSource is Texture2D) { GUILayout.BeginHorizontal(); GUILayout.Label("替换资源"); m_ReferenceReplace = EditorGUILayout.ObjectField(m_ReferenceReplace, typeof(UnityEngine.Object), false); if (!(m_ReferenceReplace is Texture2D)) { m_ReferenceReplace = null; } if (m_ReferenceObjectDict.Count > 0 && m_ReferenceReplace != null) { if (GUILayout.Button("一键替换")) { var _replaceGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_ReferenceReplace)); m_ReferenceGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_ReferenceSource)); foreach (var _object in m_ReferenceReplaceDict.Keys) { if (m_ReferenceReplaceDict[_object] && m_ReferenceObjectDict.ContainsKey(_object)) { var _allText = m_ReferenceObjectDict[_object]; var _path = AssetDatabase.GetAssetPath(_object); if (Regex.IsMatch(_allText, m_ReferenceGUID)) { string _file = Regex.Replace(_allText, m_ReferenceGUID, _replaceGuid); File.WriteAllText(_path, _file); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } m_ReferenceObjectDict.Remove(_object); } } } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } if (GUILayout.Button("查找")) { if (m_ReferenceSource == null) { Debug.LogError("未指定所引用的资源"); return; } if (!m_GlobalSearch && Regex.IsMatch(ToolsHelper.folderPath, @".*\..*")) { Debug.LogError("必须为文件夹路径"); return; } string _path = AssetDatabase.GetAssetPath(m_ReferenceSource); if (!string.IsNullOrEmpty(_path)) { string _guid = AssetDatabase.AssetPathToGUID(_path); StartFindReference(_guid); } } m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition); foreach (var _object in m_ReferenceObjectDict.Keys) { GUILayout.BeginHorizontal(); EditorGUILayout.ObjectField(_object, typeof(UnityEngine.Object), false); if (_object is GameObject && GUILayout.Button("显示详细信息")) { if (!m_ReferenceDetailDict.ContainsKey(_object)) { List _details = new List(); GetReferenceDetail(m_ReferenceObjectDict[_object], AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_ReferenceSource)), _details); m_ReferenceDetailDict.Add(_object, _details); } } if (_object is GameObject) { m_ReferenceReplaceDict[_object] = EditorGUILayout.Toggle(m_ReferenceReplaceDict[_object]); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (m_ReferenceDetailDict.ContainsKey(_object)) { var _list = m_ReferenceDetailDict[_object]; for (int i = 0; i < _list.Count; i++) { var _detail = _list[i]; var _fatherDetail = _detail.fatherDetail; GUILayout.BeginHorizontal(); GUILayout.Label(StringUtility.Contact(i + 1, ".", "Path: ")); var _path = StringUtility.Contact(_detail.fatherDetail, _fatherDetail == string.Empty ? string.Empty : "/", _detail.name); GUILayout.TextField(_path); if (_fatherDetail != string.Empty && GUILayout.Button("Goto")) { var _go = GameObject.Find(_path); if (_go != null) { Selection.activeGameObject = _go; } } if (_fatherDetail == string.Empty && GUILayout.Button("FindFather")) { _detail.fatherDetail = GetFatherDetail(_detail.fatherId); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } } GUILayout.Space(5); } GUILayout.EndScrollView(); } private void DisplayPrefabDetail() { if (m_PrefabDetails.Count > 0 && GUILayout.Button("一键转换")) { for (int i = 0; i < m_PrefabDetails.Count; i++) { m_PrefabDetails[i].GuidToObjects(); } } m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition); for (int i = 0; i < m_PrefabDetails.Count; i++) { var _detail = m_PrefabDetails[i]; if (_detail.guids.Count == 0) { continue; } GUILayout.BeginHorizontal(); var _fatherDetail = _detail.fatherDetail; GUILayout.Label(StringUtility.Contact(i + 1, ".", "Path: ")); var _path = StringUtility.Contact(_detail.fatherDetail, _fatherDetail == string.Empty ? string.Empty : "/", _detail.name); GUILayout.TextField(_path); if (_fatherDetail != string.Empty && GUILayout.Button("Goto")) { var _go = GameObject.Find(_path); if (_go != null) { Selection.activeGameObject = _go; } } if (_fatherDetail == string.Empty && GUILayout.Button("FindFather")) { _detail.fatherDetail = GetFatherDetail(_detail.fatherId); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); for (int k = 0; k < _detail.guids.Count; k++) { var _guid = _detail.guids[k]; if (_detail.objectDict.ContainsKey(_guid)) { EditorGUILayout.ObjectField(_detail.objectDict[_guid], typeof(UnityEngine.Object), false); continue; } GUILayout.BeginHorizontal(); GUILayout.Label("Guid:"); GUILayout.Label(_guid); if (GUILayout.Button("GUID To 资源")) { if (!_detail.GuidToObject(k)) { k--; } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Space(10); } GUILayout.EndScrollView(); } private void StartFindReference(string _guid) { m_ReferenceObjectDict.Clear(); m_ReferenceReplaceDict.Clear(); m_ReferenceDetailDict.Clear(); m_RefDetailDict.Clear(); var _referenceExtentions = GetReferenceExtention(); if (_referenceExtentions.Count == 0) { Debug.LogError("未指定任意一种引用资源类型"); return; } string[] _files = Directory.GetFiles(m_GlobalSearch ? Application.dataPath : ToolsHelper.folderPath, "*.*", SearchOption.AllDirectories) .Where(s => _referenceExtentions.Contains(Path.GetExtension(s).ToLower())).ToArray(); int _index = 0; if (_files == null || _files.Length == 0) { EditorUtility.DisplayDialog("提示", "未找到任意文件", "确定"); return; } EditorApplication.update = delegate () { string _file = _files[_index]; bool isCancel = EditorUtility.DisplayCancelableProgressBar("查找引用资源", _file, (float)_index / (float)_files.Length); var _allTxt = File.ReadAllText(_file); if (Regex.IsMatch(_allTxt, _guid)) { UnityEngine.Object _object = AssetDatabase.LoadAssetAtPath(GetRelativeAssetsPath(_file)); m_ReferenceObjectDict.Add(_object, _allTxt); m_ReferenceReplaceDict.Add(_object, false); } _index++; if (isCancel || _index >= _files.Length) { EditorUtility.ClearProgressBar(); EditorApplication.update = null; _index = 0; } }; } private List GetReferenceExtention() { var _list = new List(); for (int i = 0; i < m_ReferenceDisplays.Length; i++) { if ((m_ReferenceType & (1 << i)) != 0) { switch ((ReferenceType)i) { case ReferenceType.Material: _list.Add(".mat"); break; case ReferenceType.Prefab: _list.Add(".prefab"); break; case ReferenceType.Asset: _list.Add(".asset"); break; case ReferenceType.Scene: _list.Add(".unity"); break; } } } return _list; } private readonly Regex s_PrefabRegex = new Regex(@"--- !u![0-9]+ \&([0-9]+)"); private readonly Regex s_GameObjectRegex = new Regex(@"m_GameObject\: \{fileID\: ([0-9]+)\}"); private readonly Regex s_GameObjectNameRegex = new Regex(@"m_Name\: ([a-zA-Z0-9_]+)"); private readonly Regex s_GuidRegex = new Regex(@"guid\: ([a-z0-9]+)"); private readonly Regex s_GameObjectFatherRegex = new Regex(@"m_Father\: \{fileID\: ([0-9]+)\}"); private Dictionary m_RefDetailDict = new Dictionary(); private void GetReferenceDetail(string _data, string _guid, List _details) { var _strArray = s_PrefabRegex.Split(_data); MatchCollection _matchs = s_PrefabRegex.Matches(_data); List _list = new List(_strArray); m_RefDetailDict.Clear(); if (_strArray != null) { _list.RemoveAll((x) => { return Regex.IsMatch(x, "^[0-9]+$"); }); var _index = 1; EditorApplication.update = delegate () { var _match = _matchs[_index - 1]; GetRefComponent(_list[_index], _list[_index].Split('\n'), _match.Groups[1].Value); bool isCancel = EditorUtility.DisplayCancelableProgressBar("查找引用资源", StringUtility.Contact(_index, "/", _list.Count), (float)_index / (float)_list.Count); _index++; if (isCancel || _index >= _list.Count) { _index = 0; if (_guid == string.Empty) { _details.AddRange(m_RefDetailDict.Values.ToList()); EditorUtility.ClearProgressBar(); EditorApplication.update = null; System.GC.Collect(); return; } var _keys = m_RefDetailDict.Keys.ToList(); EditorApplication.update = delegate () { var _detail = m_RefDetailDict[_keys[_index]]; if (_detail.guids.Contains(_guid)) { _details.Add(_detail); } isCancel = EditorUtility.DisplayCancelableProgressBar("查找引用资源", StringUtility.Contact(_index, "/", _keys.Count), (float)_index / (float)_keys.Count); _index++; if (isCancel || _index >= _keys.Count) { EditorUtility.ClearProgressBar(); _index = 0; EditorApplication.update = null; System.GC.Collect(); } }; } }; } } private void GetRefComponent(string _source, string[] _msgs, string _fileId) { if (_msgs != null) { switch (_msgs[1]) { case "GameObject:": { RefDetail _detail = new RefDetail(); m_RefDetailDict.Add(_fileId, _detail); var _match = s_GameObjectNameRegex.Match(_source); _detail.name = _match != null ? _match.Groups[1].Value : string.Empty; _detail.fileId = _fileId; } break; case "MonoBehaviour:": { var _match = s_GameObjectRegex.Match(_source); if (_match != null) { var _id = _match.Groups[1].Value; var _refDetail = m_RefDetailDict.ContainsKey(_id) ? m_RefDetailDict[_id] : null; if (_refDetail != null) { var _guidMatchs = s_GuidRegex.Matches(_source); if (_guidMatchs != null) { foreach (Match _guidMatch in _guidMatchs) { _refDetail.guids.Add(_guidMatch.Groups[1].Value); } } } } } break; case "RectTransform:": { var _match = s_GameObjectRegex.Match(_source); if (_match != null) { var _id = _match.Groups[1].Value; var _refDetail = m_RefDetailDict.ContainsKey(_id) ? m_RefDetailDict[_id] : null; if (_refDetail != null) { _refDetail.rectId = _fileId; var _fatherMatch = s_GameObjectFatherRegex.Match(_source); _refDetail.fatherId = _fatherMatch != null ? _fatherMatch.Groups[1].Value : string.Empty; } } } break; } } } public class RefDetail { public string fileId = string.Empty; public string name = string.Empty; public string fatherId = string.Empty; public string rectId = string.Empty; public string fatherDetail = string.Empty; public List guids = new List(); public Dictionary objectDict = new Dictionary(); public void GuidToObjects() { if (objectDict.Count >= guids.Count) { return; } for (int i = 0; i < guids.Count; i++) { if (objectDict.ContainsKey(guids[i])) { continue; } if (!GuidToObject(i)) { i--; } } } public bool GuidToObject(int _index) { var _objectPath = AssetDatabase.GUIDToAssetPath(guids[_index]); var _object = AssetDatabase.LoadAssetAtPath(_objectPath); if (_object == null || (_object != null && _object.name == "UnityEngine.UI")) { guids.RemoveAt(_index); return false; } else { objectDict.Add(guids[_index], _object); return true; } } } public string GetFatherDetail(string _fatherId) { if (_fatherId == string.Empty) { return string.Empty; } foreach (var _detail in m_RefDetailDict.Values) { if (_detail.rectId == _fatherId) { var _father = GetFatherDetail(_detail.fatherId); return StringUtility.Contact(GetFatherDetail(_detail.fatherId), _father == string.Empty ? string.Empty : "/", _detail.name); } } return string.Empty; } #endregion #region 查找界面上的指定文字 [SerializeField] string m_RegexPattern = ".*[a-zA-Z]+.*"; [SerializeField] bool m_IgnoreCaps = true; [SerializeField] string m_ReplaceLabelPath = string.Empty; private Dictionary> m_PrefabLabels = new Dictionary>(); private List m_PrefabTexts = new List(); private List m_PrefabCopyTexts = new List(); private Dictionary m_ReplaceLabelDict = new Dictionary(); protected string[] lineStep = new string[] { "\r\n" }; //行间隔体 private void DisplayFindPrefabLabel() { DisplayPrefabPath(); GUILayout.BeginHorizontal(); GUILayout.Label("正则表达式:"); m_RegexPattern = EditorGUILayout.TextField(m_RegexPattern); GUILayout.Label("忽略大小写"); m_IgnoreCaps = EditorGUILayout.Toggle(m_IgnoreCaps); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("替换的文本文件:"); GUILayout.Label(m_ReplaceLabelPath); if (GUILayout.Button("Broswer")) { m_ReplaceLabelPath = EditorUtility.OpenFilePanel("替换文本文件", "", "txt"); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (GUILayout.Button("查找")) { m_PrefabLabels.Clear(); m_ReplaceLabelDict.Clear(); if (ToolsHelper.folderPath == string.Empty) { return; } StartFindPrefabLabel(); } m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition); foreach (var _object in m_PrefabLabels.Keys) { var _list = m_PrefabLabels[_object]; EditorGUILayout.ObjectField(_object, typeof(GameObject), false); for (int i = 0; i < _list.Count; i++) { EditorGUILayout.TextField(_list[i]); } GUILayout.Space(10); } GUILayout.EndScrollView(); } private void StartFindPrefabLabel() { if (File.Exists(m_ReplaceLabelPath)) { var _fileInfo = new FileInfo(m_ReplaceLabelPath); var _fs = _fileInfo.OpenRead(); var _sr = new StreamReader(_fs, Encoding.UTF8); var lines = _sr.ReadToEnd().Split(lineStep, StringSplitOptions.None); m_ReplaceLabelDict.Clear(); for (int i = 0; i < lines.Length; i++) { var _line = lines[i].Split('\t'); if (_line.Length > 1 && _line[1] != string.Empty) { var _key = _line[0].ToLower(); if (!m_ReplaceLabelDict.ContainsKey(_key)) { m_ReplaceLabelDict.Add(_key, _line[1]); } } } _fs.Dispose(); _fs.Close(); } string[] _files = Directory.GetFiles(ToolsHelper.folderPath, "*.prefab", SearchOption.AllDirectories); if (_files == null || _files.Length == 0) { return; } var _index = 0; EditorApplication.update = delegate () { var _file = _files[_index]; var _prefab = AssetDatabase.LoadAssetAtPath(GetRelativeAssetsPath(_file)); if (_prefab != null) { FindAllText(_prefab, null); bool _change = false; if (m_PrefabTexts != null && m_PrefabTexts.Count > 0) { for (int i = 0; i < m_PrefabTexts.Count; i++) { var _text = m_PrefabTexts[i]; TextEx _textEx = _text as TextEx; if (_textEx != null && _textEx.isKey) { continue; } if (Regex.IsMatch(_text.text, m_RegexPattern, m_IgnoreCaps ? RegexOptions.IgnoreCase : RegexOptions.None)) { List _list = null; if (!m_PrefabLabels.TryGetValue(_prefab, out _list)) { _list = new List(); m_PrefabLabels.Add(_prefab, _list); } _list.Add(GetParentName(_text.transform)); if (m_ReplaceLabelDict.Count > 0) { var _collections = Regex.Matches(_text.text, m_RegexPattern); for (int k = 0; k < _collections.Count; k++) { var _match = _collections[k] as Match; var _key = _match.Groups[1].Value; if (m_ReplaceLabelDict.ContainsKey(_key.ToLower())) { _text.text = Regex.Replace(_text.text, _key, m_ReplaceLabelDict[_key.ToLower()]); _change = true; } } } } } } if (_change) { EditorUtility.SetDirty(_prefab); } } bool _isCancel = EditorUtility.DisplayCancelableProgressBar("查找界面文字", StringUtility.Contact(_index, "/", _files.Length), (float)_index / _files.Length); _index++; if (_isCancel || _index >= _files.Length) { EditorUtility.ClearProgressBar(); EditorApplication.update = null; AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); _index = 0; } }; } void FindAllText(GameObject _prefab,GameObject _copy) { m_PrefabTexts.Clear(); m_PrefabCopyTexts.Clear(); FindChildText(_prefab.transform, ref m_PrefabTexts); if (_copy != null) { FindChildText(_copy.transform, ref m_PrefabCopyTexts); } } void FindChildText(Transform _transform, ref List _allTexts) { if (_transform != null) { var text = _transform.GetComponent(); if (text != null) { _allTexts.Add(text); } for (int i = 0; i < _transform.childCount; i++) { FindChildText(_transform.GetChild(i), ref _allTexts); } } } string GetParentName(Transform _parent) { if (_parent.parent != null && _parent.GetComponent() == null) { return StringUtility.Contact(GetParentName(_parent.parent), "/", _parent.name); } return _parent.name; } #endregion #region 提取界面中文 private Dictionary m_ExtractChsDict = new Dictionary(); private Dictionary m_ExtractSerializedDict = new Dictionary(); private void DisplayExtractChsLabel() { ToolsHelper.DisplayFolderPath(); ToolsHelper.DisplayExternalPath(); if (GUILayout.Button("开始查找", GUILayout.Width(200))) { m_ExtractChsDict.Clear(); m_ExtractSerializedDict.Clear(); if (ToolsHelper.folderPath.Equals(string.Empty)) { return; } if (ToolsHelper.m_ExternalFilePath.Equals(string.Empty)) { return; } StartExtractLabel(); } } private void StartExtractLabel() { string[] _files = Directory.GetFiles(ToolsHelper.folderPath, "*.prefab", SearchOption.AllDirectories); if (_files == null || _files.Length == 0) { return; } var _index = 0; var inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance); EditorApplication.update = delegate () { var _file = _files[_index]; var _prefab = AssetDatabase.LoadAssetAtPath(GetRelativeAssetsPath(_file)); if (_prefab != null) { var all_text = File.ReadAllText(_file); var _copy = PrefabUtility.InstantiatePrefab(_prefab) as GameObject; if (_copy != null) { FindAllText(_prefab, _copy); bool _change = false; if (m_PrefabCopyTexts != null && m_PrefabCopyTexts.Count > 0) { for (int i = 0; i < m_PrefabCopyTexts.Count; i++) { var _text = m_PrefabCopyTexts[i]; var serializedObject = new SerializedObject(m_PrefabTexts[i]); inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null); var localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile"); var fieldId = localIdProp.longValue; bool isSerialized = ContainGuid(all_text, fieldId.ToString()); TextEx _textEx = _text as TextEx; if (_textEx != null && _textEx.isKey) { continue; } RichText _richText = _text as RichText; if (_richText != null && _richText.language) { continue; } if (Regex.IsMatch(_text.text, ".*[\u4e00-\u9fa5]+.*")) { _change = true; var _name = GetParentName(_text.transform); var _key = _name.Replace('/', '_'); _key = GetUnRepetitionKey(_key); var _value = _text.text; if (_textEx != null && !isSerialized) { _textEx.text = _key; _textEx.isKey = true; } else if (_richText != null && !isSerialized) { _richText.language = true; _richText.enableDisplay = _key; } else if (!isSerialized) { var nTextEx = ToolsHelper.PasteText(_text); nTextEx.isKey = true; nTextEx.text = _key; } m_ExtractChsDict.Add(_key, _value); m_ExtractSerializedDict.Add(_key, isSerialized); } } } if (_change) { PrefabUtility.ReplacePrefab(_copy, _prefab, ReplacePrefabOptions.ConnectToPrefab); } GameObject.DestroyImmediate(_copy); } } bool _isCancel = EditorUtility.DisplayCancelableProgressBar("提取界面中文", StringUtility.Contact(_index, "/", _files.Length), (float)_index / _files.Length); _index++; if (_isCancel || _index >= _files.Length) { EditorUtility.ClearProgressBar(); EditorApplication.update = null; AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); StartRecordExtract(ToolsHelper.m_ExternalFilePath); _index = 0; } }; } static bool ContainGuid(string _serializedFile, string _guid) { if (string.IsNullOrEmpty(_serializedFile)) { return false; } else { var matches = Regex.Matches(_serializedFile, _guid); return matches.Count > 2; } } private void StartRecordExtract(string _path) { using (FileStream fs = new FileStream(StringUtility.Contact(_path, "/logger.txt"), FileMode.Create, FileAccess.Write)) { using (StreamWriter sw = new StreamWriter(fs)) { foreach (var _key in m_ExtractChsDict.Keys) { sw.WriteLine(StringUtility.Contact(_key, '\t', m_ExtractChsDict[_key], "\t", m_ExtractSerializedDict[_key])); } } } } static Regex m_TrimNumberRegex = new Regex(@"\d+$", RegexOptions.Singleline); private string GetUnRepetitionKey(string _key) { if (m_ExtractChsDict.ContainsKey(_key)) { var _index = 0; if (m_TrimNumberRegex.IsMatch(_key)) { var _match = m_TrimNumberRegex.Match(_key); int.TryParse(_match.Value, out _index); _index++; _key = m_TrimNumberRegex.Replace(_key, string.Empty); } _key = StringUtility.Contact(_key, _index); _key = GetUnRepetitionKey(_key); } return _key; } #endregion #region UI风格 [SerializeField] Color m_ButtonTxtColor = Color.gray; private void DisplayUIStyle() { GUILayout.BeginHorizontal(); GUILayout.Label("按钮字色"); m_ButtonTxtColor = EditorGUILayout.ColorField(m_ButtonTxtColor); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } #endregion private void DisplayPrefabPath() { GUILayout.BeginHorizontal(); GUILayout.Label("预制体路径"); m_ReplacePrefabPath = GUILayout.TextField(m_ReplacePrefabPath); m_LockPrefabPath = GUILayout.Toggle(m_LockPrefabPath, "Lock"); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private Sprite GetSpriteByAssetPath(string _path) { return AssetDatabase.LoadAssetAtPath(GetRelativeAssetsPath(_path)); } private string GetRelativeAssetsPath(string _path) { return "Assets" + Path.GetFullPath(_path).Replace(Path.GetFullPath(Application.dataPath), "").Replace('\\', '/'); } private string GetGUIDByAssets(UnityEngine.Object _object) { if (_object == null) { return string.Empty; } return AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(_object)); } } public static class ToolsHelper { [SerializeField] public static string folderPath = string.Empty; [SerializeField] public static bool lockFolderPath = false; [SerializeField] public static string m_ExternalFilePath = string.Empty; public static void DisplayFolderPath() { GUILayout.BeginHorizontal(); GUILayout.Label("文件路径"); folderPath = GUILayout.TextField(folderPath); lockFolderPath = GUILayout.Toggle(lockFolderPath, "Lock"); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } public static string GetRelativeAssetsPath(string _path) { return "Assets" + Path.GetFullPath(_path).Replace(Path.GetFullPath(Application.dataPath), "").Replace('\\', '/'); } public static string GetGUIDByAssets(UnityEngine.Object _object) { if (_object == null) { return string.Empty; } return AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(_object)); } public static void DisplayExternalPath() { GUILayout.BeginHorizontal(); GUILayout.Label(StringUtility.Contact("文件夹路径:", m_ExternalFilePath)); if (GUILayout.Button("选择文件根路径")) { var _path = EditorUtility.OpenFolderPanel("根路径", "", ""); m_ExternalFilePath = _path; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } static string content = string.Empty; static Font fontData = null; static FontStyle fontStyle; static int fontSize = 14; static float lineSpacing = 0f; static bool richText = true; static TextAnchor alignment; static HorizontalWrapMode hwmode; static VerticalWrapMode vwmode; static Color m_Color; static Material m_Material; static bool raycastTarget; public static TextEx PasteText(Text text) { content = text.text; fontData = text.font; fontStyle = text.fontStyle; fontSize = text.fontSize; lineSpacing = text.lineSpacing; richText = text.supportRichText; alignment = text.alignment; hwmode = text.horizontalOverflow; vwmode = text.verticalOverflow; m_Color = text.color; m_Material = text.material; raycastTarget = text.raycastTarget; var gameObject = text.gameObject; Component.DestroyImmediate(text); TextEx textex = gameObject.AddMissingComponent(); textex.text = content; textex.font = fontData; textex.fontStyle = fontStyle; textex.fontSize = fontSize; textex.lineSpacing = lineSpacing; textex.supportRichText = richText; textex.alignment = alignment; textex.horizontalOverflow = hwmode; textex.verticalOverflow = vwmode; textex.color = m_Color; textex.material = m_Material; textex.raycastTarget = raycastTarget; return textex; } }