using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; using UnityEngine; namespace BaseGames.Editor { /// /// 扫描项目中所有实现 接口的 ScriptableObject, /// 调用 Validate() 并在 Console 报告验证结果。同时作为构建前处理器,发现错误时中止构建。 /// /// 菜单:Tools/Validate All ScriptableObjects /// Build 回调顺序 = 1(在 AddressKeyValidator callbackOrder = 0 之后执行) /// public class SOValidationRunner : IPreprocessBuildWithReport { public int callbackOrder => 1; public void OnPreprocessBuild(BuildReport report) { var (errors, warnings) = RunAll(); foreach (var w in warnings) Debug.LogWarning(w); if (errors.Count > 0) throw new BuildFailedException( $"[SOValidationRunner] {errors.Count} 处 SO 数据错误,构建中止:\n" + string.Join("\n", errors)); } [MenuItem("Tools/Validate All ScriptableObjects")] public static void ValidateMenu() { var (errors, warnings) = RunAll(); if (errors.Count == 0 && warnings.Count == 0) { Debug.Log("[SOValidationRunner] ✅ 所有 SO 数据均合法。"); return; } foreach (var w in warnings) Debug.LogWarning(w); foreach (var e in errors) Debug.LogError(e); Debug.Log($"[SOValidationRunner] 校验完成:{errors.Count} 错误,{warnings.Count} 警告。"); } // ── Internal ────────────────────────────────────────────────────── private static (List errors, List warnings) RunAll() { var errors = new List(); var warnings = new List(); var guids = AssetDatabase.FindAssets("t:ScriptableObject"); foreach (var guid in guids) { var path = AssetDatabase.GUIDToAssetPath(guid); var so = AssetDatabase.LoadAssetAtPath(path); if (so is BaseGames.Core.IValidatable validatable) { foreach (var result in validatable.Validate()) { if (result.Severity == BaseGames.Core.ValidationSeverity.Error) errors.Add($"\u274c {result.Message} ({path})"); else warnings.Add($"\u26a0\ufe0f {result.Message} ({path})"); } } } return (errors, warnings); } } }