using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.IO; using System.Linq; using ChillConnect; public class ArrowLevelAutoGenerator : EditorWindow { // ----- 参数 ----- private int _levelCount = 10; private bool _autoSizeByDifficulty = true; private int _gridRows = 8; private int _gridCols = 8; private int _pointSpacing = 60; private Difficulty _difficulty = Difficulty.Hard; private GenerationMode _mode = GenerationMode.Random; private PatternType _pattern = PatternType.Heart; private bool _randomPattern = false; private string _outputFileName = "all_levels.json"; private enum Difficulty { Easy, Medium, Hard } private enum GenerationMode { Random, Pattern } private enum PatternType { Heart, Star, LetterA, Diamond, Circle, XShape, Random } [MenuItem("Tools/Arrow Level Auto Generator")] public static void ShowWindow() { GetWindow("自动生成关卡"); } private void OnGUI() { GUILayout.Label("自动化关卡生成器", EditorStyles.boldLabel); EditorGUILayout.Space(); _levelCount = EditorGUILayout.IntField("关卡数量", _levelCount); _autoSizeByDifficulty = EditorGUILayout.Toggle("根据难度自动调整网格尺寸", _autoSizeByDifficulty); if (!_autoSizeByDifficulty) { _gridRows = EditorGUILayout.IntField("网格行数", _gridRows); _gridCols = EditorGUILayout.IntField("网格列数", _gridCols); } else { EditorGUILayout.HelpBox("网格尺寸:简单6×6,中等8×8,困难34×21", MessageType.Info); } _pointSpacing = EditorGUILayout.IntField("点间距", _pointSpacing); _difficulty = (Difficulty)EditorGUILayout.EnumPopup("难度", _difficulty); _mode = (GenerationMode)EditorGUILayout.EnumPopup("生成模式", _mode); if (_mode == GenerationMode.Pattern) { _randomPattern = EditorGUILayout.Toggle("随机图案", _randomPattern); if (!_randomPattern) { _pattern = (PatternType)EditorGUILayout.EnumPopup("指定图案", _pattern); } } _outputFileName = EditorGUILayout.TextField("输出文件名", _outputFileName); EditorGUILayout.Space(); if (GUILayout.Button("生成关卡", GUILayout.Height(40))) { GenerateLevels(); } } private void GenerateLevels() { if (_levelCount <= 0) { EditorUtility.DisplayDialog("错误", "关卡数量必须大于0", "确定"); return; } List levels = new List(); for (int i = 0; i < _levelCount; i++) { LevelConfig lv = null; int rows, cols; if (_autoSizeByDifficulty) GetSizeByDifficulty(out rows, out cols); else { rows = _gridRows; cols = _gridCols; } if (_mode == GenerationMode.Pattern) { PatternType selectedPattern = _pattern; if (_randomPattern) selectedPattern = (PatternType)Random.Range(0, System.Enum.GetValues(typeof(PatternType)).Length - 1); lv = GeneratePatternLevel(selectedPattern, i + 1, rows, cols); if (lv == null) { Debug.LogWarning($"图案生成失败,回退到随机"); lv = GenerateRandomLevel(i + 1, rows, cols); } } else { lv = GenerateRandomLevel(i + 1, rows, cols); } if (lv != null) levels.Add(lv); } if (levels.Count == 0) { EditorUtility.DisplayDialog("错误", "没有生成任何有效关卡", "确定"); return; } AllLevelRoot root = new AllLevelRoot(); root.levels = levels; string json = JsonUtility.ToJson(root, true); string path = EditorUtility.SaveFilePanel("保存关卡配置", Application.dataPath, _outputFileName, "json"); if (!string.IsNullOrEmpty(path)) { File.WriteAllText(path, json); EditorUtility.DisplayDialog("成功", $"已生成 {levels.Count} 个关卡并保存到 {path}", "确定"); } } private void GetSizeByDifficulty(out int rows, out int cols) { switch (_difficulty) { case Difficulty.Easy: rows = 6; cols = 6; break; case Difficulty.Medium: rows = 8; cols = 8; break; case Difficulty.Hard: rows = 34; cols = 21; break; default: rows = 8; cols = 8; break; } } // ========== 改进的随机生成(无交叉) ========== private LevelConfig GenerateRandomLevel(int levelId, int rows, int cols) { LevelConfig lv = new LevelConfig(); lv.levelId = levelId; lv.levelName = $"第{levelId}关"; lv.gridRows = rows; lv.gridCols = cols; lv.pointSpacing = _pointSpacing; lv.arrows = new List(); int arrowCount = 0; int minPathLen=0,maxPathLen=0; int maxTurns = 0; switch (_difficulty) { case Difficulty.Easy: arrowCount = Random.Range(3, 6); minPathLen = 1; maxPathLen = 3; maxTurns = 1; break; case Difficulty.Medium: arrowCount = Random.Range(6, 10); minPathLen = 2; maxPathLen = 5; maxTurns = 2; break; case Difficulty.Hard: arrowCount = Random.Range(25, 40); minPathLen = 4; maxPathLen = 10; maxTurns = 5; break; } HashSet occupiedPoints = new HashSet(); int maxAttemptsPerArrow = 200; int totalAttempts = 0; int maxTotalAttempts = arrowCount * 50; for (int i = 0; i < arrowCount; i++) { bool success = false; int attempts = 0; while (!success && attempts < maxAttemptsPerArrow && totalAttempts < maxTotalAttempts) { attempts++; totalAttempts++; int start = Random.Range(0, rows * cols); if (occupiedPoints.Contains(start)) continue; int pathLen = Random.Range(minPathLen, maxPathLen + 1); List dirs = new List(); int cur = start; bool valid = true; int turns = 0; string lastDir = ""; List pathPoints = new List { start }; for (int j = 0; j < pathLen; j++) { List validDirs = GetValidDirections(cur, rows, cols); if (validDirs.Count == 0) { valid = false; break; } string chosenDir = ""; if (turns < maxTurns && !string.IsNullOrEmpty(lastDir)) { var otherDirs = validDirs.Where(d => d != lastDir).ToList(); if (otherDirs.Count > 0) chosenDir = otherDirs[Random.Range(0, otherDirs.Count)]; else chosenDir = validDirs[Random.Range(0, validDirs.Count)]; } else { chosenDir = validDirs[Random.Range(0, validDirs.Count)]; } if (chosenDir != lastDir && !string.IsNullOrEmpty(lastDir)) turns++; dirs.Add(chosenDir); cur = GetNextPoint(cur, chosenDir, cols, rows); if (cur < 0) { valid = false; break; } pathPoints.Add(cur); lastDir = chosenDir; } if (!valid) continue; // 检测冲突 bool conflict = false; foreach (int p in pathPoints) { if (occupiedPoints.Contains(p)) { conflict = true; break; } } if (conflict) continue; // 无冲突,添加箭头 ArrowConfig arrow = new ArrowConfig(); arrow.id = i + 1; arrow.startPoint = start; arrow.path = dirs; lv.arrows.Add(arrow); foreach (int p in pathPoints) occupiedPoints.Add(p); success = true; } if (!success) Debug.LogWarning($"生成第 {levelId} 关时,第 {i+1} 个箭头生成失败,跳过"); } if (lv.arrows.Count == 0) return null; return lv; } private List GetValidDirections(int point, int rows, int cols) { List dirs = new List(); int row = point / cols; int col = point % cols; if (row > 0) dirs.Add("up"); if (row < rows - 1) dirs.Add("down"); if (col > 0) dirs.Add("left"); if (col < cols - 1) dirs.Add("right"); return dirs; } // ========== 图案生成(简单版) ========== private LevelConfig GeneratePatternLevel(PatternType pattern, int levelId, int rows, int cols) { // 这里仅实现菱形图案,您可自行扩展 return GenerateDiamondPattern(levelId, rows, cols); } private LevelConfig GenerateDiamondPattern(int levelId, int rows, int cols) { if (rows < 5 || cols < 5) return null; LevelConfig lv = new LevelConfig(); lv.levelId = levelId; lv.levelName = $"图案Diamond_{levelId}"; lv.gridRows = rows; lv.gridCols = cols; lv.pointSpacing = _pointSpacing; lv.arrows = new List(); int centerRow = rows / 2; int centerCol = cols / 2; int radius = Mathf.Min(rows, cols) / 2 - 1; if (radius < 2) return null; List points = new List(); for (int i = 0; i <= radius; i++) { int r = centerRow - i; int c = centerCol + i; if (r >= 0 && c < cols) points.Add(r * cols + c); } for (int i = 1; i <= radius; i++) { int r = centerRow + i; int c = centerCol + radius - i; if (r < rows && c >= 0) points.Add(r * cols + c); } for (int i = 1; i <= radius; i++) { int r = centerRow + radius - i; int c = centerCol - i; if (r >= 0 && c >= 0) points.Add(r * cols + c); } for (int i = 1; i < radius; i++) { int r = centerRow - i; int c = centerCol - radius + i; if (r >= 0 && c >= 0) points.Add(r * cols + c); } if (points.Count < 3) return null; int maxStep = 4; int arrowId = 1; for (int i = 0; i < points.Count - 1;) { int remaining = points.Count - 1 - i; int step = Mathf.Min(maxStep, remaining); var segment = points.Skip(i).Take(step + 1).ToList(); if (segment.Count >= 2) { ArrowConfig arrow = new ArrowConfig(); arrow.id = arrowId++; arrow.startPoint = segment[0]; arrow.path = new List(); for (int j = 0; j < segment.Count - 1; j++) { string dir = GetDirection(segment[j], segment[j + 1], cols); arrow.path.Add(dir); } lv.arrows.Add(arrow); } i += step; } return lv; } // ========== 工具方法 ========== private int GetNextPoint(int current, string dir, int cols, int rows) { int row = current / cols; int col = current % cols; switch (dir) { case "up": row--; break; case "down": row++; break; case "left": col--; break; case "right": col++; break; } if (row < 0 || row >= rows || col < 0 || col >= cols) return -1; return row * cols + col; } private string GetDirection(int from, int to, int cols) { int fromRow = from / cols, fromCol = from % cols; int toRow = to / cols, toCol = to % cols; if (toRow < fromRow) return "up"; if (toRow > fromRow) return "down"; if (toCol < fromCol) return "left"; if (toCol > fromCol) return "right"; return "up"; } }