using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace BaseGames.Editor { /// /// 一键应用/校验项目推荐的 Script Execution Order。 /// public static class ScriptExecutionOrderTools { private readonly struct OrderRule { public readonly string ClassName; public readonly int Order; public OrderRule(string className, int order) { ClassName = className; Order = order; } } private static readonly OrderRule[] Rules = { new OrderRule("GameServiceRegistrar", -2000), new OrderRule("GameManager", -1000), new OrderRule("SceneService", -900), new OrderRule("GameSaveManager", -900), new OrderRule("AudioManager", -500), new OrderRule("PlayerController", -100), }; [MenuItem("BaseGames/Tools/Validation/Apply Script Execution Order Preset")] public static void ApplyPreset() { int updated = 0; int skipped = 0; var issues = new List(); foreach (var rule in Rules) { if (!TryFindMonoScript(rule.ClassName, out MonoScript script, out string issue)) { skipped++; issues.Add(issue); continue; } int current = MonoImporter.GetExecutionOrder(script); if (current == rule.Order) continue; MonoImporter.SetExecutionOrder(script, rule.Order); updated++; } AssetDatabase.SaveAssets(); if (issues.Count > 0) { Debug.LogWarning( "[ScriptExecutionOrderTools] 已应用执行顺序预设(部分脚本未处理)。\n" + $"更新: {updated}, 跳过: {skipped}\n- {string.Join("\n- ", issues)}"); return; } Debug.Log($"[ScriptExecutionOrderTools] 执行顺序预设应用完成。更新数量: {updated}。"); } [MenuItem("BaseGames/Tools/Validation/Validate Script Execution Order Preset")] public static void ValidatePreset() { var mismatches = new List(); var issues = new List(); foreach (var rule in Rules) { if (!TryFindMonoScript(rule.ClassName, out MonoScript script, out string issue)) { issues.Add(issue); continue; } int current = MonoImporter.GetExecutionOrder(script); if (current != rule.Order) mismatches.Add($"{rule.ClassName}: 当前 {current}, 期望 {rule.Order}"); } if (mismatches.Count == 0 && issues.Count == 0) { Debug.Log("[ScriptExecutionOrderTools] 执行顺序校验通过,所有脚本均符合预设。"); return; } string message = "[ScriptExecutionOrderTools] 执行顺序校验发现问题。"; if (mismatches.Count > 0) message += "\n顺序不一致:\n- " + string.Join("\n- ", mismatches); if (issues.Count > 0) message += "\n脚本解析问题:\n- " + string.Join("\n- ", issues); Debug.LogWarning(message); } /// /// 根据 查找 MonoScript。 /// 当 className 包含 '.'(全限定名)时,用 type.FullName 精确匹配; /// 否则用 type.Name 匹配(向后兼容简单类名)。 /// private static bool TryFindMonoScript(string className, out MonoScript script, out string issue) { script = null; issue = null; // 全限定名时,FindAssets 只取最后一段(简单类名)作为搜索词 bool useFullName = className.Contains('.'); string searchName = useFullName ? className.Substring(className.LastIndexOf('.') + 1) : className; string[] guids = AssetDatabase.FindAssets($"{searchName} t:MonoScript"); var matches = new List(); foreach (string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); var candidate = AssetDatabase.LoadAssetAtPath(path); if (candidate == null) continue; Type type = candidate.GetClass(); if (type == null) continue; bool nameMatch = useFullName ? type.FullName == className : type.Name == className; if (nameMatch) matches.Add(candidate); } if (matches.Count == 0) { issue = $"未找到脚本: {className}"; return false; } if (matches.Count > 1) { issue = $"存在多个同名脚本: {className}(请消歧后重试)"; return false; } script = matches[0]; return true; } } }