using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.IO; using System.Linq; using Newtonsoft.Json.Linq; namespace PictorialEditor { /// /// 画报角色ID验证工具 /// 扫描 ActivityPicturialData 目录下的所有 JSON 文件,检查角色 ID 是否有效 /// public class PictorialCharacterIdValidator : EditorWindow { private const string PICTORIAL_DATA_FOLDER = "Assets/Config/Data/ActivityPicturialData"; private const string CHARACTER_CONFIG_PATH = "Assets/Config/Data/charactercfg_characterattri.json"; private Vector2 scrollPosition; private List validationErrors = new List(); private bool isScanning = false; private string statusMessage = "点击「开始扫描」按钮开始验证"; private HashSet validCharacterIds = new HashSet(); private HashSet validNameIds = new HashSet(); // 角色名称ID(如 A00001) [System.Serializable] private class ValidationError { public string fileName; public string itemName; public string characterId; public string errorMessage; public int lineNumber; // JSON 中的大致行号 } [MenuItem("Tools/画报工具/角色ID验证工具")] public static void ShowWindow() { var window = GetWindow("画报角色ID验证"); window.minSize = new Vector2(600, 400); window.Show(); } private void OnGUI() { EditorGUILayout.Space(10); EditorGUILayout.LabelField("画报角色ID验证工具", EditorStyles.boldLabel); EditorGUILayout.HelpBox( "此工具会:\n" + "1. 加载角色配置表 (charactercfg_characterattri.json)\n" + "2. 扫描 ActivityPicturialData 目录下的所有画报 JSON 文件\n" + "3. 验证画报中的 characterId 是否有效(格式正确 + 存在于配置表中)", MessageType.Info ); EditorGUILayout.Space(10); // 扫描按钮 EditorGUI.BeginDisabledGroup(isScanning); if (GUILayout.Button("开始扫描", GUILayout.Height(40))) { StartScan(); } EditorGUI.EndDisabledGroup(); EditorGUILayout.Space(5); // 状态信息 EditorGUILayout.LabelField("状态:", EditorStyles.boldLabel); EditorGUILayout.LabelField(statusMessage); EditorGUILayout.Space(10); // 显示验证结果 if (validationErrors.Count > 0) { EditorGUILayout.LabelField($"发现 {validationErrors.Count} 个问题:", EditorStyles.boldLabel); EditorGUILayout.Space(5); scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); foreach (var error in validationErrors) { EditorGUILayout.BeginVertical("box"); // 文件名 EditorGUILayout.LabelField("文件:", EditorStyles.boldLabel); EditorGUILayout.SelectableLabel(error.fileName, GUILayout.Height(18)); // 元素名称 EditorGUILayout.LabelField("元素名称:", EditorStyles.boldLabel); EditorGUILayout.SelectableLabel(error.itemName, GUILayout.Height(18)); // 角色ID EditorGUILayout.LabelField("角色ID:", EditorStyles.boldLabel); EditorGUILayout.SelectableLabel(error.characterId, GUILayout.Height(18)); // 错误信息 EditorGUILayout.LabelField("问题:", EditorStyles.boldLabel); EditorGUILayout.HelpBox(error.errorMessage, MessageType.Error); // 打开文件按钮 if (GUILayout.Button("打开 JSON 文件", GUILayout.Height(25))) { OpenJsonFile(error.fileName); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(5); } EditorGUILayout.EndScrollView(); } else if (!isScanning && validationErrors.Count == 0 && statusMessage.Contains("扫描完成")) { EditorGUILayout.HelpBox("✅ 所有画报的角色ID都是有效的!", MessageType.Info); } } private void StartScan() { isScanning = true; validationErrors.Clear(); statusMessage = "正在加载角色配置表..."; Repaint(); try { // 加载角色配置表 if (!LoadCharacterConfig()) { statusMessage = "加载角色配置表失败,请检查文件路径"; return; } statusMessage = $"角色配置表加载成功,共 {validCharacterIds.Count} 个角色ID,{validNameIds.Count} 个名称ID。正在扫描画报文件..."; Repaint(); ScanPictorialJsonFiles(); if (validationErrors.Count > 0) { statusMessage = $"扫描完成,发现 {validationErrors.Count} 个问题"; } else { statusMessage = "扫描完成,所有角色ID都有效!"; } } catch (System.Exception e) { statusMessage = $"扫描出错: {e.Message}"; Debug.LogError($"[画报角色ID验证] 扫描出错: {e.Message}\n{e.StackTrace}"); } finally { isScanning = false; Repaint(); } } /// /// 加载角色配置表,获取所有有效的角色ID /// private bool LoadCharacterConfig() { validCharacterIds.Clear(); validNameIds.Clear(); if (!File.Exists(CHARACTER_CONFIG_PATH)) { Debug.LogError($"[画报角色ID验证] 角色配置文件不存在: {CHARACTER_CONFIG_PATH}"); return false; } try { string jsonContent = File.ReadAllText(CHARACTER_CONFIG_PATH); JArray jsonArray = JArray.Parse(jsonContent); if (jsonArray == null) { Debug.LogError($"[画报角色ID验证] 无法解析角色配置文件"); return false; } // 解析角色配置,提取所有角色ID和名称ID // 配置格式: [{"RoleID": 100001, "NameID": "A00001", ...}, ...] for (int i = 0; i < jsonArray.Count; i++) { JObject item = jsonArray[i] as JObject; if (item == null) continue; // 提取 RoleID (数字ID) int roleId = item["RoleID"]?.Value() ?? 0; if (roleId > 0) { validCharacterIds.Add(roleId); } // 提取 NameID (字符串ID,如 A00001) string nameId = item["NameID"]?.Value(); if (!string.IsNullOrEmpty(nameId)) { validNameIds.Add(nameId); } } Debug.Log($"[画报角色ID验证] 成功加载 {validCharacterIds.Count} 个角色ID,{validNameIds.Count} 个名称ID"); return validCharacterIds.Count > 0 && validNameIds.Count > 0; } catch (System.Exception e) { Debug.LogError($"[画报角色ID验证] 加载角色配置表出错: {e.Message}\n{e.StackTrace}"); return false; } } private void ScanPictorialJsonFiles() { if (!Directory.Exists(PICTORIAL_DATA_FOLDER)) { statusMessage = $"目录不存在: {PICTORIAL_DATA_FOLDER}"; return; } string[] jsonFiles = Directory.GetFiles(PICTORIAL_DATA_FOLDER, "*.json", SearchOption.TopDirectoryOnly); if (jsonFiles.Length == 0) { statusMessage = "未找到任何 JSON 文件"; return; } Debug.Log($"[画报角色ID验证] 开始扫描 {jsonFiles.Length} 个文件"); foreach (string jsonFile in jsonFiles) { ValidateJsonFile(jsonFile); } } private void ValidateJsonFile(string filePath) { string fileName = Path.GetFileName(filePath); try { string jsonContent = File.ReadAllText(filePath); JObject jsonObj = JObject.Parse(jsonContent); // 获取 items 数组 JArray items = jsonObj["items"] as JArray; if (items == null) { Debug.LogWarning($"[画报角色ID验证] {fileName} 中没有 items 数组"); return; } // 遍历每个元素 for (int i = 0; i < items.Count; i++) { JObject item = items[i] as JObject; if (item == null) continue; string itemName = item["itemName"]?.ToString() ?? $"Item_{i}"; string characterId = item["characterId"]?.ToString(); // 如果没有 characterId 或为空,跳过(这是正常的) if (string.IsNullOrEmpty(characterId)) continue; // 验证角色ID string errorMessage = ValidateCharacterId(characterId); if (!string.IsNullOrEmpty(errorMessage)) { validationErrors.Add(new ValidationError { fileName = fileName, itemName = itemName, characterId = characterId, errorMessage = errorMessage, lineNumber = EstimateLineNumber(jsonContent, itemName) }); } } } catch (System.Exception e) { Debug.LogError($"[画报角色ID验证] 解析文件失败: {fileName}, 错误: {e.Message}"); } } /// /// 验证角色ID是否有效 /// private string ValidateCharacterId(string characterId) { // 检查格式 if (string.IsNullOrWhiteSpace(characterId)) { return "角色ID为空"; } // 情况1: NameID 格式(如 "A00001", "A00012") if (characterId.StartsWith("A") && characterId.Length == 6) { // 直接在 validNameIds 中查找 if (validNameIds.Contains(characterId)) { return null; // 验证通过 } else { return $"角色名称ID不存在于配置表中: {characterId}"; } } // 情况2: RoleID 格式(纯数字或 A+数字) int numericId = 0; if (characterId.StartsWith("A") && characterId.Length > 6) { // "A100001" 格式 string numberPart = characterId.Substring(1); if (!int.TryParse(numberPart, out numericId)) { return $"无效的角色ID格式(无法解析数字部分): {characterId}"; } } else if (!characterId.StartsWith("A")) { // 纯数字格式 if (!int.TryParse(characterId, out numericId)) { return $"无效的角色ID格式(不是数字也不是A开头): {characterId}"; } // 如果小于 100000,补齐 if (numericId < 100000) numericId += 100000; } else { return $"无法识别的角色ID格式: {characterId}"; } // 检查角色ID范围 if (numericId < 100000 || numericId > 999999) { return $"角色ID超出有效范围 (100000-999999): {characterId} (解析后: {numericId})"; } // 检查角色ID是否存在于配置表中 if (!validCharacterIds.Contains(numericId)) { return $"角色ID不存在于配置表中: {characterId} (解析后: {numericId})"; } return null; // 验证通过 } /// /// 估算元素在 JSON 文件中的行号(用于定位) /// private int EstimateLineNumber(string jsonContent, string itemName) { string[] lines = jsonContent.Split('\n'); for (int i = 0; i < lines.Length; i++) { if (lines[i].Contains($"\"itemName\": \"{itemName}\"")) { return i + 1; // 行号从1开始 } } return -1; } /// /// 在外部编辑器中打开 JSON 文件 /// private void OpenJsonFile(string fileName) { string fullPath = Path.Combine(PICTORIAL_DATA_FOLDER, fileName); if (File.Exists(fullPath)) { System.Diagnostics.Process.Start(fullPath); } else { Debug.LogError($"[画报角色ID验证] 文件不存在: {fullPath}"); } } } }