diff --git a/Scripts/Editor/Design_Tools/Scene/DecorateCostPlannerEditor.cs b/Scripts/Editor/Design_Tools/Scene/DecorateCostPlannerEditor.cs new file mode 100644 index 0000000..aef998a --- /dev/null +++ b/Scripts/Editor/Design_Tools/Scene/DecorateCostPlannerEditor.cs @@ -0,0 +1,949 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using OfficeOpenXml; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; + +namespace DesignTools.Scene +{ + /// + /// 装饰场景消耗/批次配置工具 + /// 读取 Docs/config/DecorateCost.xlsx 的 DecorateCost Sheet,供策划编辑每步消耗与批次。 + /// + public class DecorateCostPlannerEditor : BaseDesignToolEditor + { + private static readonly string[] BATCH_NAMES = + { + string.Empty, + "first", + "second", + "third", + "four", + "five", + "six", + "seven", + "eight", + "nine", + "ten", + "eleven", + "twelve", + "thirteen", + "fourteen", + "fifteen", + "sixteen", + "seventeen", + "eighteen", + "nineteen", + "twenty", + "twentyone", + "twentytwo", + "twentythree", + "twentyfour", + "twentyfive", + "twentysix", + "twentyseven", + "twentyeight", + "twentynine", + "thirty", + "thirtyone", + "thirtytwo", + "thirtythree", + "thirtyfour", + "thirtyfive", + "thirtysix", + "thirtyseven", + "thirtyeight", + "thirtynine", + "forty", + "fortyone" + }; + + private const string DECORATE_COST_EXCEL_NAME = "DecorateCost.xlsx"; + private const string DECORATE_COST_SHEET_NAME = "DecorateCost"; + private const int DATA_START_ROW = 3; + private const int TOTAL_COL_COUNT = 17; + + private const int COL_ID = 1; + private const int COL_AREA_ID = 2; + private const int COL_SORT_ID = 3; + private const int COL_COST_COUNT = 4; + private const int COL_POS = 7; + + private readonly List allRows = new List(); + private readonly Dictionary rowIndexMap = new Dictionary(); + private readonly HashSet dirtyRowIndexes = new HashSet(); + private List availableAreaIds = new List(); + private List currentAreaStepRows = new List(); + + private int selectedAreaId = -1; + private int areaIdInput = 2; + private int currentAreaExpectedStepCount; + + [MenuItem("策划工具/场景/装饰场景消耗与批次")] + public static void ShowWindow() + { + var window = GetWindow("装饰场景消耗与批次"); + window.minSize = window.GetMinWindowSize(); + window.Show(); + } + + protected override string GetDocsPathPrefKey() + { + return "DecorateCostPlannerEditor_DocsPath"; + } + + protected override string GetWindowTitle() + { + return "装饰场景消耗与批次工具"; + } + + protected override Vector2 GetMinWindowSize() + { + return new Vector2(920, 680); + } + + protected override void LoadConfigData() + { + LoadDecorateCostExcel(); + currentAreaStepRows.Clear(); + currentAreaExpectedStepCount = 0; + selectedAreaId = -1; + areaIdInput = GetDefaultAreaId(); + + if (availableAreaIds.Count == 0) + { + EditorUtility.DisplayDialog("提示", "DecorateCost 中没有可编辑的 AreaId 数据。", "确定"); + return; + } + + EditorApplication.delayCall += ShowAreaInputDialog; + } + + protected override void DrawDataEditor() + { + DrawAreaSelector(); + EditorGUILayout.Space(6); + + if (selectedAreaId < 0) + { + EditorGUILayout.HelpBox("请先输入并加载 AreaId。AreaId=1 不允许编辑;AreaId=2-5 为 20 步;AreaId>=6 为 25 步。", MessageType.Info); + return; + } + + DrawAreaSummary(); + EditorGUILayout.Space(6); + DrawQuickActions(); + EditorGUILayout.Space(6); + DrawStepTable(); + } + + protected override void SaveDataToExcel() + { + SaveDecorateCostExcel(); + } + + private void LoadDecorateCostExcel() + { + allRows.Clear(); + rowIndexMap.Clear(); + dirtyRowIndexes.Clear(); + + string excelPath = GetDocsConfigFilePath(DECORATE_COST_EXCEL_NAME); + if (!File.Exists(excelPath)) + { + throw new FileNotFoundException($"未找到 {DECORATE_COST_EXCEL_NAME}", excelPath); + } + + using (ExcelPackage package = new ExcelPackage(new FileInfo(excelPath))) + { + ExcelWorksheet worksheet = package.Workbook.Worksheets[DECORATE_COST_SHEET_NAME]; + if (worksheet == null) + { + throw new Exception($"未找到 Sheet: {DECORATE_COST_SHEET_NAME}"); + } + + if (worksheet.Dimension == null) + { + availableAreaIds = new List(); + return; + } + + int rowCount = worksheet.Dimension.End.Row; + for (int row = DATA_START_ROW; row <= rowCount; row++) + { + string idText = worksheet.Cells[row, COL_ID].Text.Trim(); + string areaIdText = worksheet.Cells[row, COL_AREA_ID].Text.Trim(); + string sortIdText = worksheet.Cells[row, COL_SORT_ID].Text.Trim(); + + if (string.IsNullOrEmpty(idText) && string.IsNullOrEmpty(areaIdText) && string.IsNullOrEmpty(sortIdText)) + { + continue; + } + + string[] fields = new string[TOTAL_COL_COUNT]; + for (int col = 1; col <= TOTAL_COL_COUNT; col++) + { + fields[col - 1] = worksheet.Cells[row, col].Text ?? string.Empty; + } + + DecorateCostExcelRow dataRow = new DecorateCostExcelRow(row, fields); + allRows.Add(dataRow); + rowIndexMap[row] = dataRow; + } + } + + availableAreaIds = allRows + .Select(row => row.AreaId) + .Where(areaId => areaId > 1) + .Distinct() + .OrderBy(areaId => areaId) + .ToList(); + } + + private void DrawAreaSelector() + { + EditorGUILayout.BeginVertical("box"); + EditorGUILayout.LabelField("AreaId 选择", EditorStyles.boldLabel); + + EditorGUILayout.BeginHorizontal(); + areaIdInput = EditorGUILayout.IntField("AreaId", areaIdInput, GUILayout.Width(240)); + + if (GUILayout.Button("加载 AreaId", GUILayout.Width(120), GUILayout.Height(24))) + { + TrySelectArea(areaIdInput, true); + } + + if (GUILayout.Button("重新输入", GUILayout.Width(100), GUILayout.Height(24))) + { + ShowAreaInputDialog(); + } + + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + + string previewText = availableAreaIds.Count <= 15 + ? string.Join(", ", availableAreaIds) + : string.Join(", ", availableAreaIds.Take(15)) + " ..."; + EditorGUILayout.HelpBox($"可编辑 AreaId:{previewText}", MessageType.None); + EditorGUILayout.EndVertical(); + } + + private void DrawAreaSummary() + { + List missingSortIds = GetMissingSortIds(); + int actualCount = currentAreaStepRows.Count; + string summary = $"当前 AreaId:{selectedAreaId}\n期望步数:{currentAreaExpectedStepCount}\n实际可编辑步数:{actualCount}\n未保存修改:{dirtyRowIndexes.Count} 行"; + + if (missingSortIds.Count > 0) + { + string missingText = string.Join(", ", missingSortIds.Take(10)); + if (missingSortIds.Count > 10) + { + missingText += " ..."; + } + + EditorGUILayout.HelpBox(summary + $"\n\n⚠ 缺失步骤:{missingText}", MessageType.Warning); + return; + } + + EditorGUILayout.HelpBox(summary, MessageType.Info); + } + + private void DrawQuickActions() + { + using (new EditorGUI.DisabledScope(currentAreaStepRows.Count == 0 || currentAreaExpectedStepCount <= 0)) + { + EditorGUILayout.BeginHorizontal(); + + GUI.backgroundColor = new Color(0.78f, 0.92f, 1f); + if (GUILayout.Button("快捷设置资源消耗", GUILayout.Width(180), GUILayout.Height(28))) + { + DecorateCostPlannerBatchCostWindow.ShowWindow(currentAreaExpectedStepCount, ApplyBatchCostSettings); + } + + GUI.backgroundColor = new Color(1f, 0.9f, 0.65f); + if (GUILayout.Button("快捷设置步骤批次", GUILayout.Width(180), GUILayout.Height(28))) + { + DecorateCostPlannerBatchSkipWindow.ShowWindow(currentAreaExpectedStepCount, ApplyBatchBatchSettings); + } + + GUI.backgroundColor = Color.white; + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + } + + GUI.backgroundColor = Color.white; + } + + private void DrawStepTable() + { + EditorGUILayout.BeginVertical("box"); + EditorGUILayout.LabelField("步骤配置", EditorStyles.boldLabel); + EditorGUILayout.Space(4); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("SortId", EditorStyles.boldLabel, GUILayout.Width(70)); + EditorGUILayout.LabelField("CostCount", EditorStyles.boldLabel, GUILayout.Width(120)); + EditorGUILayout.LabelField("步骤批次", EditorStyles.boldLabel, GUILayout.Width(140)); + EditorGUILayout.LabelField("批次映射", EditorStyles.boldLabel, GUILayout.Width(120)); + EditorGUILayout.LabelField("Title", EditorStyles.boldLabel, GUILayout.Width(260)); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(2); + scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); + + foreach (DecorateCostExcelRow row in currentAreaStepRows.OrderBy(item => item.SortId)) + { + DrawSingleStepRow(row); + } + + EditorGUILayout.EndScrollView(); + EditorGUILayout.EndVertical(); + } + + private void DrawSingleStepRow(DecorateCostExcelRow row) + { + EditorGUILayout.BeginHorizontal("box"); + EditorGUILayout.LabelField(row.SortId.ToString(), GUILayout.Width(70)); + + EditorGUI.BeginChangeCheck(); + int newCostCount = EditorGUILayout.IntField(row.CostCount, GUILayout.Width(120)); + if (EditorGUI.EndChangeCheck()) + { + row.CostCount = Mathf.Max(0, newCostCount); + MarkRowDirty(row); + } + + EditorGUI.BeginChangeCheck(); + int newBatchValue = EditorGUILayout.IntField(row.BatchValue, GUILayout.Width(140)); + if (EditorGUI.EndChangeCheck()) + { + row.BatchValue = Mathf.Max(0, newBatchValue); + MarkRowDirty(row); + } + + EditorGUILayout.LabelField(GetBatchDisplayName(row.BatchValue), GUILayout.Width(120)); + + EditorGUILayout.LabelField(string.IsNullOrEmpty(row.Title) ? "-" : row.Title, GUILayout.Width(260)); + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + } + + private void ApplyBatchCostSettings(int[] costs) + { + if (costs == null || costs.Length == 0) + { + return; + } + + int updatedCount = 0; + List missingSortIds = new List(); + + for (int step = 1; step <= currentAreaExpectedStepCount; step++) + { + DecorateCostExcelRow row = currentAreaStepRows.FirstOrDefault(item => item.SortId == step); + if (row == null) + { + missingSortIds.Add(step); + continue; + } + + int groupIndex = Mathf.Clamp((step - 1) / 5, 0, costs.Length - 1); + if (row.CostCount != costs[groupIndex]) + { + row.CostCount = Mathf.Max(0, costs[groupIndex]); + MarkRowDirty(row); + updatedCount++; + } + } + + ShowBatchApplyResult("资源消耗", updatedCount, missingSortIds); + } + + private void ApplyBatchBatchSettings(int[] batches) + { + if (batches == null || batches.Length == 0) + { + return; + } + + int updatedCount = 0; + List missingSortIds = new List(); + + for (int step = 1; step <= currentAreaExpectedStepCount; step++) + { + DecorateCostExcelRow row = currentAreaStepRows.FirstOrDefault(item => item.SortId == step); + if (row == null) + { + missingSortIds.Add(step); + continue; + } + + int batchValue = step - 1 < batches.Length ? Mathf.Max(0, batches[step - 1]) : 0; + if (row.BatchValue != batchValue) + { + row.BatchValue = batchValue; + MarkRowDirty(row); + updatedCount++; + } + } + + ShowBatchApplyResult("步骤批次", updatedCount, missingSortIds); + } + + private void ShowBatchApplyResult(string label, int updatedCount, List missingSortIds) + { + string message = $"已应用{label}快捷设置,更新 {updatedCount} 步。"; + if (missingSortIds.Count > 0) + { + message += $"\n\n缺失步骤:{string.Join(", ", missingSortIds)}"; + } + + EditorUtility.DisplayDialog("完成", message, "确定"); + } + + private void SaveDecorateCostExcel() + { + if (dirtyRowIndexes.Count == 0) + { + EditorUtility.DisplayDialog("提示", "当前没有需要保存的修改。", "确定"); + return; + } + + string excelPath = GetDocsConfigFilePath(DECORATE_COST_EXCEL_NAME); + if (!File.Exists(excelPath)) + { + EditorUtility.DisplayDialog("错误", $"未找到文件:{excelPath}", "确定"); + return; + } + + try + { + using (ExcelPackage package = new ExcelPackage(new FileInfo(excelPath))) + { + ExcelWorksheet worksheet = package.Workbook.Worksheets[DECORATE_COST_SHEET_NAME]; + if (worksheet == null) + { + EditorUtility.DisplayDialog("错误", $"未找到 Sheet:{DECORATE_COST_SHEET_NAME}", "确定"); + return; + } + + foreach (int rowIndex in dirtyRowIndexes.OrderBy(index => index)) + { + if (!rowIndexMap.TryGetValue(rowIndex, out DecorateCostExcelRow row)) + { + continue; + } + + worksheet.Cells[row.RowIndex, COL_COST_COUNT].Value = row.CostCount; + worksheet.Cells[row.RowIndex, COL_POS].Value = string.IsNullOrEmpty(row.Pos) ? null : row.Pos; + } + + package.Save(); + } + + int savedCount = dirtyRowIndexes.Count; + dirtyRowIndexes.Clear(); + AssetDatabase.Refresh(); + EditorUtility.DisplayDialog("成功", $"已保存 {savedCount} 行修改到 {DECORATE_COST_EXCEL_NAME}。", "确定"); + } + catch (Exception e) + { + EditorUtility.DisplayDialog("错误", $"保存失败:{e.Message}\n{e.StackTrace}", "确定"); + } + } + + private void ShowAreaInputDialog() + { + if (availableAreaIds == null || availableAreaIds.Count == 0) + { + return; + } + + DecorateCostAreaInputWindow.ShowWindow(areaIdInput, TrySelectArea); + } + + private bool TrySelectArea(int areaId) + { + return TrySelectArea(areaId, true); + } + + private bool TrySelectArea(int areaId, bool showDialog) + { + if (areaId == 1) + { + if (showDialog) + { + EditorUtility.DisplayDialog("限制", "AreaId 1 为旧版特殊场景,不能在此工具中编辑。", "确定"); + } + return false; + } + + int expectedStepCount = GetExpectedStepCount(areaId); + if (expectedStepCount <= 0) + { + if (showDialog) + { + EditorUtility.DisplayDialog("错误", "请输入有效的 AreaId。AreaId=2-5 为 20 步,AreaId>=6 为 25 步。", "确定"); + } + return false; + } + + bool hasArea = allRows.Any(row => row.AreaId == areaId); + if (!hasArea) + { + if (showDialog) + { + EditorUtility.DisplayDialog("未找到", $"DecorateCost 中不存在 AreaId={areaId} 的配置。", "确定"); + } + return false; + } + + selectedAreaId = areaId; + areaIdInput = areaId; + currentAreaExpectedStepCount = expectedStepCount; + currentAreaStepRows = allRows + .Where(row => row.AreaId == areaId && row.SortId != 0) + .OrderBy(row => row.SortId) + .ToList(); + + if (showDialog) + { + string message = $"已加载 AreaId={areaId}\n期望步数:{currentAreaExpectedStepCount}\n可编辑步骤:{currentAreaStepRows.Count}"; + List missingSortIds = GetMissingSortIds(); + if (missingSortIds.Count > 0) + { + message += $"\n\n⚠ 缺失步骤:{string.Join(", ", missingSortIds)}"; + } + + EditorUtility.DisplayDialog("完成", message, "确定"); + } + + Repaint(); + return true; + } + + private List GetMissingSortIds() + { + if (currentAreaExpectedStepCount <= 0) + { + return new List(); + } + + HashSet existingIds = new HashSet(currentAreaStepRows.Select(row => row.SortId)); + List missingIds = new List(); + for (int sortId = 1; sortId <= currentAreaExpectedStepCount; sortId++) + { + if (!existingIds.Contains(sortId)) + { + missingIds.Add(sortId); + } + } + + return missingIds; + } + + private int GetDefaultAreaId() + { + return availableAreaIds.FirstOrDefault(); + } + + private int GetExpectedStepCount(int areaId) + { + if (areaId >= 2 && areaId <= 5) + { + return 20; + } + + if (areaId >= 6) + { + return 25; + } + + return 0; + } + + private void MarkRowDirty(DecorateCostExcelRow row) + { + dirtyRowIndexes.Add(row.RowIndex); + } + + private static string GetBatchDisplayName(int batchValue) + { + if (batchValue <= 0) + { + return "空"; + } + + if (batchValue < BATCH_NAMES.Length) + { + return BATCH_NAMES[batchValue]; + } + + return $"第{batchValue}批"; + } + + private static int ParseBatchValue(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return 0; + } + + string trimmedValue = value.Trim(); + if (int.TryParse(trimmedValue, out int numericValue)) + { + return Mathf.Max(0, numericValue); + } + + for (int index = 1; index < BATCH_NAMES.Length; index++) + { + if (string.Equals(BATCH_NAMES[index], trimmedValue, StringComparison.OrdinalIgnoreCase)) + { + return index; + } + } + + string normalizedValue = trimmedValue.Replace("-", string.Empty).Replace(" ", string.Empty).ToLowerInvariant(); + for (int index = 1; index < BATCH_NAMES.Length; index++) + { + string normalizedBatchName = BATCH_NAMES[index].Replace("-", string.Empty).Replace(" ", string.Empty).ToLowerInvariant(); + if (normalizedBatchName == normalizedValue) + { + return index; + } + } + + switch (normalizedValue) + { + case "fourth": return 4; + case "fifth": return 5; + case "sixth": return 6; + case "seventh": return 7; + case "eighth": return 8; + case "ninth": return 9; + case "tenth": return 10; + case "eleventh": return 11; + case "twelfth": return 12; + case "thirteenth": return 13; + case "fourteenth": return 14; + case "fifteenth": return 15; + case "sixteenth": return 16; + case "seventeenth": return 17; + case "eighteenth": return 18; + case "nineteenth": return 19; + case "twentieth": return 20; + case "twentyfirst": return 21; + case "twentysecond": return 22; + case "twentythird": return 23; + case "twentyfourth": return 24; + case "twentyfifth": return 25; + case "twentysixth": return 26; + case "twentyseventh": return 27; + case "twentyeighth": return 28; + case "twentyninth": return 29; + case "thirtieth": return 30; + case "thirtyfirst": return 31; + case "thirtysecond": return 32; + case "thirtythird": return 33; + case "thirtyfourth": return 34; + case "thirtyfifth": return 35; + case "thirtysixth": return 36; + case "thirtyseventh": return 37; + case "thirtyeighth": return 38; + case "thirtyninth": return 39; + case "fortieth": return 40; + case "fortyfirst": return 41; + default: return 0; + } + } + + private static string FormatBatchValue(int batchValue) + { + if (batchValue <= 0) + { + return string.Empty; + } + + if (batchValue < BATCH_NAMES.Length) + { + return BATCH_NAMES[batchValue]; + } + + return batchValue.ToString(); + } + + private sealed class DecorateCostExcelRow + { + private readonly string[] fields; + + public DecorateCostExcelRow(int rowIndex, string[] fields) + { + RowIndex = rowIndex; + this.fields = fields ?? new string[TOTAL_COL_COUNT]; + } + + public int RowIndex { get; } + + public int AreaId => ParseInt(fields[COL_AREA_ID - 1]); + + public int SortId => ParseInt(fields[COL_SORT_ID - 1]); + + public string Title => fields[4]; + + public int CostCount + { + get => ParseInt(fields[COL_COST_COUNT - 1]); + set => fields[COL_COST_COUNT - 1] = value.ToString(); + } + + public string Pos + { + get => fields[COL_POS - 1] ?? string.Empty; + set => fields[COL_POS - 1] = value ?? string.Empty; + } + + public int BatchValue + { + get => ParseBatchValue(Pos); + set => Pos = FormatBatchValue(value); + } + + private static int ParseInt(string value) + { + return int.TryParse(value, out int result) ? result : 0; + } + } + } + + /// + /// AreaId 输入窗口 + /// + public class DecorateCostAreaInputWindow : EditorWindow + { + private Func onConfirm; + private string areaIdText = string.Empty; + + public static void ShowWindow(int defaultAreaId, Func confirmCallback) + { + DecorateCostAreaInputWindow window = CreateInstance(); + window.titleContent = new GUIContent("输入 AreaId"); + window.minSize = new Vector2(320, 120); + window.maxSize = new Vector2(320, 120); + window.areaIdText = defaultAreaId > 0 ? defaultAreaId.ToString() : string.Empty; + window.onConfirm = confirmCallback; + window.ShowUtility(); + } + + private void OnGUI() + { + EditorGUILayout.Space(10); + EditorGUILayout.LabelField("请输入要编辑的 AreaId", EditorStyles.boldLabel); + EditorGUILayout.HelpBox("AreaId=1 不允许编辑;AreaId=2-5 为 20 步;AreaId>=6 为 25 步。", MessageType.Info); + + GUI.SetNextControlName("AreaIdField"); + areaIdText = EditorGUILayout.TextField("AreaId", areaIdText); + + EditorGUILayout.Space(10); + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + + if (GUILayout.Button("确定", GUILayout.Width(90), GUILayout.Height(24))) + { + Confirm(); + } + + if (GUILayout.Button("取消", GUILayout.Width(90), GUILayout.Height(24))) + { + Close(); + } + + EditorGUILayout.EndHorizontal(); + + if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return) + { + Confirm(); + Event.current.Use(); + } + } + + private void OnFocus() + { + EditorGUI.FocusTextInControl("AreaIdField"); + } + + private void Confirm() + { + if (!int.TryParse(areaIdText, out int areaId)) + { + EditorUtility.DisplayDialog("错误", "请输入有效的整数 AreaId。", "确定"); + return; + } + + if (onConfirm == null || onConfirm.Invoke(areaId)) + { + Close(); + } + } + } + + /// + /// DecorateCost 独立的批量设置资源消耗弹窗。 + /// + public class DecorateCostPlannerBatchCostWindow : EditorWindow + { + private int groupCount = 5; + private int stepCount = 25; + private IntegerField[] costFields = Array.Empty(); + private Action onConfirm; + + public static void ShowWindow(int stepCount, Action confirmCallback) + { + DecorateCostPlannerBatchCostWindow window = CreateInstance(); + window.titleContent = new GUIContent("批量设置资源消耗"); + window.minSize = new Vector2(300, 250); + window.stepCount = Mathf.Max(1, stepCount); + window.groupCount = Mathf.CeilToInt(window.stepCount / 5f); + window.costFields = new IntegerField[window.groupCount]; + window.onConfirm = confirmCallback; + window.ShowUtility(); + } + + private void CreateGUI() + { + VisualElement root = rootVisualElement; + root.Clear(); + root.style.paddingTop = 10; + root.style.paddingBottom = 10; + root.style.paddingLeft = 10; + root.style.paddingRight = 10; + + Label title = new Label($"输入{groupCount}组资源消耗数值\n每5个步骤使用同一数值(当前共{stepCount}步)"); + title.style.fontSize = 14; + title.style.marginBottom = 10; + title.style.unityTextAlign = TextAnchor.MiddleCenter; + root.Add(title); + + for (int i = 0; i < groupCount; i++) + { + int startStep = i * 5 + 1; + int endStep = Mathf.Min((i + 1) * 5, stepCount); + IntegerField field = new IntegerField($"第{startStep}-{endStep}步:"); + field.value = 0; + field.style.marginBottom = 5; + costFields[i] = field; + root.Add(field); + } + + VisualElement buttonRow = new VisualElement(); + buttonRow.style.flexDirection = FlexDirection.Row; + buttonRow.style.marginTop = 15; + + UnityEngine.UIElements.Button confirmBtn = new UnityEngine.UIElements.Button(OnConfirmClicked) { text = "确定" }; + confirmBtn.style.flexGrow = 1; + confirmBtn.style.marginRight = 5; + buttonRow.Add(confirmBtn); + + UnityEngine.UIElements.Button cancelBtn = new UnityEngine.UIElements.Button(Close) { text = "取消" }; + cancelBtn.style.flexGrow = 1; + buttonRow.Add(cancelBtn); + + root.Add(buttonRow); + } + + private void OnConfirmClicked() + { + int[] costs = new int[groupCount]; + for (int i = 0; i < groupCount; i++) + { + costs[i] = costFields[i].value; + } + + onConfirm?.Invoke(costs); + Close(); + } + } + + /// + /// DecorateCost 独立的批量设置步骤批次弹窗。 + /// + public class DecorateCostPlannerBatchSkipWindow : EditorWindow + { + private int stepCount = 25; + private IntegerField[] skipFields = Array.Empty(); + private ScrollView scrollView; + private Action onConfirm; + + public static void ShowWindow(int stepCount, Action confirmCallback) + { + DecorateCostPlannerBatchSkipWindow window = CreateInstance(); + window.titleContent = new GUIContent("批量设置批次"); + window.minSize = new Vector2(350, 500); + window.stepCount = Mathf.Max(1, stepCount); + window.skipFields = new IntegerField[window.stepCount]; + window.onConfirm = confirmCallback; + window.ShowUtility(); + } + + private void CreateGUI() + { + VisualElement root = rootVisualElement; + root.Clear(); + root.style.paddingTop = 10; + root.style.paddingBottom = 10; + root.style.paddingLeft = 10; + root.style.paddingRight = 10; + + Label title = new Label($"输入{stepCount}个数字设置批次\n0=空 1=first 2=second 3=third..."); + title.style.fontSize = 14; + title.style.marginBottom = 10; + title.style.unityTextAlign = TextAnchor.MiddleCenter; + root.Add(title); + + scrollView = new ScrollView(); + scrollView.style.flexGrow = 1; + + for (int i = 0; i < stepCount; i++) + { + IntegerField field = new IntegerField($"步骤{i + 1}:"); + field.value = 0; + field.style.marginBottom = 3; + skipFields[i] = field; + scrollView.Add(field); + } + + root.Add(scrollView); + + VisualElement buttonRow = new VisualElement(); + buttonRow.style.flexDirection = FlexDirection.Row; + buttonRow.style.marginTop = 10; + + UnityEngine.UIElements.Button confirmBtn = new UnityEngine.UIElements.Button(OnConfirmClicked) { text = "确定" }; + confirmBtn.style.flexGrow = 1; + confirmBtn.style.marginRight = 5; + buttonRow.Add(confirmBtn); + + UnityEngine.UIElements.Button cancelBtn = new UnityEngine.UIElements.Button(Close) { text = "取消" }; + cancelBtn.style.flexGrow = 1; + buttonRow.Add(cancelBtn); + + root.Add(buttonRow); + } + + private void OnConfirmClicked() + { + int[] skips = new int[stepCount]; + for (int i = 0; i < stepCount; i++) + { + skips[i] = skipFields[i].value; + } + + onConfirm?.Invoke(skips); + Close(); + } + } +} diff --git a/Scripts/Editor/Design_Tools/Scene/DecorateCostPlannerEditor.cs.meta b/Scripts/Editor/Design_Tools/Scene/DecorateCostPlannerEditor.cs.meta new file mode 100644 index 0000000..ae3430f --- /dev/null +++ b/Scripts/Editor/Design_Tools/Scene/DecorateCostPlannerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a80667981aa125e489cabc41b4db38c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: