using System; using System.Collections.Generic; using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Script.RuntimeScript.model.Data; using UnityEditor; using UnityEngine; public class SelectedJsonCoordinateChecker { // 右键菜单:在选中的JSON文件上点击"检查AllTempJson重复坐标" [MenuItem("Assets/检查AllTempJson重复坐标", false, 100)] public static void CheckSelectedJsonFile() { // 获取选中的文件 string selectedFilePath = AssetDatabase.GetAssetPath(Selection.activeObject); // 校验文件是否为JSON if (string.IsNullOrEmpty(selectedFilePath) || !selectedFilePath.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) { Debug.LogError("请选中一个JSON文件!"); return; } // 转换为绝对路径 string fullPath = Path.Combine(Application.dataPath, selectedFilePath.Replace("Assets/", "")); if (!File.Exists(fullPath)) { Debug.LogError($"文件不存在:{fullPath}"); return; } // 执行检查逻辑 CheckCoordinatesInFile(fullPath); } // 校验菜单是否可用(仅选中JSON文件时可点击) [MenuItem("Assets/检查AllTempJson重复坐标", true)] public static bool ValidateCheckSelectedJsonFile() { string path = AssetDatabase.GetAssetPath(Selection.activeObject); return !string.IsNullOrEmpty(path) && path.EndsWith(".json", StringComparison.OrdinalIgnoreCase); } // 核心检查逻辑 private static void CheckCoordinatesInFile(string fullPath) { try { // 读取JSON内容 string jsonContent = File.ReadAllText(fullPath); JObject rootObj = JObject.Parse(jsonContent); // 提取AllTempJson字段 JToken allTempToken = rootObj["AllTempJson"]; if (allTempToken == null || string.IsNullOrEmpty(allTempToken.ToString())) { Debug.Log("文件中未找到AllTempJson字段或字段为空"); return; } List> _allPropTempData = JsonConvert.DeserializeObject>>(allTempToken.ToString()); // 解析AllTempJson(二维列表结构) List ints = new List(); int index = 0; // 遍历所有PropTemp,提取坐标 foreach (List tempList in _allPropTempData) { index++; Dictionary coordCountDict = new Dictionary(); List duplicateCoords = new List(); int[,] ints1 = new int[10, 10]; ints.Add(ints1); int index1 = 0; foreach (PropTempData propTemp in tempList) { var TempArr = propTemp.ConfigData.GetArray(); int rows = TempArr.GetLength(0); int cols = TempArr.GetLength(1); // 起始 Vector3 startPoint = new Vector3(propTemp.x, propTemp.y, 0); index1++; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { int value = TempArr[i, j]; if (value > 0) { // 初始位置 Vector3 positionB = new Vector3(j, i, 0); //空间转换 // DiggingGameManager.LogError($"positionB::row:: {i}" + " col::" + j + " data.PropDirection::" + data.PropDirection); Vector3 endPosition = startPoint + GetRotatePoint(propTemp.ConfigData.PropDirection, Vector3.zero, positionB); ints1[(int)Math.Round(endPosition.x, MidpointRounding.AwayFromZero), (int)Math.Round(endPosition.y, MidpointRounding.AwayFromZero)] = index1; string coordKey = $"({endPosition.x},{endPosition.y})"; // 统计次数 if (coordCountDict.ContainsKey(coordKey)) { coordCountDict[coordKey]++; if (coordCountDict[coordKey] == 2) { duplicateCoords.Add(coordKey); } } else { coordCountDict[coordKey] = 1; } } } } } // 输出结果 Debug.Log($"检查文件:{fullPath}++{index}"); if (duplicateCoords.Count == 0) { Debug.Log("✅ AllTempJson 中无重复坐标"); } else { Debug.LogWarning($"⚠️{index} 发现{duplicateCoords.Count}个重复坐标:"); foreach (var coord in duplicateCoords) { Debug.LogWarning($"- 坐标 {coord} 重复出现 {coordCountDict[coord]} 次"); } } } // 生成输出文件路径(与原JSON文件同目录) string outputDir = Path.GetDirectoryName(Application.dataPath); string outputFileName = Path.GetFileNameWithoutExtension(fullPath) + "_CoordNumbers.csv"; string outputPath = Path.Combine(outputDir, outputFileName); // 调用绘制方法 //DrawAllIntsToSingleFile(ints, outputPath); ExportIntsToCsv(ints, outputPath); // 输出结果 Debug.Log($"检查文件:{fullPath}"); } catch (Exception e) { Debug.LogError($"检查失败:{e.Message}\n堆栈:{e.StackTrace}"); } } private static Vector3 GetRotatePoint(int angles, Vector3 positionA, Vector3 positionB) { return RotatePointAroundPivot(positionB, positionA, new Vector3(0, 0, -angles)); } private static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 angles) { Vector3 dir = point - pivot; // 获取点到枢轴的向量 dir = Quaternion.Euler(angles) * dir; // 旋转向量 point = dir + pivot; // 将点移动回枢轴 return point; } // 新增:将所有int[,]导出到CSV表格 private static void ExportIntsToCsv(List ints, string outputFilePath) { if (ints == null || ints.Count == 0) { Debug.LogWarning("ints列表为空,无法导出"); return; } try { // 1. 确定所有数组的最大X/Y范围(统一表格尺寸) int maxX = 0; int maxY = 0; foreach (var arr in ints) { maxX = Mathf.Max(maxX, arr.GetLength(0)); maxY = Mathf.Max(maxY, arr.GetLength(1)); } // 2. 准备CSV内容(首行为表头:分组索引+X坐标) List csvLines = new List(); // 3. 填充表格内容(每行对应一个Y坐标) // 遍历每个分组的当前Y行数据 for (int groupIdx = 0; groupIdx < ints.Count; groupIdx++) { int[,] arr = ints[groupIdx]; int xLength = arr.GetLength(0); for (int y = 0; y < maxY; y++) { List row = new List(); row.Add(y.ToString()); // 第一列:Y坐标 for (int x = 0; x < maxX; x++) { // 数组范围内且有值则显示,否则留空 if (x < xLength && y < arr.GetLength(1)) { int value = arr[x, y]; row.Add(value == 0 ? "" : value.ToString()); } else { row.Add(""); } } csvLines.Add(string.Join(",", row)); } csvLines.Add(""); // 分组间空行分隔 } // 4. 保存CSV文件 string directory = Path.GetDirectoryName(outputFilePath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.WriteAllLines(outputFilePath, csvLines, System.Text.Encoding.UTF8); // 5. 提示并添加Excel格式说明 Debug.Log($"坐标数据已导出到CSV表格:{outputFilePath}"); Debug.Log("提示:用Excel打开后,可通过「条件格式」→「数据条/色阶」为数值添加颜色标记"); EditorUtility.RevealInFinder(outputFilePath); } catch (Exception e) { Debug.LogError($"导出CSV失败:{e.Message}"); } } private static void DrawAllIntsToSingleFile(List ints, string outputFilePath) { if (ints == null || ints.Count == 0) { Debug.LogWarning("ints列表为空,无法绘制"); return; } try { List allTextLines = new List(); allTextLines.Add("===== 所有分组坐标数字汇总图 ====="); allTextLines.Add($"总分组数:{ints.Count} | 格式:每个分组独立区块,空格=无数据,数字/字母=坐标值"); allTextLines.Add("".PadRight(80, '-')); // 分隔线 // 遍历每个int[,],生成区块并添加到总文本 for (int i = 0; i < ints.Count; i++) { int[,] intArray = ints[i]; int groupIndex = i + 1; // 分组索引从1开始 if (intArray == null) { allTextLines.Add($"【分组 {groupIndex}】数组为空"); allTextLines.Add("".PadRight(80, '-')); continue; } // 1. 获取当前数组维度 int xLength = intArray.GetLength(0); int yLength = intArray.GetLength(1); // 2. 生成当前分组的文本区块 List groupLines = new List(); groupLines.Add($"【分组 {groupIndex}】X范围[0~{xLength - 1}] | Y范围[0~{yLength - 1}]"); // 3. 构建坐标网格 char[][] textGrid = new char[yLength][]; for (int y = 0; y < yLength; y++) { textGrid[y] = new char[xLength]; Array.Fill(textGrid[y], ' '); } // 4. 填充数组数据 for (int x = 0; x < xLength; x++) { for (int y = 0; y < yLength; y++) { int value = intArray[x, y]; if (value == 0) continue; textGrid[y][x] = value <= 9 ? (char)('0' + value) : (char)('A' + (value - 10)); } } // 5. 添加带Y轴标签的网格行 for (int y = 0; y < yLength; y++) { groupLines.Add($"Y={y,2} | " + new string(textGrid[y])); } // 6. 添加X轴标签 string xLabel = " | "; // 对齐Y轴标签 for (int x = 0; x < xLength; x++) { xLabel += $"{x,2}"; } groupLines.Add(xLabel); // 7. 将当前分组区块添加到总文本,并添加分隔线 allTextLines.AddRange(groupLines); allTextLines.Add("".PadRight(80, '-')); } // 8. 保存到单个文件 string directory = Path.GetDirectoryName(outputFilePath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.WriteAllLines(outputFilePath, allTextLines); Debug.Log($"所有分组坐标已汇总绘制到:{outputFilePath}"); EditorUtility.RevealInFinder(outputFilePath); } catch (Exception e) { Debug.LogError($"绘制汇总文件失败:{e.Message}"); } } // 右键菜单:在选中的文件夹上点击"检查所有JSON的坐标重复" [MenuItem("Assets/检查文件夹下所有JSON的坐标重复", false, 101)] public static void CheckAllJsonInSelectedFolder() { // 获取选中的文件夹路径 string selectedFolderPath = AssetDatabase.GetAssetPath(Selection.activeObject); if (string.IsNullOrEmpty(selectedFolderPath) || !Directory.Exists(selectedFolderPath)) { Debug.LogError("请选中一个有效的文件夹!"); return; } // 查找文件夹下所有JSON文件(包括子文件夹) string[] jsonFilePaths = Directory.GetFiles(selectedFolderPath, "*.json", SearchOption.AllDirectories); if (jsonFilePaths.Length == 0) { Debug.Log("文件夹下未找到任何JSON文件"); return; } // 全局坐标统计(记录所有文件中的坐标及出现位置) Dictionary> globalCoordRecord = new Dictionary>(); // 遍历所有JSON文件 foreach (string jsonPath in jsonFilePaths) { try { // 读取文件内容 string fullPath = Path.GetFullPath(jsonPath); if (!fullPath.Contains("Clamp")) { continue; } string jsonContent = File.ReadAllText(fullPath); JObject rootObj = JObject.Parse(jsonContent); // 提取AllTempJson字段 JToken allTempToken = rootObj["AllTempJson"]; if (allTempToken == null || string.IsNullOrEmpty(allTempToken.ToString())) { continue; // 跳过无AllTempJson的文件 } List> _allPropTempData = JsonConvert.DeserializeObject>>(allTempToken.ToString()); int index = 0; // 遍历所有PropTemp,提取坐标 foreach (List tempList in _allPropTempData) { index++; Dictionary coordCountDict = new Dictionary(); List duplicateCoords = new List(); foreach (PropTempData propTemp in tempList) { var TempArr = propTemp.ConfigData.GetArray(); int rows = TempArr.GetLength(0); int cols = TempArr.GetLength(1); // 起始 Vector3 startPoint = new Vector3(propTemp.x, propTemp.y, 0); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { int value = TempArr[i, j]; if (value > 0) { // 初始位置 Vector3 positionB = new Vector3(j, i, 0); //空间转换 // DiggingGameManager.LogError($"positionB::row:: {i}" + " col::" + j + " data.PropDirection::" + data.PropDirection); Vector3 endPosition = startPoint + GetRotatePoint(propTemp.ConfigData.PropDirection, Vector3.zero, positionB); string coordKey = $"({endPosition.x},{endPosition.y})"; // 统计次数 if (coordCountDict.ContainsKey(coordKey)) { coordCountDict[coordKey]++; if (coordCountDict[coordKey] == 2) { duplicateCoords.Add(coordKey); } } else { coordCountDict[coordKey] = 1; } } } } } // 输出结果 Debug.Log($"检查文件:{fullPath}++{index}"); if (duplicateCoords.Count == 0) { Debug.Log("✅ AllTempJson 中无重复坐标"); } else { Debug.LogWarning($"⚠️{index} 发现{duplicateCoords.Count}个重复坐标:"); foreach (var coord in duplicateCoords) { Debug.LogWarning($"- 坐标 {coord} 重复出现 {coordCountDict[coord]} 次"); } } } } catch (Exception e) { Debug.LogError($"解析文件失败:{jsonPath}\n错误:{e.Message}"); } } // 汇总并输出结果 Debug.Log($"===== 文件夹坐标检查结果(共扫描 {jsonFilePaths.Length} 个JSON文件) ====="); int totalDuplicateCoords = 0; foreach (var kvp in globalCoordRecord) { if (kvp.Value.Count > 1) { totalDuplicateCoords++; Debug.LogWarning($"⚠️ 坐标 {kvp.Key} 在 {kvp.Value.Count} 个文件中重复出现:"); foreach (var filePath in kvp.Value) { Debug.LogWarning($" - {filePath}"); } } } if (totalDuplicateCoords == 0) { Debug.Log("✅ 所有文件中未发现重复坐标"); } else { Debug.LogWarning($"===== 共发现 {totalDuplicateCoords} 个跨文件重复坐标 ====="); } } // 校验菜单是否可用(仅选中文件夹时可点击) [MenuItem("Assets/检查文件夹下所有JSON的坐标重复", true)] public static bool ValidateCheckAllJsonInFolder() { string path = AssetDatabase.GetAssetPath(Selection.activeObject); return !string.IsNullOrEmpty(path) && Directory.Exists(path); } }