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}");
}
}
}
}