diff --git a/Editor/GameObjectPool/GameObjectPoolEditor.cs b/Editor/GameObjectPool/GameObjectPoolEditor.cs index 64aa142..da79068 100644 --- a/Editor/GameObjectPool/GameObjectPoolEditor.cs +++ b/Editor/GameObjectPool/GameObjectPoolEditor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; @@ -7,49 +8,57 @@ namespace AlicizaX [CustomEditor(typeof(GameObjectPoolManager))] public sealed class GameObjectPoolEditor : UnityEditor.Editor { - private readonly Dictionary _foldoutState = new Dictionary(); + private readonly Dictionary _foldoutState = new Dictionary(StringComparer.Ordinal); public override void OnInspectorGUI() { serializedObject.Update(); - DrawDefaultInspector(); serializedObject.ApplyModifiedProperties(); - var pool = (GameObjectPoolManager)target; + var poolManager = (GameObjectPoolManager)target; if (!Application.isPlaying) { - EditorGUILayout.HelpBox("Enter Play Mode to inspect runtime pool state.", MessageType.Info); + EditorGUILayout.HelpBox("Enter Play Mode to inspect the runtime pool.", MessageType.Info); return; } - if (!pool.showDetailedInfo) + if (!poolManager.showDetailedInfo) { return; } EditorGUILayout.Space(); - DrawRuntimeState(pool); + DrawRuntimeState(poolManager); } public override bool RequiresConstantRepaint() { - var pool = target as GameObjectPoolManager; - return pool != null && Application.isPlaying && pool.showDetailedInfo; + var poolManager = target as GameObjectPoolManager; + return poolManager != null && Application.isPlaying && poolManager.showDetailedInfo; } private void DrawRuntimeState(GameObjectPoolManager poolManager) { - if (!poolManager.IsReady) + GameObjectPoolSummarySnapshot summary = poolManager.GetDebugSummary(); + DrawSummary(summary); + + if (summary.WaitingForBootstrap) { - EditorGUILayout.HelpBox("GameObjectPool is initializing.", MessageType.Info); + EditorGUILayout.HelpBox("Waiting for YooAssets bootstrap before pool catalog build.", MessageType.Info); + return; + } + + if (!summary.IsReady) + { + EditorGUILayout.HelpBox("Pool manager is not ready.", MessageType.Warning); return; } List snapshots = poolManager.GetDebugSnapshots(); if (snapshots.Count == 0) { - EditorGUILayout.HelpBox("No runtime pools have been created yet.", MessageType.Info); + EditorGUILayout.HelpBox("No pooled runtime instances exist yet.", MessageType.Info); return; } @@ -59,6 +68,22 @@ namespace AlicizaX } } + private static void DrawSummary(in GameObjectPoolSummarySnapshot summary) + { + EditorGUILayout.BeginVertical("box"); + EditorGUILayout.LabelField("Runtime Summary", EditorStyles.boldLabel); + EditorGUILayout.LabelField("Ready", summary.IsReady ? "Yes" : "No"); + EditorGUILayout.LabelField("Waiting Bootstrap", summary.WaitingForBootstrap ? "Yes" : "No"); + EditorGUILayout.LabelField("Pools", summary.PoolCount.ToString()); + EditorGUILayout.LabelField("Loaded Prefabs", summary.LoadedPrefabCount.ToString()); + EditorGUILayout.LabelField("Instances", summary.TotalInstanceCount.ToString()); + EditorGUILayout.LabelField("Active", summary.ActiveInstanceCount.ToString()); + EditorGUILayout.LabelField("Inactive", summary.InactiveInstanceCount.ToString()); + EditorGUILayout.LabelField("Pending Maintenance", summary.PendingMaintenanceCount.ToString()); + EditorGUILayout.EndVertical(); + EditorGUILayout.Space(); + } + private void DrawSnapshot(GameObjectPoolSnapshot snapshot) { if (snapshot == null) @@ -66,40 +91,49 @@ namespace AlicizaX return; } - string key = $"{snapshot.group}|{snapshot.assetPath}"; - if (!_foldoutState.ContainsKey(key)) + string entryLabel = string.IsNullOrWhiteSpace(snapshot.entryName) ? snapshot.assetPath : snapshot.entryName; + string foldoutKey = string.Concat(entryLabel, "|", snapshot.assetPath); + if (!_foldoutState.ContainsKey(foldoutKey)) { - _foldoutState[key] = false; + _foldoutState[foldoutKey] = false; } - EditorGUILayout.BeginVertical("box"); - _foldoutState[key] = EditorGUILayout.Foldout( - _foldoutState[key], - $"{snapshot.group} | {snapshot.assetPath} ({snapshot.activeCount}/{snapshot.totalCount})", - true); + string foldoutLabel = string.Format( + "{0} [{1}/{2}] Hit:{3}/{4}", + entryLabel, + snapshot.activeCount, + snapshot.totalCount, + snapshot.hitCount, + snapshot.acquireCount); - if (_foldoutState[key]) + EditorGUILayout.BeginVertical("box"); + _foldoutState[foldoutKey] = EditorGUILayout.Foldout(_foldoutState[foldoutKey], foldoutLabel, true); + if (_foldoutState[foldoutKey]) { - EditorGUILayout.LabelField("Entry", snapshot.entryName); + EditorGUILayout.LabelField("Rule", entryLabel); + EditorGUILayout.LabelField("Asset Path", snapshot.assetPath); EditorGUILayout.LabelField("Loader", snapshot.loaderType.ToString()); - EditorGUILayout.LabelField("Overflow Policy", snapshot.overflowPolicy.ToString()); - EditorGUILayout.LabelField("Min Retained", snapshot.minRetained.ToString()); + EditorGUILayout.LabelField("Retain Target", snapshot.retainTarget.ToString()); EditorGUILayout.LabelField("Soft Capacity", snapshot.softCapacity.ToString()); - EditorGUILayout.LabelField("Hard Capacity", snapshot.hardCapacity.ToString()); + EditorGUILayout.LabelField("Capacity", snapshot.hardCapacity.ToString()); + EditorGUILayout.LabelField("Runtime Capacity", snapshot.runtimeHardCapacity.ToString()); EditorGUILayout.LabelField("Inactive", snapshot.inactiveCount.ToString()); EditorGUILayout.LabelField("Prefab Loaded", snapshot.prefabLoaded ? "Yes" : "No"); - EditorGUILayout.LabelField("Prefab Idle", $"{snapshot.prefabIdleDuration:F1}s"); + EditorGUILayout.LabelField("Prefab Cold", FormatSeconds(snapshot.prefabIdleDuration)); + EditorGUILayout.LabelField("Wake Count", snapshot.prefabWakeCount.ToString()); + EditorGUILayout.LabelField("Wake Gap", snapshot.prefabWakeGap < 0f ? "N/A" : FormatSeconds(snapshot.prefabWakeGap)); + EditorGUILayout.LabelField("Unload Delay", snapshot.prefabUnloadDelay < 0f ? "N/A" : FormatSeconds(snapshot.prefabUnloadDelay)); + EditorGUILayout.LabelField("Next Maintenance", snapshot.nextMaintenanceIn < 0f ? "None" : FormatSeconds(snapshot.nextMaintenanceIn)); EditorGUILayout.Space(); EditorGUILayout.LabelField("Acquire", snapshot.acquireCount.ToString()); EditorGUILayout.LabelField("Release", snapshot.releaseCount.ToString()); EditorGUILayout.LabelField("Hit", snapshot.hitCount.ToString()); EditorGUILayout.LabelField("Miss", snapshot.missCount.ToString()); EditorGUILayout.LabelField("Expand", snapshot.expandCount.ToString()); - EditorGUILayout.LabelField("Exhausted", snapshot.exhaustedCount.ToString()); - EditorGUILayout.LabelField("Auto Recycle", snapshot.autoRecycleCount.ToString()); EditorGUILayout.LabelField("Destroy", snapshot.destroyCount.ToString()); EditorGUILayout.LabelField("Peak Active", snapshot.peakActive.ToString()); - EditorGUILayout.LabelField("Peak Total", snapshot.peakTotal.ToString()); + EditorGUILayout.LabelField("Peak Active Short", snapshot.peakActiveShort.ToString()); + EditorGUILayout.LabelField("Peak Active Long", snapshot.peakActiveLong.ToString()); if (snapshot.instances.Count > 0) { @@ -116,25 +150,31 @@ namespace AlicizaX EditorGUILayout.Space(); } - private static void DrawInstance(GameObjectPoolInstanceSnapshot instance) + private static void DrawInstance(GameObjectPoolInstanceSnapshot snapshot) { - if (instance == null) + if (snapshot == null) { return; } EditorGUILayout.BeginHorizontal("box"); EditorGUILayout.BeginVertical(); - EditorGUILayout.LabelField(instance.instanceName, EditorStyles.boldLabel); - EditorGUILayout.LabelField("State", instance.isActive ? "Active" : "Inactive"); - EditorGUILayout.LabelField("Life", $"{instance.lifeDuration:F1}s"); - if (!instance.isActive) + EditorGUILayout.LabelField(snapshot.instanceName, EditorStyles.boldLabel); + EditorGUILayout.LabelField("State", snapshot.isActive ? "Active" : "Inactive"); + EditorGUILayout.LabelField("Life", FormatSeconds(snapshot.lifeDuration)); + if (!snapshot.isActive) { - EditorGUILayout.LabelField("Idle", $"{instance.idleDuration:F1}s"); + EditorGUILayout.LabelField("Idle", FormatSeconds(snapshot.idleDuration)); } + EditorGUILayout.EndVertical(); - EditorGUILayout.ObjectField(instance.gameObject, typeof(GameObject), true, GUILayout.Width(120)); + EditorGUILayout.ObjectField(snapshot.gameObject, typeof(GameObject), true, GUILayout.Width(140f)); EditorGUILayout.EndHorizontal(); } + + private static string FormatSeconds(float seconds) + { + return seconds.ToString("F2") + "s"; + } } } diff --git a/Editor/GameObjectPool/PoolConfigEditorWindow.cs b/Editor/GameObjectPool/PoolConfigEditorWindow.cs index c54df31..458f61a 100644 --- a/Editor/GameObjectPool/PoolConfigEditorWindow.cs +++ b/Editor/GameObjectPool/PoolConfigEditorWindow.cs @@ -3,6 +3,7 @@ using AlicizaX.Editor; using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.UIElements; +using UnityEditorInternal; using UnityEngine; using UnityEngine.UIElements; @@ -12,37 +13,47 @@ namespace AlicizaX { private const float MinLeftWidth = 240f; private const float InitialLeftWidth = 300f; - private const int ListItemHeight = 46; - private const string WindowTitle = "Pool Config Editor"; + private const int ListItemHeight = 24; + private const string WindowTitle = "对象池配置编辑器"; private static readonly Color LeftPanelColor = new Color(0.22f, 0.22f, 0.22f, 1f); private static readonly Color RightPanelColor = new Color(0.16f, 0.16f, 0.16f, 1f); + private static readonly Color DescriptionColor = new Color(0.72f, 0.72f, 0.72f, 1f); + private static readonly List MatchModeOptions = new List { "精确匹配", "前缀匹配" }; + private static readonly List LoaderTypeOptions = new List { "AssetBundle", "Resources" }; + [SerializeField] private PoolConfigScriptableObject _asset; + [SerializeField] + private string _assetGuid; private SerializedObject _serializedObject; private SerializedProperty _entriesProperty; private readonly List _entryIndices = new List(); + [SerializeField] private int _selectedIndex; + [SerializeField] private bool _hasUnsavedChanges; + [SerializeField] + private Vector2 _entryListScrollPosition; private ToolbarButton _saveButton; private Label _titleLabel; private VisualElement _leftPane; - private ListView _listView; + private IMGUIContainer _entryListContainer; + private ReorderableList _entryList; private ScrollView _detailScrollView; private Label _detailTitleLabel; private VisualElement _detailFieldsContainer; private VisualElement _emptyContainer; - [MenuItem("AlicizaX/GameObjectPool/Open PoolConfig Editor")] - public static void OpenWindow() + private static void OpenForAsset(PoolConfigScriptableObject asset) { - Open(Selection.activeObject as PoolConfigScriptableObject); - } + if (asset == null) + { + return; + } - public static void Open(PoolConfigScriptableObject asset) - { PoolConfigEditorWindow window = GetWindow(false, WindowTitle, true); window.minSize = new Vector2(920f, 560f); window.SetAsset(asset); @@ -58,7 +69,7 @@ namespace AlicizaX return false; } - Open(asset); + OpenForAsset(asset); return true; } @@ -66,15 +77,8 @@ namespace AlicizaX { titleContent = new GUIContent(WindowTitle, EditorGUIUtility.IconContent("ScriptableObject Icon").image); BuildUi(); - - if (_asset == null && Selection.activeObject is PoolConfigScriptableObject selectedAsset) - { - SetAsset(selectedAsset); - } - else - { - RefreshUi(); - } + RestoreWindowState(); + RefreshUi(); } private void OnEnable() @@ -85,22 +89,8 @@ namespace AlicizaX BuildUi(); } - if (_asset == null && Selection.activeObject is PoolConfigScriptableObject selectedAsset) - { - SetAsset(selectedAsset); - } - else - { - RefreshUi(); - } - } - - private void OnSelectionChange() - { - if (Selection.activeObject is PoolConfigScriptableObject selectedAsset && selectedAsset != _asset) - { - SetAsset(selectedAsset); - } + RestoreWindowState(); + RefreshUi(); } private void BuildUi() @@ -113,7 +103,7 @@ namespace AlicizaX _saveButton = new ToolbarButton(SaveAsset) { - tooltip = "Save PoolConfig" + tooltip = "保存当前 PoolConfig 配置" }; _saveButton.Add(new Image { @@ -161,63 +151,10 @@ namespace AlicizaX private void BuildLeftPane() { - _listView = new ListView - { - selectionType = SelectionType.Single, - virtualizationMethod = CollectionVirtualizationMethod.FixedHeight, - reorderable = false, - showBorder = false, - showAlternatingRowBackgrounds = AlternatingRowBackground.None, - style = - { - flexGrow = 1f, - marginBottom = 4f - }, - fixedItemHeight = ListItemHeight - }; - - _listView.makeItem = MakeListItem; - _listView.bindItem = BindListItem; - _listView.selectionChanged += _ => OnListSelectionChanged(); - _leftPane.Add(_listView); - - VisualElement footer = new VisualElement(); - footer.style.flexDirection = FlexDirection.Row; - footer.style.justifyContent = Justify.Center; - footer.style.alignItems = Align.Center; - footer.style.flexShrink = 0f; - footer.style.height = 28f; - - Button addButton = new Button(AddEntry) - { - tooltip = "Add Item" - }; - addButton.style.width = 24f; - addButton.style.height = 20f; - addButton.style.marginRight = 4f; - addButton.Add(new Image - { - image = EditorUtils.Styles.PlusIcon.image, - scaleMode = ScaleMode.ScaleToFit - }); - - Button removeButton = new Button(RemoveEntry) - { - tooltip = "Remove Item", - name = "remove-button" - }; - removeButton.style.width = 24f; - removeButton.style.height = 20f; - removeButton.Add(new Image - { - image = EditorUtils.Styles.MinusIcon.image, - scaleMode = ScaleMode.ScaleToFit - }); - removeButton.SetEnabled(false); - - footer.Add(addButton); - footer.Add(removeButton); - _leftPane.Add(footer); + _entryListContainer = new IMGUIContainer(DrawEntryList); + _entryListContainer.style.flexGrow = 1f; + _entryListContainer.style.marginBottom = 4f; + _leftPane.Add(_entryListContainer); } private void BuildRightPane(VisualElement rightPane) @@ -230,7 +167,7 @@ namespace AlicizaX justifyContent = Justify.Center } }; - _emptyContainer.Add(new HelpBox("No entry selected.", HelpBoxMessageType.Info)); + _emptyContainer.Add(new HelpBox("当前没有选中任何规则。", HelpBoxMessageType.Info)); _detailScrollView = new ScrollView { @@ -256,59 +193,53 @@ namespace AlicizaX rightPane.Add(_detailScrollView); } - private VisualElement MakeListItem() - { - VisualElement root = new VisualElement(); - root.style.height = ListItemHeight; - root.style.flexDirection = FlexDirection.Column; - root.style.justifyContent = Justify.Center; - root.style.paddingLeft = 8f; - root.style.paddingRight = 8f; - root.style.paddingTop = 6f; - root.style.paddingBottom = 6f; - root.style.marginBottom = 2f; - - Label primary = new Label { name = "primary" }; - primary.style.unityTextAlign = TextAnchor.MiddleLeft; - - Label secondary = new Label { name = "secondary" }; - secondary.style.unityTextAlign = TextAnchor.MiddleLeft; - secondary.style.fontSize = 11f; - secondary.style.color = new Color(0.72f, 0.72f, 0.72f, 1f); - - root.Add(primary); - root.Add(secondary); - return root; - } - - private void BindListItem(VisualElement element, int index) - { - SerializedProperty entry = GetEntryAt(index); - element.Q