using System; using System.Collections.Generic; using System.IO; using System.Text; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif [Serializable] public class UI_FunctionHoverEntry { public string path; [TextArea(2, 6)] public string text; public string iconPath; public Sprite icon; } [CreateAssetMenu(menuName = "UI/Function Hover Config", fileName = "UI_FunctionHoverConfig")] public class UI_FunctionHoverConfig : ScriptableObject { public List entries = new List(); #if UNITY_EDITOR [ContextMenu("Import From 素材/指向")] private void ImportFromDefaultFile() { ImportFromFile("素材/指向"); } [ContextMenu("Import From UI_FunctionHoverConfig.json")] private void ImportFromDefaultJson() { ImportFromJsonFile("Assets/Resources/UI_FunctionHoverConfig.json"); } public void ImportFromFile(string relativePath) { if (string.IsNullOrEmpty(relativePath)) return; string fullPath = Path.Combine(Application.dataPath, "..", relativePath); if (!File.Exists(fullPath)) { Debug.LogWarning("UI_FunctionHoverConfig: file not found: " + fullPath); return; } string content = File.ReadAllText(fullPath, Encoding.UTF8); var parsed = ParseConfig(content); if (parsed.Count > 0) { entries = parsed; EditorUtility.SetDirty(this); AssetDatabase.SaveAssets(); } } public void ImportFromJsonFile(string relativePath) { if (string.IsNullOrEmpty(relativePath)) return; string fullPath = Path.Combine(Application.dataPath, "..", relativePath); if (!File.Exists(fullPath)) { Debug.LogWarning("UI_FunctionHoverConfig: json not found: " + fullPath); return; } string content = File.ReadAllText(fullPath, Encoding.UTF8); var parsed = ParseJson(content); if (parsed.Count > 0) { entries = parsed; EditorUtility.SetDirty(this); AssetDatabase.SaveAssets(); } } #endif private static List ParseConfig(string content) { var result = new List(); if (string.IsNullOrEmpty(content)) return result; var blocks = content.Split(new[] { "\r\n\r\n", "\n\n" }, StringSplitOptions.RemoveEmptyEntries); foreach (var block in blocks) { string pathLine = null; string textLine = null; string iconLine = null; var lines = block.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { if (line.StartsWith("路径")) pathLine = line; else if (line.StartsWith("文本")) textLine = line; else if (line.StartsWith("显示icon")) iconLine = line; } if (pathLine == null || textLine == null) continue; string pathsRaw = GetValueAfterColon(pathLine); string text = GetValueAfterColon(textLine); string iconPath = iconLine != null ? GetValueAfterColon(iconLine) : null; var paths = SplitPaths(pathsRaw); var icon = LoadIcon(iconPath); foreach (var p in paths) { if (string.IsNullOrWhiteSpace(p)) continue; result.Add(new UI_FunctionHoverEntry { path = p.Trim(), text = text, iconPath = iconPath, icon = icon }); } } return result; } [Serializable] private class JsonRoot { public List entries; } [Serializable] private class JsonEntry { public List paths; public string text; public string icon; } private static List ParseJson(string content) { var result = new List(); if (string.IsNullOrEmpty(content)) return result; JsonRoot root = null; try { root = JsonUtility.FromJson(content); } catch { root = null; } if (root == null || root.entries == null) return result; for (int i = 0; i < root.entries.Count; i++) { var entry = root.entries[i]; if (entry == null || entry.paths == null) continue; Sprite icon = LoadIcon(entry.icon); for (int p = 0; p < entry.paths.Count; p++) { var path = entry.paths[p]; if (string.IsNullOrWhiteSpace(path)) continue; result.Add(new UI_FunctionHoverEntry { path = path.Trim(), text = entry.text, iconPath = entry.icon, icon = icon }); } } return result; } private static string GetValueAfterColon(string line) { int idx = line.IndexOf(':'); if (idx < 0) idx = line.IndexOf(':'); if (idx < 0) return line.Trim(); return line.Substring(idx + 1).Trim(); } private static string[] SplitPaths(string raw) { if (string.IsNullOrEmpty(raw)) return Array.Empty(); string tmp = raw.Replace("和", "|") .Replace("、", "|") .Replace(",", "|") .Replace(",", "|") .Replace(";", "|") .Replace(";", "|"); return tmp.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); } private static Sprite LoadIcon(string iconPath) { if (string.IsNullOrWhiteSpace(iconPath)) return null; string path = NormalizeAssetPath(iconPath); if (!path.StartsWith("Assets", StringComparison.OrdinalIgnoreCase)) path = "Assets/" + path; #if UNITY_EDITOR Sprite direct = TryLoadByPath(path); if (direct != null) return direct; string name = Path.GetFileName(path); string folder = Path.GetDirectoryName(path); List searchFolders = new List(); if (!string.IsNullOrEmpty(folder) && Directory.Exists(Path.Combine(Application.dataPath, "..", folder))) searchFolders.Add(folder); string uiUiFolder = "Assets/artworks/UI_UI"; if (Directory.Exists(Path.Combine(Application.dataPath, "..", uiUiFolder))) searchFolders.Add(uiUiFolder); string artworksFolder = "Assets/artworks"; if (Directory.Exists(Path.Combine(Application.dataPath, "..", artworksFolder))) searchFolders.Add(artworksFolder); string[] folders = searchFolders.Count > 0 ? searchFolders.ToArray() : null; var guids = AssetDatabase.FindAssets(name + " t:Sprite", folders); if (guids.Length == 0) guids = AssetDatabase.FindAssets(name + " t:Texture2D", folders); if (guids.Length > 0) { string best = PickBestAssetPath(guids, name, folder); var sprite = AssetDatabase.LoadAssetAtPath(best); if (sprite != null) return sprite; var tex = AssetDatabase.LoadAssetAtPath(best); if (tex != null) return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); } #endif return null; } private static string NormalizeAssetPath(string raw) { if (string.IsNullOrWhiteSpace(raw)) return string.Empty; string path = raw.Replace('>', '/').Replace('\\', '/').Trim(); path = path.Replace("Ul_Ul", "UI_UI"); path = path.Replace("(", " (").Replace(")", ")"); path = path.Replace(" ", " ").Trim(); return path; } #if UNITY_EDITOR private static Sprite TryLoadByPath(string path) { if (string.IsNullOrWhiteSpace(path)) return null; string[] candidates = path.IndexOf('.') >= 0 ? new[] { path } : new[] { path + ".png", path + ".jpg", path + ".jpeg" }; for (int i = 0; i < candidates.Length; i++) { string p = candidates[i]; var sprite = AssetDatabase.LoadAssetAtPath(p); if (sprite != null) return sprite; var tex = AssetDatabase.LoadAssetAtPath(p); if (tex != null) return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); } return null; } private static string PickBestAssetPath(string[] guids, string name, string preferredFolder) { if (guids == null || guids.Length == 0) return null; string best = AssetDatabase.GUIDToAssetPath(guids[0]); if (guids.Length == 1) return best; string preferredLower = (preferredFolder ?? string.Empty).Replace('\\', '/').ToLowerInvariant(); string uiUiFolder = "assets/artworks/ui_ui"; for (int i = 0; i < guids.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(guids[i]); if (string.IsNullOrEmpty(path)) continue; string lower = path.ToLowerInvariant(); if (!string.IsNullOrEmpty(preferredLower) && lower.Contains(preferredLower)) return path; if (lower.Contains(uiUiFolder)) best = path; } return best; } #endif }