Framework/Assets/Framework/Editor/SceneStepEditorWindow.cs

642 lines
25 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.

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<ProcessStep> steps = new List<ProcessStep>();
private List<bool> stepFoldouts = new List<bool>(); // 用于控制步骤的折叠状态
private List<List<bool>> actionFoldouts = new List<List<bool>>(); // 用于控制每个步骤内动作的折叠状态
private Vector2 scrollPosition; // 滚动条位置
private string configFilePath;
private static SceneStepEditorWindow window;
private string selectedFileName; // 当前选择的文件名
private string dataFolderPath;
[MenuItem("工具/流程编辑器")]
public static void ShowWindow()
{
window = GetWindow<SceneStepEditorWindow>("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<ProcessStep> newSteps = new List<ProcessStep>(); // 空的步骤列表
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($"/// <summary>");
writer.WriteLine($"/// {fileNameWithoutExtension} 流程事件处理类");
writer.WriteLine($"/// </summary>");
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($" /// <summary>");
writer.WriteLine($" /// 步骤 {i + 1}: {step.StepDescription}");
writer.WriteLine($" /// 动作 {j + 1}: {action.Title}");
writer.WriteLine($" /// 目标对象: {targetName}");
writer.WriteLine($" /// </summary>");
// 生成事件注册代码
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<List<ProcessStep>>(json) ?? new List<ProcessStep>();
stepFoldouts = new List<bool>(new bool[steps.Count]);
actionFoldouts = new List<List<bool>>();
foreach (var step in steps)
{
actionFoldouts.Add(new List<bool>(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<(string ObjectName, ProcessTargetType Type)>();
action.MultipleChoiceQuestions = null;
action.IsSequential = false;
}
else if (action.ActionType == ProcessActionType.)
{
// 如果是多选题类型,清除目标对象和判断题数据
action.TargetObjects = new List<(string ObjectName, ProcessTargetType Type)>();
action.JudgmentQuestions = null;
action.IsSequential = false;
}
}
}
// 保存 JSON 数据
string json = JsonConvert.SerializeObject(steps, Formatting.Indented);
File.WriteAllText(configFilePath, json);
EditorUtility.DisplayDialog("保存成功", "配置已保存到 " + selectedFileName, "确定");
// 重新加载文件以刷新界面
LoadProcessFile();
}
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;
}
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.BeginHorizontal();
if (GUILayout.Button("-", GUILayout.Width(30)))
{
action.TargetObjects.RemoveAt(k);
GUILayout.EndHorizontal();
continue;
}
GUILayout.Space(10);
action.TargetObjects[k] = (
ObjectName: EditorGUILayout.TextField($"目标对象 {k + 1}", action.TargetObjects[k].ObjectName, GUILayout.Width(400)),
Type: (ProcessTargetType)EditorGUILayout.EnumPopup(action.TargetObjects[k].Type, GUILayout.Width(100))
);
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("+", GUILayout.Width(300)))
{
action.TargetObjects.Add(("", ProcessTargetType.Model));
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
private void DrawJudgmentQuestions(ProcessStepDescription action)
{
GUILayout.Label("判断题配置", EditorStyles.boldLabel);
// 确保 JudgmentQuestions 列表已初始化
if (action.JudgmentQuestions == null)
{
action.JudgmentQuestions = new List<JudgmentQuestionConfig>();
}
// 显示所有判断题
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);
GUILayout.EndVertical();
GUILayout.Space(5);
}
// 添加新题目按钮
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("添加判断题", GUILayout.Width(300)))
{
action.JudgmentQuestions.Add(new JudgmentQuestionConfig
{
Question = "",
CorrectAnswer = ""
});
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
private void DrawMultipleChoiceQuestions(ProcessStepDescription action)
{
GUILayout.Label("多选题配置", EditorStyles.boldLabel);
// 确保 MultipleChoiceQuestions 列表已初始化
if (action.MultipleChoiceQuestions == null)
{
action.MultipleChoiceQuestions = new List<MultipleChoiceConfig>();
}
// 显示所有多选题
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);
// 选项列表
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());
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
private void AddNewStep()
{
steps.Add(new ProcessStep("新步骤", new List<ProcessStepDescription>()));
stepFoldouts.Add(true);
actionFoldouts.Add(new List<bool>());
}
private void AddNewAction(int stepIndex)
{
steps[stepIndex].Actions.Add(new ProcessStepDescription(
targetObjects: new List<(string, ProcessTargetType)>(),
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<bool>());
}
private void EnsureActionFoldoutList(int stepIndex, int actionIndex)
{
if (actionFoldouts[stepIndex].Count <= actionIndex) actionFoldouts[stepIndex].Add(true);
}
}
}