using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using Newtonsoft.Json; using System.Linq; using Framework.ProcessMode; namespace DefaultNamespace.ProcessMode { public class SceneStepEditorWindow : EditorWindow { private List steps = new List(); private List stepFoldouts = new List(); // 用于控制步骤的折叠状态 private List> actionFoldouts = new List>(); // 用于控制每个步骤内动作的折叠状态 private Vector2 scrollPosition; // 滚动条位置 private string configFilePath; private static SceneStepEditorWindow window; private string selectedFileName; // 当前选择的文件名 private string dataFolderPath; [MenuItem("工具/流程编辑器")] public static void ShowWindow() { window = GetWindow("Scene Step 编辑器"); float windowWidth = 600; float windowHeight = 600; Rect centeredPosition = new Rect( (Screen.currentResolution.width - windowWidth) / 2, (Screen.currentResolution.height - windowHeight) / 2, windowWidth, windowHeight ); window.position = centeredPosition; } private void OnEnable() { dataFolderPath = Path.Combine(Application.streamingAssetsPath, "DataConfig"); if (!Directory.Exists(dataFolderPath)) { Directory.CreateDirectory(dataFolderPath); } // 加载所有流程文件 var files = Directory.GetFiles(dataFolderPath, "*.json"); var fileNames = files.Select(file => Path.GetFileName(file)).ToList(); if (fileNames.Count > 0) { selectedFileName = fileNames[0]; // 默认选择第一个文件 configFilePath = Path.Combine(dataFolderPath, selectedFileName); LoadProcessFile(); } else { selectedFileName = "新流程.json"; // 默认新流程文件 configFilePath = Path.Combine(dataFolderPath, selectedFileName); } } private void OnGUI() { GUILayout.Label("步骤编辑器", EditorStyles.boldLabel); // 流程文件选择菜单 GUILayout.Label("选择流程文件", EditorStyles.boldLabel); string[] files = Directory.GetFiles(dataFolderPath, "*.json").Select(f => Path.GetFileName(f)).ToArray(); int selectedIndex = System.Array.IndexOf(files, selectedFileName); int newSelectedIndex = EditorGUILayout.Popup(selectedIndex, files); if (newSelectedIndex != selectedIndex) { selectedFileName = files[newSelectedIndex]; configFilePath = Path.Combine(dataFolderPath, selectedFileName); LoadProcessFile(); } // 新增流程按钮 GUILayout.Space(10); if (GUILayout.Button("新增流程", GUILayout.Height(30))) { string newFileName = EditorUtility.SaveFilePanel("保存新流程", dataFolderPath, "新流程.json", "json"); if (!string.IsNullOrEmpty(newFileName)) { CreateNewProcessFile(newFileName); } } DrawCreateLine(); scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, true); GUILayout.BeginVertical(GUILayout.ExpandWidth(true)); if (steps.Count == 0) { if (GUILayout.Button("+", GUILayout.Width(30))) { AddNewStep(); } } for (int i = 0; i < steps.Count; i++) { EnsureFoldoutLists(i); DrawSectionSeparator(); GUILayout.BeginHorizontal(); if (i == steps.Count - 1 && GUILayout.Button("+", GUILayout.Width(30))) { AddNewStep(); } if (GUILayout.Button("-", GUILayout.Width(30))) { steps.RemoveAt(i); stepFoldouts.RemoveAt(i); actionFoldouts.RemoveAt(i); GUILayout.EndHorizontal(); continue; } stepFoldouts[i] = EditorGUILayout.Foldout(stepFoldouts[i], $"{steps[i].StepDescription} 编号: {i}", true); GUILayout.EndHorizontal(); if (stepFoldouts[i]) { EditorGUI.indentLevel++; steps[i].StepDescription = EditorGUILayout.TextField("描述", steps[i].StepDescription); GUILayout.Space(5); DrawActionList(i); EditorGUI.indentLevel--; } DrawBlueLine(); } GUILayout.Space(20); GUILayout.EndVertical(); GUILayout.EndScrollView(); DrawSaveButton(); if (GUILayout.Button("生成 ProcessEvents 代码", GUILayout.Height(40))) { GenerateProcessEventsCode(); } } private void CreateNewProcessFile(string filePath) { // 创建一个新的流程文件,初始化为空的流程数据 List newSteps = new List(); // 空的步骤列表 string json = JsonConvert.SerializeObject(newSteps, Formatting.Indented); // 保存新文件 File.WriteAllText(filePath, json); // 设置新的文件为当前编辑的文件 selectedFileName = Path.GetFileName(filePath); configFilePath = filePath; // 加载新文件内容 LoadProcessFile(); // 提示文件创建成功 EditorUtility.DisplayDialog("新流程创建成功", $"已创建新流程:{selectedFileName}", "确定"); } private void GenerateProcessEventsCode() { if (string.IsNullOrEmpty(configFilePath)) { EditorUtility.DisplayDialog("错误", "请先选择一个流程文件", "确定"); return; } // 读取流程文件数据 LoadProcessFile(); if (steps.Count == 0) { EditorUtility.DisplayDialog("错误", "当前流程文件为空,无法生成代码", "确定"); return; } // 生成代码的文件路径 string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(selectedFileName); string filePath = Path.Combine(Application.dataPath, $"{fileNameWithoutExtension}_ProcessEvents.cs"); // 打开文件写入 using (StreamWriter writer = new StreamWriter(filePath, false)) { // 根据流程文件名生成不同的类名 writer.WriteLine($"using UnityEngine;"); writer.WriteLine($"using UnityEngine.SceneManagement;"); writer.WriteLine($"using Framework.ProcessMode;"); writer.WriteLine($"using Framework.Dto;"); writer.WriteLine(); writer.WriteLine($"/// "); writer.WriteLine($"/// {fileNameWithoutExtension} 流程事件处理类"); writer.WriteLine($"/// "); writer.WriteLine($"public class {fileNameWithoutExtension}ProcessEvents : MonoBehaviour"); writer.WriteLine("{"); writer.WriteLine(" [ProcessAction]"); writer.WriteLine(" public void RegisterProcessEvents()"); writer.WriteLine(" {"); // 遍历步骤,动作和目标对象生成代码 for (int i = 0; i < steps.Count; i++) { var step = steps[i]; for (int j = 0; j < step.Actions.Count; j++) { var action = step.Actions[j]; for (int k = 0; k < action.TargetObjects.Count; k++) { string targetName = action.TargetObjects[k].ObjectName; // 生成方法注释 writer.WriteLine($" /// "); writer.WriteLine($" /// 步骤 {i + 1}: {step.StepDescription}"); writer.WriteLine($" /// 动作 {j + 1}: {action.Title}"); writer.WriteLine($" /// 目标对象: {targetName}"); writer.WriteLine($" /// "); // 生成事件注册代码 writer.WriteLine($" EventRegistrationCenter.AddEventsToAllStepEvents(new EventStepInfo({i}, {j}, \"{targetName}\", () =>"); writer.WriteLine(" {"); writer.WriteLine(" // 在这里加入事件处理逻辑"); writer.WriteLine($" Debug.Log(\"执行事件:步骤 {i + 1} -> 动作 {j + 1} -> {targetName}\");"); writer.WriteLine(" }));"); writer.WriteLine(); } } } writer.WriteLine(" }"); writer.WriteLine("}"); } // 提示生成成功 EditorUtility.DisplayDialog("生成成功", $"{fileNameWithoutExtension}ProcessEvents 代码已生成!", "确定"); } private void DrawCreateLine() { Rect rect = GUILayoutUtility.GetRect(3, 5, GUILayout.MaxWidth(position.width)); EditorGUI.DrawRect(rect, new Color(0.729f, 0.519f, 0.219f)); // Blueish color } private void DrawBlueLine() { Rect rect = GUILayoutUtility.GetRect(3, 5, GUILayout.MaxWidth(position.width)); EditorGUI.DrawRect(rect, new Color(0.329f, 0.519f, 0.219f)); // Blueish color } private void DrawSectionSeparator() { Rect rect = GUILayoutUtility.GetRect(3, 5, GUILayout.MaxWidth(position.width)); EditorGUI.DrawRect(rect, new Color(0.219f, 0.219f, 0.219f)); // Dark gray color for separator } private void LoadProcessFile() { if (File.Exists(configFilePath)) { string json = File.ReadAllText(configFilePath); steps = JsonConvert.DeserializeObject>(json) ?? new List(); stepFoldouts = new List(new bool[steps.Count]); actionFoldouts = new List>(); foreach (var step in steps) { actionFoldouts.Add(new List(new bool[step.Actions.Count])); } } else { steps.Clear(); stepFoldouts.Clear(); actionFoldouts.Clear(); } } private void SaveConfiguration() { bool saveConfirmed = EditorUtility.DisplayDialog( "确认保存", "是否要保存当前配置到 " + selectedFileName + " 并生成枚举文件?", "确定", "取消" ); if (!saveConfirmed) return; // 在序列化之前,根据动作类型清理不需要的数据 foreach (var step in steps) { foreach (var action in step.Actions) { if (action.ActionType == ProcessActionType.默认) { // 如果是默认类型,清除判断题和多选题数据 action.JudgmentQuestions = null; action.MultipleChoiceQuestions = null; } else if (action.ActionType == ProcessActionType.判断题) { // 如果是判断题类型,清除目标对象和多选题数据 action.TargetObjects = new List(); action.MultipleChoiceQuestions = null; action.IsSequential = false; } else if (action.ActionType == ProcessActionType.多选题) { // 如果是多选题类型,清除目标对象和判断题数据 action.TargetObjects = new List(); action.JudgmentQuestions = null; action.IsSequential = false; } } } // 保存 JSON 数据 string json = JsonConvert.SerializeObject(steps, Formatting.Indented); File.WriteAllText(configFilePath, json); // EditorUtility.DisplayDialog("保存成功", "配置已保存到 " + selectedFileName, "确定"); // 重新加载文件以刷新界面,但保持当前选择的文件 string currentSelectedFile = selectedFileName; LoadProcessFile(); selectedFileName = currentSelectedFile; configFilePath = Path.Combine(dataFolderPath, selectedFileName); } private void DrawSaveButton() { GUI.backgroundColor = Color.green; if (GUILayout.Button("保存到 " + selectedFileName, GUILayout.Height(40))) { SaveConfiguration(); } GUI.backgroundColor = Color.white; } private void DrawActionList(int stepIndex) { if (steps[stepIndex].Actions.Count == 0) { if (GUILayout.Button("+", GUILayout.Width(30))) { AddNewAction(stepIndex); } } for (int j = 0; j < steps[stepIndex].Actions.Count; j++) { EnsureActionFoldoutList(stepIndex, j); GUILayout.BeginHorizontal(); if (j == steps[stepIndex].Actions.Count - 1 && GUILayout.Button("+", GUILayout.Width(30))) { AddNewAction(stepIndex); } if (GUILayout.Button("-", GUILayout.Width(30))) { steps[stepIndex].Actions.RemoveAt(j); actionFoldouts[stepIndex].RemoveAt(j); GUILayout.EndHorizontal(); continue; } // 添加向上按钮(如果不是第一个) if (j > 0 && GUILayout.Button("↑", GUILayout.Width(30))) { // 交换当前动作与上一个动作 var tempAction = steps[stepIndex].Actions[j]; steps[stepIndex].Actions[j] = steps[stepIndex].Actions[j - 1]; steps[stepIndex].Actions[j - 1] = tempAction; // 交换折叠状态 var tempFoldout = actionFoldouts[stepIndex][j]; actionFoldouts[stepIndex][j] = actionFoldouts[stepIndex][j - 1]; actionFoldouts[stepIndex][j - 1] = tempFoldout; } // 添加向下按钮(如果不是最后一个) if (j < steps[stepIndex].Actions.Count - 1 && GUILayout.Button("↓", GUILayout.Width(30))) { var tempAction = steps[stepIndex].Actions[j]; steps[stepIndex].Actions[j] = steps[stepIndex].Actions[j + 1]; steps[stepIndex].Actions[j + 1] = tempAction; // 交换折叠状态 var tempFoldout = actionFoldouts[stepIndex][j]; actionFoldouts[stepIndex][j] = actionFoldouts[stepIndex][j + 1]; actionFoldouts[stepIndex][j + 1] = tempFoldout; } actionFoldouts[stepIndex][j] = EditorGUILayout.Foldout(actionFoldouts[stepIndex][j], $"{steps[stepIndex].Actions[j].Title} 编号: {stepIndex}-{j}", true); GUILayout.EndHorizontal(); if (actionFoldouts[stepIndex][j]) { EditorGUI.indentLevel++; DrawActionDetails(steps[stepIndex].Actions[j]); EditorGUI.indentLevel--; } } } private void DrawActionDetails(ProcessStepDescription action) { action.Title = EditorGUILayout.TextField("标题", action.Title); action.Description = EditorGUILayout.TextField("描述", action.Description); // 添加动作类型选择 action.ActionType = (ProcessActionType)EditorGUILayout.EnumPopup("动作类型", action.ActionType); // 根据动作类型显示不同的配置界面 if (action.ActionType == ProcessActionType.默认) { action.IsSequential = EditorGUILayout.Toggle("按顺序点击", action.IsSequential); action.Score = EditorGUILayout.FloatField("分数", action.Score); action.RequireCorrectCompletion = EditorGUILayout.Toggle("需要正确完成", action.RequireCorrectCompletion); GUILayout.Space(5); DrawTargetObjects(action); } else if (action.ActionType == ProcessActionType.判断题) { action.Score = EditorGUILayout.FloatField("分数", action.Score); action.RequireCorrectCompletion = EditorGUILayout.Toggle("需要正确完成", action.RequireCorrectCompletion); GUILayout.Space(10); DrawJudgmentQuestions(action); } else if (action.ActionType == ProcessActionType.多选题) { action.Score = EditorGUILayout.FloatField("分数", action.Score); action.RequireCorrectCompletion = EditorGUILayout.Toggle("需要正确完成", action.RequireCorrectCompletion); GUILayout.Space(10); DrawMultipleChoiceQuestions(action); } } private void DrawTargetObjects(ProcessStepDescription action) { GUILayout.Label("目标对象列表", EditorStyles.boldLabel); for (int k = 0; k < action.TargetObjects.Count; k++) { GUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.BeginHorizontal(); GUILayout.Label($"目标对象 {k + 1}", EditorStyles.boldLabel); // 添加向上按钮(如果不是第一个) if (k > 0 && GUILayout.Button("↑", GUILayout.Width(30))) { // 交换当前目标对象与上一个目标对象 var temp = action.TargetObjects[k]; action.TargetObjects[k] = action.TargetObjects[k - 1]; action.TargetObjects[k - 1] = temp; } // 添加向下按钮(如果不是最后一个) if (k < action.TargetObjects.Count - 1 && GUILayout.Button("↓", GUILayout.Width(30))) { var temp = action.TargetObjects[k]; action.TargetObjects[k] = action.TargetObjects[k + 1]; action.TargetObjects[k + 1] = temp; } // 删除按钮 if (GUILayout.Button("-", GUILayout.Width(30))) { action.TargetObjects.RemoveAt(k); GUILayout.EndHorizontal(); GUILayout.EndVertical(); continue; } GUILayout.EndHorizontal(); GUILayout.Space(5); action.TargetObjects[k].ObjectName = EditorGUILayout.TextField("对象名称", action.TargetObjects[k].ObjectName); action.TargetObjects[k].Type = (ProcessTargetType)EditorGUILayout.EnumPopup("对象类型", action.TargetObjects[k].Type); action.TargetObjects[k].Score = EditorGUILayout.FloatField("分数", action.TargetObjects[k].Score); GUILayout.EndVertical(); GUILayout.Space(5); } GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (GUILayout.Button("添加目标对象", GUILayout.Width(300))) { action.TargetObjects.Add(new TargetObjectConfig { ObjectName = "", Type = ProcessTargetType.Model, Score = 0 }); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private void DrawJudgmentQuestions(ProcessStepDescription action) { GUILayout.Label("判断题配置", EditorStyles.boldLabel); if (action.JudgmentQuestions == null) { action.JudgmentQuestions = new List(); } for (int i = 0; i < action.JudgmentQuestions.Count; i++) { GUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.BeginHorizontal(); GUILayout.Label($"题目 {i + 1}", EditorStyles.boldLabel); if (GUILayout.Button("-", GUILayout.Width(30))) { action.JudgmentQuestions.RemoveAt(i); GUILayout.EndHorizontal(); GUILayout.EndVertical(); continue; } GUILayout.EndHorizontal(); GUILayout.Space(5); action.JudgmentQuestions[i].Question = EditorGUILayout.TextField("题目内容", action.JudgmentQuestions[i].Question); action.JudgmentQuestions[i].CorrectAnswer = EditorGUILayout.TextField("正确答案", action.JudgmentQuestions[i].CorrectAnswer); action.JudgmentQuestions[i].Score = EditorGUILayout.FloatField("分数", action.JudgmentQuestions[i].Score); GUILayout.EndVertical(); GUILayout.Space(5); } GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (GUILayout.Button("添加判断题", GUILayout.Width(300))) { action.JudgmentQuestions.Add(new JudgmentQuestionConfig { Question = "", CorrectAnswer = "", Score = 0 }); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private void DrawMultipleChoiceQuestions(ProcessStepDescription action) { GUILayout.Label("多选题配置", EditorStyles.boldLabel); if (action.MultipleChoiceQuestions == null) { action.MultipleChoiceQuestions = new List(); } for (int i = 0; i < action.MultipleChoiceQuestions.Count; i++) { GUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.BeginHorizontal(); GUILayout.Label($"题目 {i + 1}", EditorStyles.boldLabel); if (GUILayout.Button("-", GUILayout.Width(30))) { action.MultipleChoiceQuestions.RemoveAt(i); GUILayout.EndHorizontal(); GUILayout.EndVertical(); continue; } GUILayout.EndHorizontal(); GUILayout.Space(5); action.MultipleChoiceQuestions[i].Question = EditorGUILayout.TextField("题目内容", action.MultipleChoiceQuestions[i].Question); action.MultipleChoiceQuestions[i].Score = EditorGUILayout.FloatField("分数", action.MultipleChoiceQuestions[i].Score); GUILayout.Label("选项列表", EditorStyles.boldLabel); for (int j = 0; j < action.MultipleChoiceQuestions[i].Options.Count; j++) { GUILayout.BeginHorizontal(); action.MultipleChoiceQuestions[i].Options[j] = EditorGUILayout.TextField($"选项 {j + 1}", action.MultipleChoiceQuestions[i].Options[j]); bool isCorrect = action.MultipleChoiceQuestions[i].CorrectAnswers.Contains(action.MultipleChoiceQuestions[i].Options[j]); bool newIsCorrect = EditorGUILayout.Toggle("是正确答案", isCorrect, GUILayout.Width(100)); if (newIsCorrect != isCorrect) { if (newIsCorrect) { if (!action.MultipleChoiceQuestions[i].CorrectAnswers.Contains(action.MultipleChoiceQuestions[i].Options[j])) { action.MultipleChoiceQuestions[i].CorrectAnswers.Add(action.MultipleChoiceQuestions[i].Options[j]); } } else { action.MultipleChoiceQuestions[i].CorrectAnswers.Remove(action.MultipleChoiceQuestions[i].Options[j]); } } if (GUILayout.Button("-", GUILayout.Width(30))) { action.MultipleChoiceQuestions[i].Options.RemoveAt(j); action.MultipleChoiceQuestions[i].CorrectAnswers.Remove(action.MultipleChoiceQuestions[i].Options[j]); GUILayout.EndHorizontal(); continue; } GUILayout.EndHorizontal(); } if (GUILayout.Button("添加选项", GUILayout.Width(100))) { action.MultipleChoiceQuestions[i].Options.Add(""); } GUILayout.EndVertical(); GUILayout.Space(5); } GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (GUILayout.Button("添加多选题", GUILayout.Width(300))) { action.MultipleChoiceQuestions.Add(new MultipleChoiceConfig { Question = "", Score = 0 }); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private void AddNewStep() { steps.Add(new ProcessStep("新步骤", new List())); stepFoldouts.Add(true); actionFoldouts.Add(new List()); } private void AddNewAction(int stepIndex) { steps[stepIndex].Actions.Add(new ProcessStepDescription( targetObjects: new List(), action: null, description: "", isSequential: false, stepDescription: "", score: 0, title: "新动作" )); actionFoldouts[stepIndex].Add(true); } private void EnsureFoldoutLists(int index) { if (stepFoldouts.Count <= index) stepFoldouts.Add(true); if (actionFoldouts.Count <= index) actionFoldouts.Add(new List()); } private void EnsureActionFoldoutList(int stepIndex, int actionIndex) { if (actionFoldouts[stepIndex].Count <= actionIndex) actionFoldouts[stepIndex].Add(true); } } }