Files

495 lines
19 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 文件名: ArrowLevelEditor.cs
// 路径: Assets/Editor/ArrowLevelEditor.cs
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ChillConnect;
public class ArrowLevelEditor : EditorWindow
{
// ========== 多关卡数据 ==========
private List<LevelConfig> _levels = new List<LevelConfig>();
private int _selectedLevelIndex = -1;
private int _nextLevelId = 1;
// ========== 当前正在编辑的箭头路径 ==========
private List<int> _currentPathPoints = new List<int>();
private int _arrowIdCounter = 1;
// ========== 预设颜色 ==========
private readonly Color[] _colorPalette = new Color[]
{
new Color(1f, 0.3f, 0.3f),
new Color(1f, 0.6f, 0.2f),
new Color(1f, 0.9f, 0.2f),
new Color(0.3f, 0.8f, 0.4f),
new Color(0.2f, 0.8f, 0.9f),
new Color(0.3f, 0.5f, 1f),
new Color(0.7f, 0.4f, 1f),
new Color(1f, 0.5f, 0.8f)
};
private Vector2 _scrollPos;
[MenuItem("Tools/Arrow Level Editor")]
public static void ShowWindow()
{
GetWindow<ArrowLevelEditor>("箭头关卡编辑器 (多关卡)");
}
private void OnEnable()
{
if (_levels.Count == 0)
CreateNewLevel("第1关", 8, 8, 60);
if (_selectedLevelIndex < 0 || _selectedLevelIndex >= _levels.Count)
_selectedLevelIndex = 0;
OnLevelChanged();
}
private void OnGUI()
{
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
// ========== 顶部工具栏 ==========
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("导入 all_levels.json", GUILayout.Width(150))) ImportLevels();
if (GUILayout.Button("导出 all_levels.json", GUILayout.Width(150))) ExportAllLevels();
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
// ========== 关卡管理 ==========
EditorGUILayout.BeginHorizontal();
GUILayout.Label("关卡列表", EditorStyles.boldLabel);
if (GUILayout.Button("新建关卡", GUILayout.Width(80)))
CreateNewLevel($"第{_levels.Count + 1}关", 8, 8, 60);
if (GUILayout.Button("删除当前", GUILayout.Width(80)))
{
if (_selectedLevelIndex >= 0 && _selectedLevelIndex < _levels.Count)
{
if (EditorUtility.DisplayDialog("删除关卡", $"确定删除 '{_levels[_selectedLevelIndex].levelName}'", "确定", "取消"))
{
_levels.RemoveAt(_selectedLevelIndex);
if (_levels.Count == 0) CreateNewLevel("第1关", 8, 8, 60);
_selectedLevelIndex = Mathf.Clamp(_selectedLevelIndex, 0, _levels.Count - 1);
OnLevelChanged();
}
}
}
if (GUILayout.Button("复制当前", GUILayout.Width(80)))
{
if (_selectedLevelIndex >= 0)
{
var src = _levels[_selectedLevelIndex];
LevelConfig copy = new LevelConfig();
copy.levelId = _nextLevelId++;
copy.levelName = src.levelName + "_副本";
copy.gridRows = src.gridRows;
copy.gridCols = src.gridCols;
copy.pointSpacing = src.pointSpacing;
copy.arrows = new List<ArrowConfig>();
foreach (var a in src.arrows)
{
ArrowConfig newA = new ArrowConfig();
newA.id = a.id;
newA.startPoint = a.startPoint;
newA.path = new List<string>(a.path);
copy.arrows.Add(newA);
}
_levels.Add(copy);
_selectedLevelIndex = _levels.Count - 1;
OnLevelChanged();
}
}
EditorGUILayout.EndHorizontal();
// 显示关卡列表
for (int i = 0; i < _levels.Count; i++)
{
var lv = _levels[i];
EditorGUILayout.BeginHorizontal();
if (i == _selectedLevelIndex) GUI.backgroundColor = Color.green;
if (GUILayout.Button($"ID:{lv.levelId} {lv.levelName} ({lv.arrows.Count}箭头)", GUILayout.MinWidth(150)))
{
if (_selectedLevelIndex != i)
{
_selectedLevelIndex = i;
OnLevelChanged();
}
}
GUI.backgroundColor = Color.white;
EditorGUILayout.EndHorizontal();
}
GUILayout.Space(10);
// ========== 当前关卡编辑区 ==========
if (_selectedLevelIndex >= 0 && _selectedLevelIndex < _levels.Count)
{
LevelConfig currentLevel = _levels[_selectedLevelIndex];
EditorGUILayout.LabelField($"编辑关卡: {currentLevel.levelName}", EditorStyles.boldLabel);
currentLevel.levelId = EditorGUILayout.IntField("关卡ID", currentLevel.levelId);
currentLevel.levelName = EditorGUILayout.TextField("关卡名称", currentLevel.levelName);
currentLevel.gridRows = EditorGUILayout.IntField("行数", currentLevel.gridRows);
currentLevel.gridCols = EditorGUILayout.IntField("列数", currentLevel.gridCols);
currentLevel.pointSpacing = EditorGUILayout.IntField("点间距", currentLevel.pointSpacing);
GUILayout.Space(10);
EditorGUILayout.LabelField("绘制当前箭头", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("撤销上一点") && _currentPathPoints.Count > 0)
_currentPathPoints.RemoveAt(_currentPathPoints.Count - 1);
if (GUILayout.Button("清空当前路径"))
_currentPathPoints.Clear();
if (GUILayout.Button("清空所有箭头"))
{
if (EditorUtility.DisplayDialog("确认", "删除当前关卡所有箭头?", "确定", "取消"))
{
currentLevel.arrows.Clear();
_arrowIdCounter = 1;
_currentPathPoints.Clear();
}
}
EditorGUILayout.EndHorizontal();
// 网格绘制(增强:显示起点/终点标记)
DrawGridWithArrows(currentLevel);
// 确认生成
if (GUILayout.Button("确认生成当前箭头 (加入列表)"))
{
if (_currentPathPoints.Count < 2)
{
EditorUtility.DisplayDialog("错误", "路径至少需要2个点", "确定");
}
else
{
ArrowConfig newArrow = new ArrowConfig();
newArrow.id = _arrowIdCounter++;
newArrow.startPoint = _currentPathPoints[0];
newArrow.path = new List<string>();
for (int i = 0; i < _currentPathPoints.Count - 1; i++)
{
newArrow.path.Add(GetDirection(_currentPathPoints[i], _currentPathPoints[i + 1], currentLevel.gridCols));
}
int colorIdx = (currentLevel.arrows.Count) % _colorPalette.Length;
newArrow.runtimeColor = _colorPalette[colorIdx];
currentLevel.arrows.Add(newArrow);
_currentPathPoints.Clear();
Debug.Log($"箭头已生成,当前箭头总数 {currentLevel.arrows.Count}");
}
}
// 箭头列表(显示起点→终点)
GUILayout.Space(10);
EditorGUILayout.LabelField($"当前箭头 ({currentLevel.arrows.Count})", EditorStyles.boldLabel);
for (int i = 0; i < currentLevel.arrows.Count; i++)
{
var arrow = currentLevel.arrows[i];
// 计算终点
int endPoint = arrow.startPoint;
if (arrow.path.Count > 0)
{
int cur = arrow.startPoint;
foreach (string dir in arrow.path)
{
int next = GetNextPoint(cur, dir, currentLevel.gridCols, currentLevel.gridRows);
if (next >= 0) cur = next;
}
endPoint = cur;
}
EditorGUILayout.BeginHorizontal();
Color arrowColor = arrow.runtimeColor;
if (arrowColor == default)
{
arrowColor = _colorPalette[i % _colorPalette.Length];
arrow.runtimeColor = arrowColor;
}
GUI.backgroundColor = arrowColor;
GUILayout.Label(" ", GUILayout.Width(20), GUILayout.Height(18));
GUI.backgroundColor = Color.white;
if (GUILayout.Button($"ID:{arrow.id} {arrow.startPoint} → {endPoint} 路径:{string.Join(",", arrow.path)}", GUILayout.MinWidth(200)))
{
// 可扩展高亮
}
if (GUILayout.Button("✕", GUILayout.Width(20)))
{
if (EditorUtility.DisplayDialog("删除箭头", $"删除箭头 ID:{arrow.id}", "确定", "取消"))
{
currentLevel.arrows.RemoveAt(i);
i--;
}
}
EditorGUILayout.EndHorizontal();
}
}
else
{
EditorGUILayout.HelpBox("请先创建或选择一个关卡", MessageType.Info);
}
EditorGUILayout.EndScrollView();
}
// ========== 辅助方法 ==========
private void CreateNewLevel(string name, int rows, int cols, int spacing)
{
LevelConfig lv = new LevelConfig();
lv.levelId = _nextLevelId++;
lv.levelName = name;
lv.gridRows = rows;
lv.gridCols = cols;
lv.pointSpacing = spacing;
lv.arrows = new List<ArrowConfig>();
_levels.Add(lv);
_selectedLevelIndex = _levels.Count - 1;
OnLevelChanged();
}
private void OnLevelChanged()
{
_currentPathPoints.Clear();
if (_selectedLevelIndex >= 0 && _selectedLevelIndex < _levels.Count)
{
var lv = _levels[_selectedLevelIndex];
_arrowIdCounter = lv.arrows.Count + 1;
for (int i = 0; i < lv.arrows.Count; i++)
{
if (lv.arrows[i].runtimeColor == default)
lv.arrows[i].runtimeColor = _colorPalette[i % _colorPalette.Length];
}
}
}
// ========== 增强的网格绘制(标记起点/终点) ==========
private void DrawGridWithArrows(LevelConfig level)
{
int totalPoints = level.gridRows * level.gridCols;
int rows = level.gridRows;
int cols = level.gridCols;
// 构建:点ID -> (箭头索引, 是否起点, 是否终点)
var pointInfo = new Dictionary<int, List<(int idx, bool isStart, bool isEnd)>>();
for (int i = 0; i < level.arrows.Count; i++)
{
var arrow = level.arrows[i];
List<int> allPoints = new List<int> { arrow.startPoint };
int cur = arrow.startPoint;
foreach (string dir in arrow.path)
{
int next = GetNextPoint(cur, dir, cols, rows);
if (next >= 0) allPoints.Add(next);
cur = next;
}
// 终点是路径最后一个点
int end = allPoints.LastOrDefault();
for (int j = 0; j < allPoints.Count; j++)
{
int pid = allPoints[j];
bool isStart = (j == 0);
bool isEnd = (j == allPoints.Count - 1);
if (!pointInfo.ContainsKey(pid))
pointInfo[pid] = new List<(int, bool, bool)>();
pointInfo[pid].Add((i, isStart, isEnd));
}
}
// 构建每个点的显示标签
var pointLabels = new Dictionary<int, string>();
foreach (var kv in pointInfo)
{
int pid = kv.Key;
var list = kv.Value;
bool hasStart = list.Any(x => x.isStart);
bool hasEnd = list.Any(x => x.isEnd);
bool multi = list.Count > 1;
string label = "";
if (hasStart && hasEnd)
label = "S/E";
else if (hasStart)
label = "S";
else if (hasEnd)
label = "E";
if (multi && (hasStart || hasEnd))
label += "+";
else if (multi && !hasStart && !hasEnd)
label = "+";
pointLabels[pid] = label;
}
// 绘制网格
for (int i = 0; i < totalPoints; i++)
{
int row = i / cols;
int col = i % cols;
if (col == 0) EditorGUILayout.BeginHorizontal();
bool isInCurrentPath = _currentPathPoints.Contains(i);
bool isInSavedPath = pointInfo.ContainsKey(i);
Color bgColor = GUI.backgroundColor;
string label = i.ToString();
if (isInSavedPath)
{
var owners = pointInfo[i];
if (owners.Count == 1)
{
int idx = owners[0].idx;
bgColor = level.arrows[idx].runtimeColor;
}
else
{
bgColor = Color.gray;
}
// 添加起点/终点标记
if (pointLabels.ContainsKey(i))
label += pointLabels[i];
}
if (isInCurrentPath)
{
bgColor = Color.green;
label = i + "●";
}
// 起点/终点特殊颜色边框(仅当无重叠时)
if (isInSavedPath && pointInfo[i].Count == 1)
{
var info = pointInfo[i][0];
if (info.isStart)
bgColor = Color.Lerp(bgColor, Color.green, 0.3f);
else if (info.isEnd)
bgColor = Color.Lerp(bgColor, Color.red, 0.3f);
}
GUI.backgroundColor = bgColor;
if (GUILayout.Button(label, GUILayout.Width(50), GUILayout.Height(30))) // 增加宽度容纳标记
{
if (_currentPathPoints.Count == 0)
_currentPathPoints.Add(i);
else
{
int last = _currentPathPoints[_currentPathPoints.Count - 1];
if (IsAdjacent(last, i, cols))
{
if (isInSavedPath)
Debug.LogWarning($"点 {i} 已被其他箭头占用,可能重叠");
_currentPathPoints.Add(i);
}
else
EditorUtility.DisplayDialog("提示", $"必须与上一个点 ({last}) 相邻", "确定");
}
}
GUI.backgroundColor = Color.white;
if (col == cols - 1) EditorGUILayout.EndHorizontal();
}
}
// ========== 工具方法 ==========
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 bool IsAdjacent(int a, int b, int cols)
{
int aRow = a / cols, aCol = a % cols;
int bRow = b / cols, bCol = b % cols;
return (Mathf.Abs(aRow - bRow) + Mathf.Abs(aCol - bCol)) == 1;
}
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";
}
// ========== 导入/导出 ==========
private void ImportLevels()
{
string path = EditorUtility.OpenFilePanel("导入 all_levels.json", Application.dataPath, "json");
if (string.IsNullOrEmpty(path)) return;
try
{
string json = File.ReadAllText(path);
AllLevelRoot root = JsonUtility.FromJson<AllLevelRoot>(json);
if (root != null && root.levels != null && root.levels.Count > 0)
{
_levels = root.levels;
foreach (var lv in _levels)
for (int i = 0; i < lv.arrows.Count; i++)
lv.arrows[i].runtimeColor = _colorPalette[i % _colorPalette.Length];
int maxId = _levels.Max(l => l.levelId);
_nextLevelId = maxId + 1;
_selectedLevelIndex = 0;
OnLevelChanged();
Debug.Log($"导入成功,共 {_levels.Count} 个关卡");
}
else
EditorUtility.DisplayDialog("导入失败", "文件格式不正确或为空", "确定");
}
catch (System.Exception e)
{
EditorUtility.DisplayDialog("导入错误", e.Message, "确定");
}
}
private void ExportAllLevels()
{
if (_levels.Count == 0) { EditorUtility.DisplayDialog("错误", "没有关卡可导出", "确定"); return; }
AllLevelRoot root = new AllLevelRoot();
root.levels = new List<LevelConfig>();
foreach (var lv in _levels)
{
LevelConfig copy = new LevelConfig();
copy.levelId = lv.levelId;
copy.levelName = lv.levelName;
copy.gridRows = lv.gridRows;
copy.gridCols = lv.gridCols;
copy.pointSpacing = lv.pointSpacing;
copy.arrows = new List<ArrowConfig>();
foreach (var a in lv.arrows)
{
ArrowConfig newA = new ArrowConfig();
newA.id = a.id;
newA.startPoint = a.startPoint;
newA.path = new List<string>(a.path);
copy.arrows.Add(newA);
}
root.levels.Add(copy);
}
string json = JsonUtility.ToJson(root, true);
string path = EditorUtility.SaveFilePanel("保存 all_levels.json", Application.dataPath, "all_levels.json", "json");
if (!string.IsNullOrEmpty(path))
{
File.WriteAllText(path, json);
EditorUtility.DisplayDialog("成功", $"已保存 {root.levels.Count} 个关卡", "确定");
}
}
}