diff --git a/Assets/Editor/TimelineNameCollector/TimelineNameCache.cs b/Assets/Editor/TimelineNameCollector/TimelineNameCache.cs deleted file mode 100644 index afa3d2a57..000000000 --- a/Assets/Editor/TimelineNameCollector/TimelineNameCache.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Security.Cryptography; -using System.Text; -using Newtonsoft.Json; -using UnityEditor; -using UnityEngine; - -namespace AibisDream.EditorTools -{ - [Serializable] - public class TimelineNameCacheEntry - { - public string guid; - public string assetPath; - public string fileHash; - public string lastModified; - public string timelineName; - public string gameObjectPath; - public bool isInScene; - public int lineNumber; - } - - [Serializable] - public class TimelineNameCache - { - public const int CurrentVersion = 1; - - public int version = CurrentVersion; - public string lastFullScanTime; - public List entries = new List(); - - private static readonly string CacheFileName = "timeline_name_cache.json"; - - public static string CacheFolderFullPath => - Path.Combine(Application.dataPath, "Editor/TimelineNameCollector/Cache"); - - public static string CacheFileFullPath => - Path.Combine(CacheFolderFullPath, CacheFileName); - - public static TimelineNameCache Load() - { - try - { - if (!File.Exists(CacheFileFullPath)) - { - return new TimelineNameCache - { - version = CurrentVersion, - lastFullScanTime = null, - entries = new List() - }; - } - - var json = File.ReadAllText(CacheFileFullPath, Encoding.UTF8); - if (string.IsNullOrEmpty(json)) - { - return new TimelineNameCache - { - version = CurrentVersion, - lastFullScanTime = null, - entries = new List() - }; - } - - var cache = JsonConvert.DeserializeObject(json); - if (cache == null) - { - cache = new TimelineNameCache(); - } - - if (cache.version != CurrentVersion) - { - cache.version = CurrentVersion; - cache.lastFullScanTime = null; - cache.entries = cache.entries ?? new List(); - } - - return cache; - } - catch (Exception e) - { - Debug.LogError($"[TimelineNameCache] Failed to load cache: {e}"); - return new TimelineNameCache - { - version = CurrentVersion, - lastFullScanTime = null, - entries = new List() - }; - } - } - - public void Save() - { - try - { - if (!Directory.Exists(CacheFolderFullPath)) - { - Directory.CreateDirectory(CacheFolderFullPath); - } - - entries ??= new List(); - - var settings = new JsonSerializerSettings - { - Formatting = Formatting.Indented, - StringEscapeHandling = StringEscapeHandling.Default - }; - var json = JsonConvert.SerializeObject(this, settings); - File.WriteAllText(CacheFileFullPath, json, Encoding.UTF8); - } - catch (Exception e) - { - Debug.LogError($"[TimelineNameCache] Failed to save cache: {e}"); - } - } - - public void SetLastFullScanNow() - { - lastFullScanTime = DateTime.UtcNow.ToString("o"); - } - - public static string ComputeFileHash(string fullPath) - { - try - { - if (!File.Exists(fullPath)) - { - return null; - } - - using (var stream = File.OpenRead(fullPath)) - using (var md5 = MD5.Create()) - { - var hash = md5.ComputeHash(stream); - var sb = new StringBuilder(hash.Length * 2); - foreach (var b in hash) - { - sb.Append(b.ToString("x2")); - } - return sb.ToString(); - } - } - catch (Exception e) - { - Debug.LogError($"[TimelineNameCache] Failed to compute file hash for '{fullPath}': {e}"); - return null; - } - } - - public static string GetAssetFullPath(string assetPath) - { - if (string.IsNullOrEmpty(assetPath)) - return null; - - if (!assetPath.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase) && - !assetPath.Equals("Assets", StringComparison.OrdinalIgnoreCase)) - { - return null; - } - - var relative = assetPath.Substring("Assets".Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - return Path.Combine(Application.dataPath, relative); - } - - public void RemoveEntriesForAsset(string assetPath) - { - if (entries == null || string.IsNullOrEmpty(assetPath)) return; - entries.RemoveAll(e => e != null && e.assetPath == assetPath); - } - - public void CleanupDeletedAssets() - { - if (entries == null) return; - entries.RemoveAll(e => - { - if (e == null || string.IsNullOrEmpty(e.assetPath)) return true; - return AssetDatabase.LoadMainAssetAtPath(e.assetPath) == null; - }); - } - } -} - diff --git a/Assets/Editor/TimelineNameCollector/TimelineNameCache.cs.meta b/Assets/Editor/TimelineNameCollector/TimelineNameCache.cs.meta deleted file mode 100644 index 2e5d3561f..000000000 --- a/Assets/Editor/TimelineNameCollector/TimelineNameCache.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 58e8fb78cff00f44482eff9d6a1b7a64 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Editor/TimelineNameCollector/TimelineNameCollectorWindow.cs b/Assets/Editor/TimelineNameCollector/TimelineNameCollectorWindow.cs deleted file mode 100644 index df624103a..000000000 --- a/Assets/Editor/TimelineNameCollector/TimelineNameCollectorWindow.cs +++ /dev/null @@ -1,567 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using UnityEditor; -using UnityEditor.SceneManagement; -using UnityEngine; - -namespace AibisDream.EditorTools -{ - public class TimelineNameCollectorWindow : EditorWindow - { - private TimelineNameCache _cache; - - private Vector2 _scrollPos; - private string _searchText = string.Empty; - private bool _showScenes = true; - private bool _showPrefabs = true; - private bool _showConflictsOnly; - - private readonly List _sortedEntries = new List(); - private readonly Dictionary> _entriesByName = - new Dictionary>(StringComparer.OrdinalIgnoreCase); - - private readonly HashSet _conflictNames = - new HashSet(StringComparer.OrdinalIgnoreCase); - - private TimelineNameCacheEntry _selectedEntry; - - [MenuItem("Tools/Timeline Name Collector")] - public static void ShowWindow() - { - var window = GetWindow("Timeline Name Collector"); - window.minSize = new Vector2(700, 400); - window.Show(); - } - - private void OnEnable() - { - LoadCacheIfNeeded(); - RebuildIndexes(); - } - - private void LoadCacheIfNeeded() - { - _cache ??= TimelineNameCache.Load(); - _cache.entries ??= new List(); - } - - private void RebuildIndexes() - { - _sortedEntries.Clear(); - _entriesByName.Clear(); - _conflictNames.Clear(); - - if (_cache == null || _cache.entries == null) - return; - - foreach (var entry in _cache.entries) - { - if (entry == null) - continue; - - _sortedEntries.Add(entry); - - if (!_entriesByName.TryGetValue(entry.timelineName, out var list)) - { - list = new List(); - _entriesByName[entry.timelineName] = list; - } - list.Add(entry); - } - - _sortedEntries.Sort((a, b) => - { - var nameCompare = string.Compare(a.timelineName, b.timelineName, StringComparison.OrdinalIgnoreCase); - if (nameCompare != 0) return nameCompare; - var typeCompare = a.isInScene.CompareTo(b.isInScene); - if (typeCompare != 0) return -typeCompare; // 场景优先 - return string.Compare(a.assetPath, b.assetPath, StringComparison.OrdinalIgnoreCase); - }); - - foreach (var kv in _entriesByName) - { - if (kv.Value.Count > 1) - { - _conflictNames.Add(kv.Key); - } - } - } - - private void OnGUI() - { - LoadCacheIfNeeded(); - - DrawToolbar(); - EditorGUILayout.Space(); - DrawFilterBar(); - EditorGUILayout.Space(); - DrawSummary(); - EditorGUILayout.Space(); - DrawListArea(); - EditorGUILayout.Space(); - DrawDetailArea(); - } - - private void DrawToolbar() - { - EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); - - if (GUILayout.Button("刷新选中", EditorStyles.toolbarButton, GUILayout.Width(90))) - { - RefreshSelected(); - } - - if (GUILayout.Button("刷新全部", EditorStyles.toolbarButton, GUILayout.Width(90))) - { - RefreshAll(forceFull: false); - } - - if (GUILayout.Button("强制全量", EditorStyles.toolbarButton, GUILayout.Width(90))) - { - RefreshAll(forceFull: true); - } - - GUILayout.FlexibleSpace(); - - var lastFullScanLabel = string.IsNullOrEmpty(_cache.lastFullScanTime) - ? "未进行全量扫描" - : $"上次全量: {FormatTime(_cache.lastFullScanTime)}"; - GUILayout.Label(lastFullScanLabel, EditorStyles.miniLabel); - - EditorGUILayout.EndHorizontal(); - } - - private void DrawFilterBar() - { - EditorGUILayout.BeginHorizontal(); - - GUILayout.Label("搜索:", GUILayout.Width(40)); - _searchText = EditorGUILayout.TextField(_searchText); - - _showScenes = GUILayout.Toggle(_showScenes, "场景", GUILayout.Width(60)); - _showPrefabs = GUILayout.Toggle(_showPrefabs, "预制体", GUILayout.Width(70)); - _showConflictsOnly = GUILayout.Toggle(_showConflictsOnly, "只看冲突", GUILayout.Width(80)); - - EditorGUILayout.EndHorizontal(); - } - - private void DrawSummary() - { - var total = _sortedEntries.Count; - var conflictCount = _conflictNames.Count; - EditorGUILayout.LabelField($"找到 {total} 个 TimelineName 条目({conflictCount} 个重名)", - EditorStyles.boldLabel); - } - - private void DrawListArea() - { - EditorGUILayout.LabelField("列表", EditorStyles.boldLabel); - - EditorGUILayout.BeginHorizontal(); - GUILayout.Label("Timeline Name", GUILayout.Width(220)); - GUILayout.Label("类型", GUILayout.Width(50)); - GUILayout.Label("位置", GUILayout.ExpandWidth(true)); - GUILayout.Label("", GUILayout.Width(90)); - EditorGUILayout.EndHorizontal(); - - var rect = GUILayoutUtility.GetRect(0, 100000, 0, 100000); - _scrollPos = GUI.BeginScrollView(rect, _scrollPos, new Rect(0, 0, rect.width - 20, _sortedEntries.Count * 20 + 10)); - - var y = 0f; - var rowHeight = 20f; - var viewWidth = rect.width - 20; - - foreach (var entry in _sortedEntries) - { - if (!PassFilter(entry)) - continue; - - var isConflict = _conflictNames.Contains(entry.timelineName); - var rowRect = new Rect(0, y, viewWidth, rowHeight); - DrawRow(rowRect, entry, isConflict); - y += rowHeight; - } - - GUI.EndScrollView(); - } - - private void DrawRow(Rect rect, TimelineNameCacheEntry entry, bool isConflict) - { - var typeLabel = entry.isInScene ? "场景" : "预制体"; - var location = GetShortLocation(entry); - - var bgColor = GUI.backgroundColor; - if (isConflict) - { - GUI.backgroundColor = new Color(1f, 0.9f, 0.9f); - } - - if (Event.current.type == EventType.MouseDown && rect.Contains(Event.current.mousePosition)) - { - if (Event.current.button == 0) - { - _selectedEntry = entry; - Repaint(); - Event.current.Use(); - } - } - - GUI.Box(rect, GUIContent.none, EditorStyles.helpBox); - - GUI.backgroundColor = bgColor; - - var colRect = rect; - colRect.x += 4; - colRect.width = 220; - GUI.Label(colRect, entry.timelineName); - - colRect.x += colRect.width + 4; - colRect.width = 50; - GUI.Label(colRect, typeLabel); - - colRect.x += colRect.width + 4; - colRect.width = rect.width - colRect.x - 100; - GUI.Label(colRect, location); - - colRect.x += colRect.width + 4; - colRect.width = 90; - if (GUI.Button(colRect, "项目中显示")) - { - PingEntry(entry); - } - } - - private void DrawDetailArea() - { - EditorGUILayout.LabelField("选中项详情", EditorStyles.boldLabel); - - if (_selectedEntry == null) - { - EditorGUILayout.HelpBox("在上方列表中点击某一行查看详情。", MessageType.Info); - return; - } - - GUILayout.Label($"Timeline Name: {_selectedEntry.timelineName}", EditorStyles.boldLabel); - - if (_entriesByName.TryGetValue(_selectedEntry.timelineName, out var group)) - { - foreach (var e in group) - { - EditorGUILayout.BeginVertical("box"); - - EditorGUILayout.LabelField("资源路径", e.assetPath); - EditorGUILayout.LabelField("GameObject", string.IsNullOrEmpty(e.gameObjectPath) ? "(未知)" : e.gameObjectPath); - EditorGUILayout.LabelField("类型", e.isInScene ? "场景" : "预制体"); - EditorGUILayout.LabelField("行号", e.lineNumber.ToString()); - - EditorGUILayout.BeginHorizontal(); - if (GUILayout.Button("在Project窗口显示", GUILayout.Width(140))) - { - PingEntry(e); - } - - if (e.isInScene && GUILayout.Button("打开场景并选中", GUILayout.Width(140))) - { - OpenSceneAndSelect(e); - } - - if (GUILayout.Button("复制名称", GUILayout.Width(100))) - { - EditorGUIUtility.systemCopyBuffer = e.timelineName; - } - - EditorGUILayout.EndHorizontal(); - EditorGUILayout.EndVertical(); - } - } - } - - private bool PassFilter(TimelineNameCacheEntry entry) - { - if (!_showScenes && entry.isInScene) - return false; - if (!_showPrefabs && !entry.isInScene) - return false; - - if (_showConflictsOnly && !_conflictNames.Contains(entry.timelineName)) - return false; - - if (string.IsNullOrEmpty(_searchText)) - return true; - - var s = _searchText.Trim(); - if (s.Length == 0) - return true; - - var cmp = StringComparison.OrdinalIgnoreCase; - - if (!string.IsNullOrEmpty(entry.timelineName) && - entry.timelineName.Contains(s, cmp)) - return true; - - if (!string.IsNullOrEmpty(entry.assetPath) && - entry.assetPath.Contains(s, cmp)) - return true; - - if (!string.IsNullOrEmpty(entry.gameObjectPath) && - entry.gameObjectPath.Contains(s, cmp)) - return true; - - return false; - } - - private static string GetShortLocation(TimelineNameCacheEntry entry) - { - if (string.IsNullOrEmpty(entry.assetPath)) - return string.Empty; - - var fileName = Path.GetFileNameWithoutExtension(entry.assetPath); - - if (string.IsNullOrEmpty(entry.gameObjectPath)) - { - return fileName; - } - - return $"{fileName} / {entry.gameObjectPath}"; - } - - private static void PingEntry(TimelineNameCacheEntry entry) - { - if (string.IsNullOrEmpty(entry.assetPath)) - return; - - var obj = AssetDatabase.LoadMainAssetAtPath(entry.assetPath); - if (obj != null) - { - EditorGUIUtility.PingObject(obj); - Selection.activeObject = obj; - } - } - - private static void OpenSceneAndSelect(TimelineNameCacheEntry entry) - { - if (string.IsNullOrEmpty(entry.assetPath)) - return; - - if (!entry.isInScene) - return; - - if (!File.Exists(entry.assetPath)) - { - Debug.LogWarning($"场景文件不存在: {entry.assetPath}"); - return; - } - - var scene = EditorSceneManager.OpenScene(entry.assetPath, OpenSceneMode.Single); - if (!scene.IsValid()) - { - Debug.LogWarning($"无法打开场景: {entry.assetPath}"); - return; - } - - if (string.IsNullOrEmpty(entry.gameObjectPath)) - return; - - var parts = entry.gameObjectPath.Split('/'); - GameObject current = null; - - foreach (var part in parts) - { - if (current == null) - { - var roots = scene.GetRootGameObjects(); - current = Array.Find(roots, go => go.name == part); - } - else - { - var child = current.transform.Find(part); - current = child != null ? child.gameObject : null; - } - - if (current == null) - break; - } - - if (current != null) - { - Selection.activeGameObject = current; - EditorGUIUtility.PingObject(current); - } - else - { - Debug.LogWarning($"在场景 {scene.name} 中未找到路径: {entry.gameObjectPath}"); - } - } - - private void RefreshSelected() - { - LoadCacheIfNeeded(); - - var guids = Selection.assetGUIDs; - if (guids == null || guids.Length == 0) - { - EditorUtility.DisplayDialog("Timeline Name Collector", "请在 Project 窗口中先选中场景或预制体资源。", "确定"); - return; - } - - try - { - EditorUtility.DisplayProgressBar("Timeline Name Collector", "正在刷新选中资源...", 0f); - - var processed = 0; - foreach (var guid in guids) - { - var assetPath = AssetDatabase.GUIDToAssetPath(guid); - if (!IsSupportedAsset(assetPath)) - continue; - - ProcessSingleAsset(guid, assetPath); - - processed++; - EditorUtility.DisplayProgressBar( - "Timeline Name Collector", - $"正在解析: {assetPath}", - processed / (float)guids.Length); - } - } - finally - { - EditorUtility.ClearProgressBar(); - } - - _cache.CleanupDeletedAssets(); - _cache.Save(); - RebuildIndexes(); - Repaint(); - } - - private void RefreshAll(bool forceFull) - { - LoadCacheIfNeeded(); - - var sceneGuids = AssetDatabase.FindAssets("t:Scene"); - var prefabGuids = AssetDatabase.FindAssets("t:Prefab"); - - var allGuids = new HashSet(sceneGuids); - foreach (var g in prefabGuids) - allGuids.Add(g); - - var guidList = new List(allGuids); - guidList.Sort(); - - // 现有文件哈希,按 assetPath 去重 - var existingHashes = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var e in _cache.entries) - { - if (e == null || string.IsNullOrEmpty(e.assetPath) || string.IsNullOrEmpty(e.fileHash)) - continue; - - if (!existingHashes.ContainsKey(e.assetPath)) - { - existingHashes[e.assetPath] = e.fileHash; - } - } - - try - { - EditorUtility.DisplayProgressBar("Timeline Name Collector", - forceFull ? "正在强制全量扫描..." : "正在增量扫描...", 0f); - - for (var i = 0; i < guidList.Count; i++) - { - var guid = guidList[i]; - var assetPath = AssetDatabase.GUIDToAssetPath(guid); - if (!IsSupportedAsset(assetPath)) - continue; - - var fullPath = TimelineNameCache.GetAssetFullPath(assetPath); - if (string.IsNullOrEmpty(fullPath) || !File.Exists(fullPath)) - continue; - - var newHash = TimelineNameCache.ComputeFileHash(fullPath); - - var needScan = forceFull; - if (!needScan) - { - if (!existingHashes.TryGetValue(assetPath, out var oldHash) || - string.IsNullOrEmpty(newHash) || - !string.Equals(oldHash, newHash, StringComparison.OrdinalIgnoreCase)) - { - needScan = true; - } - } - - if (!needScan) - continue; - - ProcessSingleAsset(guid, assetPath, newHash); - - var progress = (i + 1) / (float)guidList.Count; - EditorUtility.DisplayProgressBar("Timeline Name Collector", - $"正在解析: {assetPath}", progress); - } - } - finally - { - EditorUtility.ClearProgressBar(); - } - - _cache.CleanupDeletedAssets(); - _cache.SetLastFullScanNow(); - _cache.Save(); - RebuildIndexes(); - Repaint(); - } - - private void ProcessSingleAsset(string guid, string assetPath, string precomputedHash = null) - { - var fullPath = TimelineNameCache.GetAssetFullPath(assetPath); - if (string.IsNullOrEmpty(fullPath) || !File.Exists(fullPath)) - return; - - var fileHash = precomputedHash ?? TimelineNameCache.ComputeFileHash(fullPath); - var lastModifiedUtc = File.GetLastWriteTimeUtc(fullPath).ToString("o"); - - _cache.RemoveEntriesForAsset(assetPath); - - var entries = YamlTimelineParser.ParseAsset( - assetPath, - guid, - fileHash, - lastModifiedUtc); - - if (entries != null && entries.Count > 0) - { - _cache.entries.AddRange(entries); - } - } - - private static bool IsSupportedAsset(string assetPath) - { - if (string.IsNullOrEmpty(assetPath)) - return false; - - var ext = Path.GetExtension(assetPath); - if (string.IsNullOrEmpty(ext)) - return false; - - ext = ext.ToLowerInvariant(); - return ext == ".unity" || ext == ".prefab"; - } - - private static string FormatTime(string isoTime) - { - if (string.IsNullOrEmpty(isoTime)) - return "-"; - - if (DateTime.TryParse(isoTime, out var dt)) - { - return dt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); - } - - return isoTime; - } - } -} - diff --git a/Assets/Editor/TimelineNameCollector/TimelineNameCollectorWindow.cs.meta b/Assets/Editor/TimelineNameCollector/TimelineNameCollectorWindow.cs.meta deleted file mode 100644 index b63134680..000000000 --- a/Assets/Editor/TimelineNameCollector/TimelineNameCollectorWindow.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6a43798aefb6a4142a7abe8d3dc1649a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Editor/TimelineNameCollector/YamlTimelineParser.cs b/Assets/Editor/TimelineNameCollector/YamlTimelineParser.cs deleted file mode 100644 index 8753e7adf..000000000 --- a/Assets/Editor/TimelineNameCollector/YamlTimelineParser.cs +++ /dev/null @@ -1,375 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.RegularExpressions; -using UnityEditor; -using UnityEngine; - -namespace AibisDream.EditorTools -{ - /// - /// 解析 Unity YAML 场景 / 预制体中的 DirectorHandler 组件,提取 timelineName 和 GameObject 路径。 - /// 解析方式:按 "--- !u! &" 分段,构建 GameObject / Transform / MonoBehaviour 的索引。 - /// - public static class YamlTimelineParser - { - // 来自 Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs.meta - public const string DirectorHandlerScriptGuid = "07d657d6b80509b4eb04f59afaa9aa2d"; - - private const int ClassIdGameObject = 1; - private const int ClassIdTransform = 4; - private const int ClassIdMonoBehaviour = 114; - - private static readonly Regex HeaderRegex = - new Regex(@"^--- !u!(\d+) &(\d+)", RegexOptions.Compiled); - - private static readonly Regex FileIdRegex = - new Regex(@"fileID:\s*(\d+)", RegexOptions.Compiled); - - private class YamlObject - { - public int classId; - public long fileId; - public int startLine; - public int endLine; - } - - private class TransformInfo - { - public long transformFileId; - public long parentTransformFileId; - public long gameObjectFileId; - } - - public static List ParseAsset( - string assetPath, - string guid, - string fileHash, - string lastModifiedUtc) - { - var result = new List(); - - if (string.IsNullOrEmpty(assetPath)) - return result; - - var fullPath = TimelineNameCache.GetAssetFullPath(assetPath); - if (string.IsNullOrEmpty(fullPath) || !File.Exists(fullPath)) - return result; - - string[] lines; - try - { - lines = File.ReadAllLines(fullPath); - } - catch (Exception e) - { - Debug.LogError($"[YamlTimelineParser] Failed to read '{fullPath}': {e}"); - return result; - } - - if (lines.Length == 0 || !lines[0].StartsWith("%YAML", StringComparison.Ordinal)) - { - // 非 text 序列化,无法解析 - return result; - } - - var objects = BuildObjects(lines); - if (objects.Count == 0) - return result; - - var gameObjectNames = new Dictionary(); - var transforms = new Dictionary(); - var transformByGameObject = new Dictionary(); - - foreach (var obj in objects) - { - switch (obj.classId) - { - case ClassIdGameObject: - var name = ExtractGameObjectName(lines, obj); - if (!string.IsNullOrEmpty(name)) - { - gameObjectNames[obj.fileId] = name; - } - break; - - case ClassIdTransform: - var tInfo = ExtractTransformInfo(lines, obj); - if (tInfo != null) - { - transforms[tInfo.transformFileId] = tInfo; - if (tInfo.gameObjectFileId != 0 && - !transformByGameObject.ContainsKey(tInfo.gameObjectFileId)) - { - transformByGameObject[tInfo.gameObjectFileId] = tInfo.transformFileId; - } - } - break; - } - } - - foreach (var obj in objects) - { - if (obj.classId != ClassIdMonoBehaviour) - continue; - - if (!IsDirectorHandler(lines, obj)) - continue; - - var monoInfo = ExtractDirectorHandlerInfo( - lines, - obj, - assetPath, - guid, - fileHash, - lastModifiedUtc, - gameObjectNames, - transforms, - transformByGameObject); - - if (monoInfo != null) - { - result.Add(monoInfo); - } - } - - return result; - } - - private static List BuildObjects(string[] lines) - { - var objects = new List(); - YamlObject current = null; - - for (var i = 0; i < lines.Length; i++) - { - var line = lines[i]; - var match = HeaderRegex.Match(line); - if (!match.Success) - continue; - - if (current != null) - { - current.endLine = i - 1; - objects.Add(current); - } - - if (!int.TryParse(match.Groups[1].Value, out var classId)) - continue; - - if (!long.TryParse(match.Groups[2].Value, out var fileId)) - continue; - - current = new YamlObject - { - classId = classId, - fileId = fileId, - startLine = i, - endLine = i - }; - } - - if (current != null) - { - current.endLine = lines.Length - 1; - objects.Add(current); - } - - return objects; - } - - private static string ExtractGameObjectName(string[] lines, YamlObject obj) - { - for (var i = obj.startLine; i <= obj.endLine; i++) - { - var line = lines[i]; - var trimmed = line.TrimStart(); - if (!trimmed.StartsWith("m_Name:", StringComparison.Ordinal)) - continue; - - var idx = trimmed.IndexOf(':'); - if (idx < 0 || idx + 1 >= trimmed.Length) - break; - - var name = trimmed.Substring(idx + 1).Trim(); - name = name.Trim('"'); - return UnescapeUnityYamlString(name); - } - - return null; - } - - private static TransformInfo ExtractTransformInfo(string[] lines, YamlObject obj) - { - long gameObjectFileId = 0; - long parentTransformFileId = 0; - - for (var i = obj.startLine; i <= obj.endLine; i++) - { - var line = lines[i].TrimStart(); - - if (line.StartsWith("m_GameObject:", StringComparison.Ordinal)) - { - var match = FileIdRegex.Match(line); - if (match.Success && long.TryParse(match.Groups[1].Value, out var id)) - { - gameObjectFileId = id; - } - } - else if (line.StartsWith("m_Father:", StringComparison.Ordinal)) - { - var match = FileIdRegex.Match(line); - if (match.Success && long.TryParse(match.Groups[1].Value, out var id)) - { - parentTransformFileId = id; - } - } - } - - if (gameObjectFileId == 0) - return null; - - return new TransformInfo - { - transformFileId = obj.fileId, - parentTransformFileId = parentTransformFileId, - gameObjectFileId = gameObjectFileId - }; - } - - private static bool IsDirectorHandler(string[] lines, YamlObject obj) - { - for (var i = obj.startLine; i <= obj.endLine; i++) - { - var line = lines[i].TrimStart(); - if (!line.StartsWith("m_Script:", StringComparison.Ordinal)) - continue; - - if (line.Contains(DirectorHandlerScriptGuid, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } - - private static TimelineNameCacheEntry ExtractDirectorHandlerInfo( - string[] lines, - YamlObject obj, - string assetPath, - string guid, - string fileHash, - string lastModifiedUtc, - Dictionary gameObjectNames, - Dictionary transforms, - Dictionary transformByGameObject) - { - long gameObjectFileId = 0; - string timelineName = null; - int timelineNameLine = -1; - - for (var i = obj.startLine; i <= obj.endLine; i++) - { - var rawLine = lines[i]; - var line = rawLine.TrimStart(); - - if (line.StartsWith("m_GameObject:", StringComparison.Ordinal)) - { - var match = FileIdRegex.Match(line); - if (match.Success && long.TryParse(match.Groups[1].Value, out var id)) - { - gameObjectFileId = id; - } - } - else if (line.StartsWith("timelineName:", StringComparison.Ordinal)) - { - var idx = line.IndexOf(':'); - if (idx >= 0 && idx + 1 < line.Length) - { - var value = line.Substring(idx + 1).Trim(); - value = value.Trim('"'); - value = value.Trim('\''); - timelineName = UnescapeUnityYamlString(value); - timelineNameLine = i + 1; // 转为 1-based - } - } - } - - if (string.IsNullOrEmpty(timelineName)) - { - // 没有设置 timelineName 的组件不计入 - return null; - } - - var isInScene = assetPath.EndsWith(".unity", StringComparison.OrdinalIgnoreCase); - var goPath = BuildGameObjectPath(gameObjectFileId, gameObjectNames, transforms, transformByGameObject); - - return new TimelineNameCacheEntry - { - guid = guid, - assetPath = assetPath, - fileHash = fileHash, - lastModified = lastModifiedUtc, - timelineName = timelineName, - gameObjectPath = goPath, - isInScene = isInScene, - lineNumber = timelineNameLine > 0 ? timelineNameLine : obj.startLine + 1 - }; - } - - private static string BuildGameObjectPath( - long gameObjectFileId, - Dictionary gameObjectNames, - Dictionary transforms, - Dictionary transformByGameObject) - { - if (gameObjectFileId == 0) - return string.Empty; - - if (!transformByGameObject.TryGetValue(gameObjectFileId, out var transformId)) - { - gameObjectNames.TryGetValue(gameObjectFileId, out var nameOnly); - return nameOnly ?? string.Empty; - } - - var segments = new List(); - var currentTransformId = transformId; - var safety = 0; - - while (currentTransformId != 0 && safety++ < 256) - { - if (!transforms.TryGetValue(currentTransformId, out var tInfo)) - break; - - if (!gameObjectNames.TryGetValue(tInfo.gameObjectFileId, out var name)) - { - name = "GameObject"; - } - - segments.Add(name); - currentTransformId = tInfo.parentTransformFileId; - } - - segments.Reverse(); - return string.Join("/", segments); - } - - /// - /// Unity YAML 中的字符串可能将非 ASCII 字符存储为 \uXXXX 转义,需要解码为实际 Unicode 字符。 - /// - private static string UnescapeUnityYamlString(string value) - { - if (string.IsNullOrEmpty(value)) return value; - try - { - return Regex.Unescape(value); - } - catch - { - return value; - } - } - } -} - diff --git a/Assets/Editor/TimelineNameCollector/YamlTimelineParser.cs.meta b/Assets/Editor/TimelineNameCollector/YamlTimelineParser.cs.meta deleted file mode 100644 index fbb7a24f4..000000000 --- a/Assets/Editor/TimelineNameCollector/YamlTimelineParser.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 19f28acc911bf7d4e86035daebc33190 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: