From e6ae903c030a9f709bddf80ab801cd7875eb02a6 Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Fri, 24 Jul 2026 13:16:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B5=8B=E8=AF=95=E5=AD=98=E6=A1=A3?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeveloperModeTestSavePanelBuilder.cs | 460 ++ .../DeveloperModeTestSavePanelBuilder.cs.meta | 11 + .../DeveloperMode/TestSaveSystemTests.cs | 235 + .../DeveloperMode/TestSaveSystemTests.cs.meta | 11 + .../Prefabs/DeveloperModePanel.prefab | 6772 ++++++++++------- .../Actor/佩佩像素版-通用动画.aseprite.meta | 450 +- Assets/Scripts/AssetRefs/ConstRef.cs | 3 + Assets/Scripts/Game Loop/GameManager.cs | 21 + Assets/Scripts/SaveSystem/Development.meta | 8 + .../Development/TestSaveCoverage.cs | 170 + .../Development/TestSaveCoverage.cs.meta | 11 + .../Development/TestSaveRecorder.cs | 90 + .../Development/TestSaveRecorder.cs.meta | 11 + .../Development/TestSaveRepository.cs | 620 ++ .../Development/TestSaveRepository.cs.meta | 11 + .../SaveSystem/Development/TestSaveTypes.cs | 79 + .../Development/TestSaveTypes.cs.meta | 11 + .../Scripts/SaveSystem/SavePointEvaluator.cs | 32 +- .../SaveSystem/SaveRestoreOrchestrator.cs | 37 + .../DeveloperMode/DeveloperModeListRow.cs | 16 +- Assets/Scripts/UI/Panel/DeveloperModePanel.cs | 438 +- 21 files changed, 6647 insertions(+), 2850 deletions(-) create mode 100644 Assets/Editor/DeveloperMode/DeveloperModeTestSavePanelBuilder.cs create mode 100644 Assets/Editor/DeveloperMode/DeveloperModeTestSavePanelBuilder.cs.meta create mode 100644 Assets/Editor/DeveloperMode/TestSaveSystemTests.cs create mode 100644 Assets/Editor/DeveloperMode/TestSaveSystemTests.cs.meta create mode 100644 Assets/Scripts/SaveSystem/Development.meta create mode 100644 Assets/Scripts/SaveSystem/Development/TestSaveCoverage.cs create mode 100644 Assets/Scripts/SaveSystem/Development/TestSaveCoverage.cs.meta create mode 100644 Assets/Scripts/SaveSystem/Development/TestSaveRecorder.cs create mode 100644 Assets/Scripts/SaveSystem/Development/TestSaveRecorder.cs.meta create mode 100644 Assets/Scripts/SaveSystem/Development/TestSaveRepository.cs create mode 100644 Assets/Scripts/SaveSystem/Development/TestSaveRepository.cs.meta create mode 100644 Assets/Scripts/SaveSystem/Development/TestSaveTypes.cs create mode 100644 Assets/Scripts/SaveSystem/Development/TestSaveTypes.cs.meta diff --git a/Assets/Editor/DeveloperMode/DeveloperModeTestSavePanelBuilder.cs b/Assets/Editor/DeveloperMode/DeveloperModeTestSavePanelBuilder.cs new file mode 100644 index 000000000..8db0a8f31 --- /dev/null +++ b/Assets/Editor/DeveloperMode/DeveloperModeTestSavePanelBuilder.cs @@ -0,0 +1,460 @@ +#if UNITY_EDITOR +using System; +using System.Linq; +using AibisDream.UI; +using TMPro; +using UnityEditor; +using UnityEngine; +using UnityEngine.UI; + +namespace AibisDream.DeveloperMode.Editor +{ + /// 只重建 DeveloperModePanel 的测试存档页,避免覆盖其他页的人工调整。 + public static class DeveloperModeTestSavePanelBuilder + { + private const string PrefabPath = + "Assets/GameContent/Feature_MainUI/Prefabs/DeveloperModePanel.prefab"; + private const string FontAssetPath = + "Assets/Font/Assets/WenQuanYi Bitmap Song 14px SDF.asset"; + + private static readonly Color Surface = new(0.055f, 0.085f, 0.11f, 0.96f); + private static readonly Color Accent = new(0.16f, 0.75f, 0.88f, 1f); + private static readonly Color TextColor = new(0.88f, 0.96f, 1f, 1f); + + [MenuItem("Tools/AIBIS/Developer Mode/Rebuild Test Save Page")] + public static void BuildFromMenu() + { + Build(); + Selection.activeObject = AssetDatabase.LoadAssetAtPath(PrefabPath); + } + + public static void BuildFromCommandLine() + { + try + { + Build(); + Debug.Log("[DeveloperModeTestSavePanelBuilder] Build completed."); + } + catch (Exception ex) + { + Debug.LogException(ex); + EditorApplication.Exit(1); + } + } + + private static void Build() + { + var root = PrefabUtility.LoadPrefabContents(PrefabPath); + try + { + var panel = root.GetComponent() + ?? throw new InvalidOperationException("DeveloperModePanel component not found."); + var serialized = new SerializedObject(panel); + var pages = serialized.FindProperty("tabPages") + ?? throw new MissingFieldException(nameof(DeveloperModePanel), "tabPages"); + if (pages.arraySize < 4) + { + throw new InvalidOperationException("DeveloperModePanel requires five tab pages."); + } + + var savePage = pages.GetArrayElementAtIndex(3).objectReferenceValue as GameObject + ?? throw new InvalidOperationException("Save page reference is missing."); + ClearChildren(savePage.transform); + ConfigurePageLayout(savePage); + BuildSavePage(savePage.transform, serialized); + RenameSaveTab(serialized); + + var font = AssetDatabase.LoadAssetAtPath(FontAssetPath) + ?? throw new InvalidOperationException($"Font not found: {FontAssetPath}"); + foreach (var text in root.GetComponentsInChildren(true)) + { + text.font = font; + } + + serialized.ApplyModifiedPropertiesWithoutUndo(); + PrefabUtility.SaveAsPrefabAsset(root, PrefabPath); + } + finally + { + PrefabUtility.UnloadPrefabContents(root); + } + + AssetDatabase.SaveAssets(); + } + + private static void BuildSavePage(Transform page, SerializedObject serialized) + { + var recording = CreateHorizontal("Recording Toolbar", page, 6f); + AddLayout(recording, 38f); + var recordingToggle = CreateToggle("Record Test Saves", recording.transform, "录制测试存档", 145f); + var recordingState = CreateText("Recording State", recording.transform, "○ OFF", 14f, FontStyles.Bold); + AddLayout(recordingState.gameObject, 34f); + recordingState.rectTransform.sizeDelta = new Vector2(105f, 0f); + var search = CreateInput("Test Save Search", recording.transform, "搜索章节、节点或场景...", 250f); + var reload = CreateButton("Reload Test Saves", recording.transform, "重新扫描", 92f, 34f); + var open = CreateButton("Open Test Saves", recording.transform, "打开目录", 92f, 34f); + + var columns = CreateHorizontal("Library Columns", page, 7f); + var columnsLayout = columns.AddComponent(); + columnsLayout.flexibleHeight = 1f; + columnsLayout.minHeight = 420f; + + var chapters = CreateVertical("Chapter Column", columns.transform, 4f); + var chapterLayout = chapters.AddComponent(); + chapterLayout.preferredWidth = 245f; + chapterLayout.flexibleWidth = 0f; + chapterLayout.flexibleHeight = 1f; + var chapterTitle = CreateText("Title", chapters.transform, "章节 / 覆盖率", 16f, FontStyles.Bold); + AddLayout(chapterTitle.gameObject, 28f); + CreateList( + "Chapter List", + chapters.transform, + out var chapterContent, + out var chapterTemplate); + + var entries = CreateVertical("Entry Column", columns.transform, 4f); + var entryLayout = entries.AddComponent(); + entryLayout.flexibleWidth = 1f; + entryLayout.flexibleHeight = 1f; + var entryTitle = CreateText( + "Title", + entries.transform, + "存档点(● 已录制 / ○ 缺失 / ✕ 无效)", + 16f, + FontStyles.Bold); + AddLayout(entryTitle.gameObject, 28f); + CreateList( + "Entry List", + entries.transform, + out var entryContent, + out var entryTemplate); + + var actions = CreateHorizontal("Actions", page, 6f); + AddLayout(actions, 36f); + var jump = CreateButton("Jump", actions.transform, "跳转到选中", 118f, 32f); + var deleteSelected = CreateButton("Delete Selected", actions.transform, "删除选中", 105f, 32f); + var deleteInvalid = CreateButton("Delete Invalid", actions.transform, "清理无效", 105f, 32f); + var clear = CreateButton("Clear Library", actions.transform, "清空存档库", 118f, 32f); + + var status = CreateText( + "Save Status", + page, + "测试存档录制默认关闭;开启后仅复制正式节点自动档。", + 14f); + AddLayout(status.gameObject, 46f); + status.color = new Color(0.45f, 1f, 0.65f); + status.alignment = TextAlignmentOptions.TopLeft; + + Set(serialized, "testSaveRecordingToggle", recordingToggle); + Set(serialized, "testSaveRecordingStateText", recordingState); + Set(serialized, "testSaveSearchInput", search); + Set(serialized, "reloadSavesButton", reload.GetComponent