From d10dfc1892bbf776220814cc7a9199e5b1fee84a Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Mon, 20 Jul 2026 14:29:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BC=80=E5=8F=91=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Editor/DeveloperMode.meta | 8 + .../DeveloperMode/DeveloperModeCoreTests.cs | 117 + .../DeveloperModeCoreTests.cs.meta} | 2 +- .../DeveloperModePanelBuilder.cs | 631 + .../DeveloperModePanelBuilder.cs.meta} | 2 +- .../DevSavePromoteWindow.cs | 2 + .../Prefabs/DeveloperModePanel.prefab | 17229 ++++++++++++++++ .../Prefabs/DeveloperModePanel.prefab.meta | 7 + Assets/Scenes/Persistence.unity | 2110 +- Assets/Scripts/AssemblyInfo.cs | 3 + ...ToolPanel.cs.meta => AssemblyInfo.cs.meta} | 2 +- Assets/Scripts/AssetRefs/ConstRef.cs | 2 +- Assets/Scripts/Framework/LogKit/LogKit.cs | 31 +- .../Framework/LogKit/RuntimeLogBuffer.cs | 142 + .../Framework/LogKit/RuntimeLogBuffer.cs.meta | 11 + Assets/Scripts/Game Loop/EventEnums.cs | 3 +- .../Scripts/Game Loop/YarnVariableStorage.cs | 14 +- .../SaveSystem/SaveRestoreOrchestrator.cs | 3 + Assets/Scripts/UI/DialogUI/VariableBar.cs | 94 - Assets/Scripts/UI/Panel/DeveloperMode.meta | 8 + .../DeveloperMode/DeveloperModeListRow.cs | 41 + .../DeveloperModeListRow.cs.meta | 11 + .../DeveloperMode/DeveloperPathUtility.cs | 74 + .../DeveloperPathUtility.cs.meta | 11 + .../DeveloperVariableSnapshot.cs | 144 + .../DeveloperVariableSnapshot.cs.meta | 11 + Assets/Scripts/UI/Panel/DeveloperModePanel.cs | 891 + .../UI/Panel/DeveloperModePanel.cs.meta | 11 + Assets/Scripts/UI/Panel/InfoPanel.cs | 3 +- Assets/Scripts/UI/Panel/RecordingToolPanel.cs | 85 - Assets/Scripts/UI/UIManager.cs | 22 +- Assets/Scripts/Utility/DevSaveCatalog.cs | 220 + Assets/Scripts/Utility/DevSaveCatalog.cs.meta | 11 + Assets/Scripts/Utility/DevSaveJumpTool.cs | 461 - Assets/Scripts/Utility/DeveloperModeGate.cs | 21 + .../Scripts/Utility/DeveloperModeGate.cs.meta | 11 + 36 files changed, 19782 insertions(+), 2667 deletions(-) create mode 100644 Assets/Editor/DeveloperMode.meta create mode 100644 Assets/Editor/DeveloperMode/DeveloperModeCoreTests.cs rename Assets/{Scripts/Utility/DevSaveJumpTool.cs.meta => Editor/DeveloperMode/DeveloperModeCoreTests.cs.meta} (83%) create mode 100644 Assets/Editor/DeveloperMode/DeveloperModePanelBuilder.cs rename Assets/{Scripts/UI/DialogUI/VariableBar.cs.meta => Editor/DeveloperMode/DeveloperModePanelBuilder.cs.meta} (83%) create mode 100644 Assets/GameContent/Feature_MainUI/Prefabs/DeveloperModePanel.prefab create mode 100644 Assets/GameContent/Feature_MainUI/Prefabs/DeveloperModePanel.prefab.meta create mode 100644 Assets/Scripts/AssemblyInfo.cs rename Assets/Scripts/{UI/Panel/RecordingToolPanel.cs.meta => AssemblyInfo.cs.meta} (83%) create mode 100644 Assets/Scripts/Framework/LogKit/RuntimeLogBuffer.cs create mode 100644 Assets/Scripts/Framework/LogKit/RuntimeLogBuffer.cs.meta delete mode 100644 Assets/Scripts/UI/DialogUI/VariableBar.cs create mode 100644 Assets/Scripts/UI/Panel/DeveloperMode.meta create mode 100644 Assets/Scripts/UI/Panel/DeveloperMode/DeveloperModeListRow.cs create mode 100644 Assets/Scripts/UI/Panel/DeveloperMode/DeveloperModeListRow.cs.meta create mode 100644 Assets/Scripts/UI/Panel/DeveloperMode/DeveloperPathUtility.cs create mode 100644 Assets/Scripts/UI/Panel/DeveloperMode/DeveloperPathUtility.cs.meta create mode 100644 Assets/Scripts/UI/Panel/DeveloperMode/DeveloperVariableSnapshot.cs create mode 100644 Assets/Scripts/UI/Panel/DeveloperMode/DeveloperVariableSnapshot.cs.meta create mode 100644 Assets/Scripts/UI/Panel/DeveloperModePanel.cs create mode 100644 Assets/Scripts/UI/Panel/DeveloperModePanel.cs.meta delete mode 100644 Assets/Scripts/UI/Panel/RecordingToolPanel.cs create mode 100644 Assets/Scripts/Utility/DevSaveCatalog.cs create mode 100644 Assets/Scripts/Utility/DevSaveCatalog.cs.meta delete mode 100644 Assets/Scripts/Utility/DevSaveJumpTool.cs create mode 100644 Assets/Scripts/Utility/DeveloperModeGate.cs create mode 100644 Assets/Scripts/Utility/DeveloperModeGate.cs.meta diff --git a/Assets/Editor/DeveloperMode.meta b/Assets/Editor/DeveloperMode.meta new file mode 100644 index 000000000..5774f9757 --- /dev/null +++ b/Assets/Editor/DeveloperMode.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 665561f3805a96446b45ea1d50bfe520 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/DeveloperMode/DeveloperModeCoreTests.cs b/Assets/Editor/DeveloperMode/DeveloperModeCoreTests.cs new file mode 100644 index 000000000..1754eead2 --- /dev/null +++ b/Assets/Editor/DeveloperMode/DeveloperModeCoreTests.cs @@ -0,0 +1,117 @@ +#if UNITY_EDITOR +using System.Linq; +using AibisDream.Kit; +using AibisDream.UI; +using AibisDream.Utility; +using NUnit.Framework; +using UnityEditor; +using UnityEngine; + +namespace AibisDream.DeveloperMode.Editor +{ + public sealed class DeveloperModeCoreTests + { + [Test] + public void RuntimeLogBufferEvictsOldestAndPreservesOrder() + { + var buffer = new RuntimeLogBuffer(3); + buffer.Add(LogEntry.System(LogLevel.Info, LogCategory.General, "one")); + buffer.Add(LogEntry.System(LogLevel.Warning, LogCategory.Save, "two")); + buffer.Add(LogEntry.System(LogLevel.Error, LogCategory.Yarn, "three")); + buffer.Add(LogEntry.System(LogLevel.Fatal, LogCategory.Scene, "four")); + + var snapshot = buffer.Snapshot(); + Assert.That(snapshot.Select(item => item.Message), Is.EqualTo(new[] { "two", "three", "four" })); + Assert.That(snapshot.Select(item => item.Sequence), Is.Ordered.Ascending); + Assert.That(snapshot.Length, Is.EqualTo(buffer.Capacity)); + } + + [Test] + public void RuntimeLogBufferAcceptsConcurrentWritersAndClears() + { + var buffer = new RuntimeLogBuffer(128); + System.Threading.Tasks.Parallel.For(0, 1000, index => + buffer.Add(LogEntry.System(LogLevel.Info, LogCategory.General, index.ToString()))); + + var snapshot = buffer.Snapshot(); + Assert.That(snapshot.Length, Is.EqualTo(128)); + Assert.That(snapshot.Select(item => item.Sequence), Is.Ordered.Ascending); + buffer.Clear(); + Assert.That(buffer.Snapshot(), Is.Empty); + } + + [Test] + public void YarnStorageTypedSettersAndDebugSnapshotAreReadOnlyAndConsistent() + { + var go = new GameObject("YarnVariableStorage Test"); + try + { + var storage = go.AddComponent(); + storage.SetValue("$score", 12.5f); + storage.SetValue("$global_flag", true); + storage.SetValue("$name", "AIBIS"); + + var snapshot = DeveloperVariableSnapshot.Capture(storage, null); + Assert.That(snapshot.Select(item => item.Name), + Is.EquivalentTo(new[] { "$score", "$global_flag", "$name" })); + Assert.That(snapshot.Single(item => item.Name == "$score").Value, Is.EqualTo("12.5")); + Assert.That(snapshot.Single(item => item.Name == "$global_flag").IsGlobal, Is.True); + Assert.That(snapshot.All(item => item.HasRuntimeOverride), Is.True); + + storage.ClearLocal(); + var remaining = DeveloperVariableSnapshot.Capture(storage, null); + Assert.That(remaining.Select(item => item.Name), Is.EqualTo(new[] { "$global_flag" })); + storage.Clear(); + Assert.That(DeveloperVariableSnapshot.Capture(storage, null), Is.Empty); + } + finally + { + Object.DestroyImmediate(go); + } + } + + [Test] + public void EditorBuildEnablesDeveloperMode() + { + Assert.That(DeveloperModeGate.IsEnabled, Is.True); + } + + [Test] + public void PrefabIsConfiguredAndPersistenceReferencesIt() + { + const string prefabPath = "Assets/GameContent/Feature_MainUI/Prefabs/DeveloperModePanel.prefab"; + const string scenePath = "Assets/Scenes/Persistence.unity"; + var prefab = AssetDatabase.LoadAssetAtPath(prefabPath); + Assert.That(prefab, Is.Not.Null); + Assert.That(prefab.activeSelf, Is.False); + var panel = prefab.GetComponent(); + Assert.That(panel, Is.Not.Null); + Assert.That(panel.IsConfigured, Is.True); + Assert.That(prefab.GetComponent().sizeDelta, Is.EqualTo(new Vector2(840f, 760f))); + Assert.That(AssetDatabase.GetDependencies(scenePath), Does.Contain(prefabPath)); + } + + [Test] + public void PanelToggleIsNonModalAndEscapeClosesIt() + { + const string prefabPath = "Assets/GameContent/Feature_MainUI/Prefabs/DeveloperModePanel.prefab"; + var prefab = AssetDatabase.LoadAssetAtPath(prefabPath); + var instance = Object.Instantiate(prefab); + try + { + var panel = instance.GetComponent(); + var originalScale = Time.timeScale; + panel.TogglePanel(); + Assert.That(panel.IsOpen, Is.True); + Assert.That(Time.timeScale, Is.EqualTo(originalScale)); + Assert.That(panel.HandleEscape(), Is.True); + Assert.That(panel.IsOpen, Is.False); + } + finally + { + Object.DestroyImmediate(instance); + } + } + } +} +#endif diff --git a/Assets/Scripts/Utility/DevSaveJumpTool.cs.meta b/Assets/Editor/DeveloperMode/DeveloperModeCoreTests.cs.meta similarity index 83% rename from Assets/Scripts/Utility/DevSaveJumpTool.cs.meta rename to Assets/Editor/DeveloperMode/DeveloperModeCoreTests.cs.meta index af1ecd1fc..71b3a549c 100644 --- a/Assets/Scripts/Utility/DevSaveJumpTool.cs.meta +++ b/Assets/Editor/DeveloperMode/DeveloperModeCoreTests.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: bb148bf10c3ed12439c0cc4d8ef20b99 +guid: b95ba9d1e80e3dc4e83bc408569c4955 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Editor/DeveloperMode/DeveloperModePanelBuilder.cs b/Assets/Editor/DeveloperMode/DeveloperModePanelBuilder.cs new file mode 100644 index 000000000..21099cc57 --- /dev/null +++ b/Assets/Editor/DeveloperMode/DeveloperModePanelBuilder.cs @@ -0,0 +1,631 @@ +#if UNITY_EDITOR +using System; +using System.Linq; +using AibisDream.UI; +using TMPro; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.UI; + +namespace AibisDream.DeveloperMode.Editor +{ + public static class DeveloperModePanelBuilder + { + private const string PrefabPath = "Assets/GameContent/Feature_MainUI/Prefabs/DeveloperModePanel.prefab"; + private const string ScenePath = "Assets/Scenes/Persistence.unity"; + private static readonly Color Background = new(0.025f, 0.045f, 0.065f, 0.97f); + 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 Runtime Panel")] + public static void BuildFromMenu() + { + Build(); + Selection.activeObject = AssetDatabase.LoadAssetAtPath(PrefabPath); + } + + public static void BuildFromCommandLine() + { + try + { + Build(); + Debug.Log("[DeveloperModePanelBuilder] Build completed."); + } + catch (Exception ex) + { + Debug.LogException(ex); + EditorApplication.Exit(1); + } + } + + private static void Build() + { + var root = BuildPrefabContents(); + root.SetActive(false); + PrefabUtility.SaveAsPrefabAsset(root, PrefabPath); + UnityEngine.Object.DestroyImmediate(root); + AssetDatabase.SaveAssets(); + ReplaceSceneInstance(); + AssetDatabase.SaveAssets(); + } + + private static GameObject BuildPrefabContents() + { + var root = CreateUiObject("Developer Mode Panel", typeof(CanvasRenderer), typeof(Image), typeof(DeveloperModePanel)); + var rootRect = root.GetComponent(); + rootRect.anchorMin = new Vector2(1f, 0.5f); + rootRect.anchorMax = new Vector2(1f, 0.5f); + rootRect.pivot = new Vector2(1f, 0.5f); + rootRect.anchoredPosition = new Vector2(-16f, 0f); + rootRect.sizeDelta = new Vector2(840f, 760f); + root.GetComponent().color = Background; + + var panel = root.GetComponent(); + var header = CreatePanel("Header", root.transform, Surface); + SetAnchors(header.GetComponent(), new Vector2(0f, 1f), new Vector2(1f, 1f), + new Vector2(0f, -46f), Vector2.zero); + var title = CreateText("Title", header.transform, "AIBIS DREAM / DEVELOPMENT MODE", 22f, FontStyles.Bold); + SetAnchors(title.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 1f), + new Vector2(18f, 0f), new Vector2(-64f, 0f)); + title.alignment = TextAlignmentOptions.MidlineLeft; + var close = CreateButton("Close", header.transform, "×", 44f, 36f); + var closeRect = close.GetComponent(); + closeRect.anchorMin = new Vector2(1f, 0.5f); + closeRect.anchorMax = new Vector2(1f, 0.5f); + closeRect.pivot = new Vector2(1f, 0.5f); + closeRect.anchoredPosition = new Vector2(-5f, 0f); + closeRect.sizeDelta = new Vector2(44f, 36f); + + var tabs = CreateHorizontal("Tabs", root.transform, 4f, new RectOffset(4, 4, 3, 3)); + SetAnchors(tabs.GetComponent(), new Vector2(0f, 1f), new Vector2(1f, 1f), + new Vector2(0f, -88f), new Vector2(0f, -46f)); + var tabNames = new[] { "概览", "日志", "Yarn 变量", "存档跳转", "显示与控制" }; + var tabButtons = tabNames.Select(name => CreateButton($"Tab {name}", tabs.transform, name, 0f, 34f)).ToArray(); + foreach (var button in tabButtons) + { + var layout = button.GetComponent(); + layout.flexibleWidth = 1f; + layout.preferredWidth = 0f; + } + + var pageRoot = CreateUiObject("Pages", typeof(RectTransform)); + pageRoot.transform.SetParent(root.transform, false); + SetAnchors(pageRoot.GetComponent(), Vector2.zero, Vector2.one, + new Vector2(8f, 8f), new Vector2(-8f, -94f)); + + var overview = BuildOverviewPage(pageRoot.transform, out var overviewRefs); + var logs = BuildLogPage(pageRoot.transform, out var logRefs); + var variables = BuildVariablePage(pageRoot.transform, out var variableRefs); + var saves = BuildSavePage(pageRoot.transform, out var saveRefs); + var display = BuildDisplayPage(pageRoot.transform, out var displayRefs); + var pages = new[] { overview, logs, variables, saves, display }; + foreach (var page in pages) + { + Stretch(page.GetComponent()); + page.SetActive(false); + } + + var serialized = new SerializedObject(panel); + Set(serialized, "closeButton", close.GetComponent