[Opt]重构GameObjectPool
This commit is contained in:
parent
03f479dc0e
commit
49353c6d61
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -7,49 +8,57 @@ namespace AlicizaX
|
|||||||
[CustomEditor(typeof(GameObjectPoolManager))]
|
[CustomEditor(typeof(GameObjectPoolManager))]
|
||||||
public sealed class GameObjectPoolEditor : UnityEditor.Editor
|
public sealed class GameObjectPoolEditor : UnityEditor.Editor
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, bool> _foldoutState = new Dictionary<string, bool>();
|
private readonly Dictionary<string, bool> _foldoutState = new Dictionary<string, bool>(StringComparer.Ordinal);
|
||||||
|
|
||||||
public override void OnInspectorGUI()
|
public override void OnInspectorGUI()
|
||||||
{
|
{
|
||||||
serializedObject.Update();
|
serializedObject.Update();
|
||||||
|
|
||||||
DrawDefaultInspector();
|
DrawDefaultInspector();
|
||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
var pool = (GameObjectPoolManager)target;
|
var poolManager = (GameObjectPoolManager)target;
|
||||||
if (!Application.isPlaying)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pool.showDetailedInfo)
|
if (!poolManager.showDetailedInfo)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorGUILayout.Space();
|
EditorGUILayout.Space();
|
||||||
DrawRuntimeState(pool);
|
DrawRuntimeState(poolManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool RequiresConstantRepaint()
|
public override bool RequiresConstantRepaint()
|
||||||
{
|
{
|
||||||
var pool = target as GameObjectPoolManager;
|
var poolManager = target as GameObjectPoolManager;
|
||||||
return pool != null && Application.isPlaying && pool.showDetailedInfo;
|
return poolManager != null && Application.isPlaying && poolManager.showDetailedInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawRuntimeState(GameObjectPoolManager poolManager)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GameObjectPoolSnapshot> snapshots = poolManager.GetDebugSnapshots();
|
List<GameObjectPoolSnapshot> snapshots = poolManager.GetDebugSnapshots();
|
||||||
if (snapshots.Count == 0)
|
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;
|
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)
|
private void DrawSnapshot(GameObjectPoolSnapshot snapshot)
|
||||||
{
|
{
|
||||||
if (snapshot == null)
|
if (snapshot == null)
|
||||||
@ -66,40 +91,49 @@ namespace AlicizaX
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string key = $"{snapshot.group}|{snapshot.assetPath}";
|
string entryLabel = string.IsNullOrWhiteSpace(snapshot.entryName) ? snapshot.assetPath : snapshot.entryName;
|
||||||
if (!_foldoutState.ContainsKey(key))
|
string foldoutKey = string.Concat(entryLabel, "|", snapshot.assetPath);
|
||||||
|
if (!_foldoutState.ContainsKey(foldoutKey))
|
||||||
{
|
{
|
||||||
_foldoutState[key] = false;
|
_foldoutState[foldoutKey] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorGUILayout.BeginVertical("box");
|
string foldoutLabel = string.Format(
|
||||||
_foldoutState[key] = EditorGUILayout.Foldout(
|
"{0} [{1}/{2}] Hit:{3}/{4}",
|
||||||
_foldoutState[key],
|
entryLabel,
|
||||||
$"{snapshot.group} | {snapshot.assetPath} ({snapshot.activeCount}/{snapshot.totalCount})",
|
snapshot.activeCount,
|
||||||
true);
|
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("Loader", snapshot.loaderType.ToString());
|
||||||
EditorGUILayout.LabelField("Overflow Policy", snapshot.overflowPolicy.ToString());
|
EditorGUILayout.LabelField("Retain Target", snapshot.retainTarget.ToString());
|
||||||
EditorGUILayout.LabelField("Min Retained", snapshot.minRetained.ToString());
|
|
||||||
EditorGUILayout.LabelField("Soft Capacity", snapshot.softCapacity.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("Inactive", snapshot.inactiveCount.ToString());
|
||||||
EditorGUILayout.LabelField("Prefab Loaded", snapshot.prefabLoaded ? "Yes" : "No");
|
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.Space();
|
||||||
EditorGUILayout.LabelField("Acquire", snapshot.acquireCount.ToString());
|
EditorGUILayout.LabelField("Acquire", snapshot.acquireCount.ToString());
|
||||||
EditorGUILayout.LabelField("Release", snapshot.releaseCount.ToString());
|
EditorGUILayout.LabelField("Release", snapshot.releaseCount.ToString());
|
||||||
EditorGUILayout.LabelField("Hit", snapshot.hitCount.ToString());
|
EditorGUILayout.LabelField("Hit", snapshot.hitCount.ToString());
|
||||||
EditorGUILayout.LabelField("Miss", snapshot.missCount.ToString());
|
EditorGUILayout.LabelField("Miss", snapshot.missCount.ToString());
|
||||||
EditorGUILayout.LabelField("Expand", snapshot.expandCount.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("Destroy", snapshot.destroyCount.ToString());
|
||||||
EditorGUILayout.LabelField("Peak Active", snapshot.peakActive.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)
|
if (snapshot.instances.Count > 0)
|
||||||
{
|
{
|
||||||
@ -116,25 +150,31 @@ namespace AlicizaX
|
|||||||
EditorGUILayout.Space();
|
EditorGUILayout.Space();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawInstance(GameObjectPoolInstanceSnapshot instance)
|
private static void DrawInstance(GameObjectPoolInstanceSnapshot snapshot)
|
||||||
{
|
{
|
||||||
if (instance == null)
|
if (snapshot == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorGUILayout.BeginHorizontal("box");
|
EditorGUILayout.BeginHorizontal("box");
|
||||||
EditorGUILayout.BeginVertical();
|
EditorGUILayout.BeginVertical();
|
||||||
EditorGUILayout.LabelField(instance.instanceName, EditorStyles.boldLabel);
|
EditorGUILayout.LabelField(snapshot.instanceName, EditorStyles.boldLabel);
|
||||||
EditorGUILayout.LabelField("State", instance.isActive ? "Active" : "Inactive");
|
EditorGUILayout.LabelField("State", snapshot.isActive ? "Active" : "Inactive");
|
||||||
EditorGUILayout.LabelField("Life", $"{instance.lifeDuration:F1}s");
|
EditorGUILayout.LabelField("Life", FormatSeconds(snapshot.lifeDuration));
|
||||||
if (!instance.isActive)
|
if (!snapshot.isActive)
|
||||||
{
|
{
|
||||||
EditorGUILayout.LabelField("Idle", $"{instance.idleDuration:F1}s");
|
EditorGUILayout.LabelField("Idle", FormatSeconds(snapshot.idleDuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorGUILayout.EndVertical();
|
EditorGUILayout.EndVertical();
|
||||||
EditorGUILayout.ObjectField(instance.gameObject, typeof(GameObject), true, GUILayout.Width(120));
|
EditorGUILayout.ObjectField(snapshot.gameObject, typeof(GameObject), true, GUILayout.Width(140f));
|
||||||
EditorGUILayout.EndHorizontal();
|
EditorGUILayout.EndHorizontal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string FormatSeconds(float seconds)
|
||||||
|
{
|
||||||
|
return seconds.ToString("F2") + "s";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ using AlicizaX.Editor;
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.Callbacks;
|
using UnityEditor.Callbacks;
|
||||||
using UnityEditor.UIElements;
|
using UnityEditor.UIElements;
|
||||||
|
using UnityEditorInternal;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
@ -12,37 +13,47 @@ namespace AlicizaX
|
|||||||
{
|
{
|
||||||
private const float MinLeftWidth = 240f;
|
private const float MinLeftWidth = 240f;
|
||||||
private const float InitialLeftWidth = 300f;
|
private const float InitialLeftWidth = 300f;
|
||||||
private const int ListItemHeight = 46;
|
private const int ListItemHeight = 24;
|
||||||
private const string WindowTitle = "Pool Config Editor";
|
private const string WindowTitle = "对象池配置编辑器";
|
||||||
|
|
||||||
private static readonly Color LeftPanelColor = new Color(0.22f, 0.22f, 0.22f, 1f);
|
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 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<string> MatchModeOptions = new List<string> { "精确匹配", "前缀匹配" };
|
||||||
|
private static readonly List<string> LoaderTypeOptions = new List<string> { "AssetBundle", "Resources" };
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
private PoolConfigScriptableObject _asset;
|
private PoolConfigScriptableObject _asset;
|
||||||
|
[SerializeField]
|
||||||
|
private string _assetGuid;
|
||||||
private SerializedObject _serializedObject;
|
private SerializedObject _serializedObject;
|
||||||
private SerializedProperty _entriesProperty;
|
private SerializedProperty _entriesProperty;
|
||||||
private readonly List<int> _entryIndices = new List<int>();
|
private readonly List<int> _entryIndices = new List<int>();
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
private int _selectedIndex;
|
private int _selectedIndex;
|
||||||
|
[SerializeField]
|
||||||
private bool _hasUnsavedChanges;
|
private bool _hasUnsavedChanges;
|
||||||
|
[SerializeField]
|
||||||
|
private Vector2 _entryListScrollPosition;
|
||||||
|
|
||||||
private ToolbarButton _saveButton;
|
private ToolbarButton _saveButton;
|
||||||
private Label _titleLabel;
|
private Label _titleLabel;
|
||||||
private VisualElement _leftPane;
|
private VisualElement _leftPane;
|
||||||
private ListView _listView;
|
private IMGUIContainer _entryListContainer;
|
||||||
|
private ReorderableList _entryList;
|
||||||
private ScrollView _detailScrollView;
|
private ScrollView _detailScrollView;
|
||||||
private Label _detailTitleLabel;
|
private Label _detailTitleLabel;
|
||||||
private VisualElement _detailFieldsContainer;
|
private VisualElement _detailFieldsContainer;
|
||||||
private VisualElement _emptyContainer;
|
private VisualElement _emptyContainer;
|
||||||
|
|
||||||
[MenuItem("AlicizaX/GameObjectPool/Open PoolConfig Editor")]
|
private static void OpenForAsset(PoolConfigScriptableObject asset)
|
||||||
public static void OpenWindow()
|
|
||||||
{
|
{
|
||||||
Open(Selection.activeObject as PoolConfigScriptableObject);
|
if (asset == null)
|
||||||
}
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Open(PoolConfigScriptableObject asset)
|
|
||||||
{
|
|
||||||
PoolConfigEditorWindow window = GetWindow<PoolConfigEditorWindow>(false, WindowTitle, true);
|
PoolConfigEditorWindow window = GetWindow<PoolConfigEditorWindow>(false, WindowTitle, true);
|
||||||
window.minSize = new Vector2(920f, 560f);
|
window.minSize = new Vector2(920f, 560f);
|
||||||
window.SetAsset(asset);
|
window.SetAsset(asset);
|
||||||
@ -58,7 +69,7 @@ namespace AlicizaX
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Open(asset);
|
OpenForAsset(asset);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,15 +77,8 @@ namespace AlicizaX
|
|||||||
{
|
{
|
||||||
titleContent = new GUIContent(WindowTitle, EditorGUIUtility.IconContent("ScriptableObject Icon").image);
|
titleContent = new GUIContent(WindowTitle, EditorGUIUtility.IconContent("ScriptableObject Icon").image);
|
||||||
BuildUi();
|
BuildUi();
|
||||||
|
RestoreWindowState();
|
||||||
if (_asset == null && Selection.activeObject is PoolConfigScriptableObject selectedAsset)
|
RefreshUi();
|
||||||
{
|
|
||||||
SetAsset(selectedAsset);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RefreshUi();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEnable()
|
private void OnEnable()
|
||||||
@ -85,22 +89,8 @@ namespace AlicizaX
|
|||||||
BuildUi();
|
BuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_asset == null && Selection.activeObject is PoolConfigScriptableObject selectedAsset)
|
RestoreWindowState();
|
||||||
{
|
RefreshUi();
|
||||||
SetAsset(selectedAsset);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RefreshUi();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSelectionChange()
|
|
||||||
{
|
|
||||||
if (Selection.activeObject is PoolConfigScriptableObject selectedAsset && selectedAsset != _asset)
|
|
||||||
{
|
|
||||||
SetAsset(selectedAsset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildUi()
|
private void BuildUi()
|
||||||
@ -113,7 +103,7 @@ namespace AlicizaX
|
|||||||
|
|
||||||
_saveButton = new ToolbarButton(SaveAsset)
|
_saveButton = new ToolbarButton(SaveAsset)
|
||||||
{
|
{
|
||||||
tooltip = "Save PoolConfig"
|
tooltip = "保存当前 PoolConfig 配置"
|
||||||
};
|
};
|
||||||
_saveButton.Add(new Image
|
_saveButton.Add(new Image
|
||||||
{
|
{
|
||||||
@ -161,63 +151,10 @@ namespace AlicizaX
|
|||||||
|
|
||||||
private void BuildLeftPane()
|
private void BuildLeftPane()
|
||||||
{
|
{
|
||||||
_listView = new ListView
|
_entryListContainer = new IMGUIContainer(DrawEntryList);
|
||||||
{
|
_entryListContainer.style.flexGrow = 1f;
|
||||||
selectionType = SelectionType.Single,
|
_entryListContainer.style.marginBottom = 4f;
|
||||||
virtualizationMethod = CollectionVirtualizationMethod.FixedHeight,
|
_leftPane.Add(_entryListContainer);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildRightPane(VisualElement rightPane)
|
private void BuildRightPane(VisualElement rightPane)
|
||||||
@ -230,7 +167,7 @@ namespace AlicizaX
|
|||||||
justifyContent = Justify.Center
|
justifyContent = Justify.Center
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
_emptyContainer.Add(new HelpBox("No entry selected.", HelpBoxMessageType.Info));
|
_emptyContainer.Add(new HelpBox("当前没有选中任何规则。", HelpBoxMessageType.Info));
|
||||||
|
|
||||||
_detailScrollView = new ScrollView
|
_detailScrollView = new ScrollView
|
||||||
{
|
{
|
||||||
@ -256,59 +193,53 @@ namespace AlicizaX
|
|||||||
rightPane.Add(_detailScrollView);
|
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<Label>("primary").text = GetPrimaryLabel(entry);
|
|
||||||
element.Q<Label>("secondary").text = GetSecondaryLabel(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetAsset(PoolConfigScriptableObject asset)
|
private void SetAsset(PoolConfigScriptableObject asset)
|
||||||
{
|
{
|
||||||
_asset = asset;
|
_asset = asset;
|
||||||
|
_assetGuid = GetAssetGuid(asset);
|
||||||
_selectedIndex = 0;
|
_selectedIndex = 0;
|
||||||
_hasUnsavedChanges = false;
|
_hasUnsavedChanges = false;
|
||||||
|
_entryListScrollPosition = Vector2.zero;
|
||||||
|
RebindAssetState();
|
||||||
|
RefreshUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RestoreWindowState()
|
||||||
|
{
|
||||||
|
if (_asset == null && !string.IsNullOrEmpty(_assetGuid))
|
||||||
|
{
|
||||||
|
string assetPath = AssetDatabase.GUIDToAssetPath(_assetGuid);
|
||||||
|
if (!string.IsNullOrEmpty(assetPath))
|
||||||
|
{
|
||||||
|
_asset = AssetDatabase.LoadAssetAtPath<PoolConfigScriptableObject>(assetPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_asset != null)
|
||||||
|
{
|
||||||
|
_assetGuid = GetAssetGuid(_asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
RebindAssetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RebindAssetState()
|
||||||
|
{
|
||||||
|
_entryList = null;
|
||||||
|
|
||||||
if (_asset == null)
|
if (_asset == null)
|
||||||
{
|
{
|
||||||
|
_assetGuid = string.Empty;
|
||||||
_serializedObject = null;
|
_serializedObject = null;
|
||||||
_entriesProperty = null;
|
_entriesProperty = null;
|
||||||
}
|
_entryIndices.Clear();
|
||||||
else
|
return;
|
||||||
{
|
|
||||||
_asset.Normalize();
|
|
||||||
_serializedObject = new SerializedObject(_asset);
|
|
||||||
_entriesProperty = _serializedObject.FindProperty("entries");
|
|
||||||
RebuildEntryIndices();
|
|
||||||
ClampSelection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshUi();
|
_asset.Normalize();
|
||||||
|
_serializedObject = new SerializedObject(_asset);
|
||||||
|
_entriesProperty = _serializedObject.FindProperty("entries");
|
||||||
|
RebuildEntryIndices();
|
||||||
|
ClampSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshUi()
|
private void RefreshUi()
|
||||||
@ -324,24 +255,23 @@ namespace AlicizaX
|
|||||||
if (_asset == null || _serializedObject == null)
|
if (_asset == null || _serializedObject == null)
|
||||||
{
|
{
|
||||||
_entryIndices.Clear();
|
_entryIndices.Clear();
|
||||||
_listView.itemsSource = _entryIndices;
|
_entryList = null;
|
||||||
_listView.Rebuild();
|
_entryListContainer?.MarkDirtyRepaint();
|
||||||
_emptyContainer.Clear();
|
_emptyContainer.Clear();
|
||||||
_emptyContainer.Add(new HelpBox("Select or double-click a PoolConfig asset to edit it in this window.", HelpBoxMessageType.Info));
|
_emptyContainer.Add(new HelpBox("请选择或双击 PoolConfig 资源,然后在这个窗口里编辑对象池规则。", HelpBoxMessageType.Info));
|
||||||
_emptyContainer.style.display = DisplayStyle.Flex;
|
_emptyContainer.style.display = DisplayStyle.Flex;
|
||||||
_detailScrollView.style.display = DisplayStyle.None;
|
_detailScrollView.style.display = DisplayStyle.None;
|
||||||
UpdateRemoveButtonState();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RebuildEntryIndices();
|
RebuildEntryIndices();
|
||||||
ClampSelection();
|
ClampSelection();
|
||||||
|
EnsureEntryReorderableList();
|
||||||
|
SyncEntryListSelection();
|
||||||
|
_entryListContainer?.MarkDirtyRepaint();
|
||||||
|
|
||||||
_listView.itemsSource = _entryIndices;
|
|
||||||
_listView.Rebuild();
|
|
||||||
if (_entryIndices.Count > 0)
|
if (_entryIndices.Count > 0)
|
||||||
{
|
{
|
||||||
_listView.SetSelectionWithoutNotify(new[] { _selectedIndex });
|
|
||||||
_emptyContainer.style.display = DisplayStyle.None;
|
_emptyContainer.style.display = DisplayStyle.None;
|
||||||
_detailScrollView.style.display = DisplayStyle.Flex;
|
_detailScrollView.style.display = DisplayStyle.Flex;
|
||||||
RebuildDetailFields();
|
RebuildDetailFields();
|
||||||
@ -349,12 +279,10 @@ namespace AlicizaX
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
_emptyContainer.Clear();
|
_emptyContainer.Clear();
|
||||||
_emptyContainer.Add(new HelpBox("No entry selected.", HelpBoxMessageType.Info));
|
_emptyContainer.Add(new HelpBox("当前没有可编辑的规则,请先新增一条对象池规则。", HelpBoxMessageType.Info));
|
||||||
_emptyContainer.style.display = DisplayStyle.Flex;
|
_emptyContainer.style.display = DisplayStyle.Flex;
|
||||||
_detailScrollView.style.display = DisplayStyle.None;
|
_detailScrollView.style.display = DisplayStyle.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateRemoveButtonState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RebuildEntryIndices()
|
private void RebuildEntryIndices()
|
||||||
@ -367,24 +295,121 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateRemoveButtonState()
|
private void DrawEntryList()
|
||||||
{
|
{
|
||||||
Button removeButton = _leftPane?.Q<Button>("remove-button");
|
if (_asset == null || _serializedObject == null || _entriesProperty == null)
|
||||||
removeButton?.SetEnabled(_entryIndices.Count > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnListSelectionChanged()
|
|
||||||
{
|
|
||||||
if (_listView.selectedIndex < 0 || _listView.selectedIndex >= _entryIndices.Count)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_selectedIndex = _listView.selectedIndex;
|
EnsureEntryReorderableList();
|
||||||
UpdateRemoveButtonState();
|
SyncEntryListSelection();
|
||||||
|
|
||||||
|
_serializedObject.Update();
|
||||||
|
_entryListScrollPosition = EditorGUILayout.BeginScrollView(_entryListScrollPosition, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
|
||||||
|
Rect listRect = GUILayoutUtility.GetRect(0f, _entryList.GetHeight(), GUILayout.ExpandWidth(true));
|
||||||
|
_entryList.DoList(listRect);
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureEntryReorderableList()
|
||||||
|
{
|
||||||
|
if (_serializedObject == null || _entriesProperty == null)
|
||||||
|
{
|
||||||
|
_entryList = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entryList != null && _entryList.serializedProperty == _entriesProperty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_entryList = new ReorderableList(_serializedObject, _entriesProperty, true, true, true, true)
|
||||||
|
{
|
||||||
|
elementHeight = ListItemHeight + 6f
|
||||||
|
};
|
||||||
|
|
||||||
|
_entryList.drawHeaderCallback = rect =>
|
||||||
|
{
|
||||||
|
EditorGUI.LabelField(rect, Utility.Text.Format("对象池规则 ({0})", _entriesProperty.arraySize), EditorStyles.boldLabel);
|
||||||
|
};
|
||||||
|
_entryList.drawElementCallback = DrawEntryListElement;
|
||||||
|
_entryList.onSelectCallback = OnEntryListSelected;
|
||||||
|
_entryList.onAddCallback = OnEntryListAdd;
|
||||||
|
_entryList.onRemoveCallback = OnEntryListRemove;
|
||||||
|
_entryList.onReorderCallback = OnEntryListReordered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawEntryListElement(Rect rect, int index, bool isActive, bool isFocused)
|
||||||
|
{
|
||||||
|
SerializedProperty entry = GetEntryAt(index);
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect.y += 5f;
|
||||||
|
|
||||||
|
string primaryLabel = GetPrimaryLabel(entry);
|
||||||
|
string assetPath = entry.FindPropertyRelative("assetPath").stringValue;
|
||||||
|
string tooltip = string.IsNullOrWhiteSpace(assetPath) || string.Equals(primaryLabel, assetPath, System.StringComparison.Ordinal)
|
||||||
|
? primaryLabel
|
||||||
|
: assetPath;
|
||||||
|
|
||||||
|
Rect primaryRect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight);
|
||||||
|
EditorGUI.LabelField(primaryRect, new GUIContent(primaryLabel, tooltip), EditorStyles.boldLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEntryListSelected(ReorderableList list)
|
||||||
|
{
|
||||||
|
if (list.index < 0 || list.index >= _entryIndices.Count)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectedIndex = list.index;
|
||||||
RebuildDetailFields();
|
RebuildDetailFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEntryListAdd(ReorderableList list)
|
||||||
|
{
|
||||||
|
AddEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEntryListRemove(ReorderableList list)
|
||||||
|
{
|
||||||
|
RemoveEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEntryListReordered(ReorderableList list)
|
||||||
|
{
|
||||||
|
if (_asset == null || _serializedObject == null || _entriesProperty == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshEntryPriorities();
|
||||||
|
_serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
||||||
|
_asset.Normalize();
|
||||||
|
RebuildEntryIndices();
|
||||||
|
_selectedIndex = Mathf.Clamp(list.index, 0, _entryIndices.Count - 1);
|
||||||
|
_hasUnsavedChanges = true;
|
||||||
|
RefreshTitle();
|
||||||
|
RebuildDetailFields();
|
||||||
|
_entryListContainer?.MarkDirtyRepaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SyncEntryListSelection()
|
||||||
|
{
|
||||||
|
if (_entryList == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_entryList.index = _entryIndices.Count == 0 ? -1 : _selectedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
private void RebuildDetailFields()
|
private void RebuildDetailFields()
|
||||||
{
|
{
|
||||||
if (_asset == null || _serializedObject == null)
|
if (_asset == null || _serializedObject == null)
|
||||||
@ -409,16 +434,124 @@ namespace AlicizaX
|
|||||||
bool enterChildren = true;
|
bool enterChildren = true;
|
||||||
while (iterator.NextVisible(enterChildren) && !SerializedProperty.EqualContents(iterator, end))
|
while (iterator.NextVisible(enterChildren) && !SerializedProperty.EqualContents(iterator, end))
|
||||||
{
|
{
|
||||||
PropertyField field = new PropertyField(iterator.Copy());
|
SerializedProperty currentProperty = iterator.Copy();
|
||||||
field.RegisterCallback<SerializedPropertyChangeEvent>(OnDetailPropertyChanged);
|
if (!ShouldDisplayField(currentProperty.name))
|
||||||
_detailFieldsContainer.Add(field);
|
{
|
||||||
|
enterChildren = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_detailFieldsContainer.Add(CreateDetailField(currentProperty));
|
||||||
enterChildren = false;
|
enterChildren = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_detailFieldsContainer.Bind(_serializedObject);
|
_detailFieldsContainer.Bind(_serializedObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VisualElement CreateDetailField(SerializedProperty property)
|
||||||
|
{
|
||||||
|
if (TryCreateLocalizedEnumField(property, out VisualElement enumFieldContainer))
|
||||||
|
{
|
||||||
|
return enumFieldContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
VisualElement container = new VisualElement();
|
||||||
|
container.style.flexDirection = FlexDirection.Column;
|
||||||
|
container.style.marginBottom = 4f;
|
||||||
|
|
||||||
|
string label = property.name == "group" ? "\u5206\u7ec4" : GetFieldLabel(property.name);
|
||||||
|
PropertyField field = new PropertyField(property, label);
|
||||||
|
if (IsReadOnlyField(property.name))
|
||||||
|
{
|
||||||
|
field.SetEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
field.RegisterCallback<SerializedPropertyChangeEvent>(OnDetailPropertyChanged);
|
||||||
|
container.Add(field);
|
||||||
|
|
||||||
|
string description = property.name == "group"
|
||||||
|
? "\u7528\u4e8e GameObjectPoolManager \u4e0b\u7684\u7a7a\u95f2\u8282\u70b9\u5f52\u7c7b\uff0c\u4e0d\u586b\u6216\u7a7a\u503c\u4f1a\u81ea\u52a8\u56de\u843d\u5230 DefaultGroup\u3002"
|
||||||
|
: GetFieldDescription(property.name);
|
||||||
|
if (!string.IsNullOrWhiteSpace(description))
|
||||||
|
{
|
||||||
|
Label descriptionLabel = new Label(description);
|
||||||
|
descriptionLabel.style.whiteSpace = WhiteSpace.Normal;
|
||||||
|
descriptionLabel.style.fontSize = 11f;
|
||||||
|
descriptionLabel.style.color = DescriptionColor;
|
||||||
|
descriptionLabel.style.marginLeft = 4f;
|
||||||
|
descriptionLabel.style.marginRight = 4f;
|
||||||
|
descriptionLabel.style.marginTop = -2f;
|
||||||
|
descriptionLabel.style.marginBottom = 6f;
|
||||||
|
container.Add(descriptionLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryCreateLocalizedEnumField(SerializedProperty property, out VisualElement container)
|
||||||
|
{
|
||||||
|
List<string> options = GetEnumOptions(property.name);
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
container = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
container = new VisualElement();
|
||||||
|
container.style.flexDirection = FlexDirection.Column;
|
||||||
|
container.style.marginBottom = 4f;
|
||||||
|
|
||||||
|
int currentIndex = Mathf.Clamp(property.enumValueIndex, 0, options.Count - 1);
|
||||||
|
PopupField<string> popupField = new PopupField<string>(GetFieldLabel(property.name), options, currentIndex);
|
||||||
|
string propertyPath = property.propertyPath;
|
||||||
|
popupField.RegisterValueChangedCallback(_ =>
|
||||||
|
{
|
||||||
|
if (_serializedObject == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int selectedIndex = options.IndexOf(popupField.value);
|
||||||
|
if (selectedIndex < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_serializedObject.Update();
|
||||||
|
SerializedProperty targetProperty = _serializedObject.FindProperty(propertyPath);
|
||||||
|
if (targetProperty == null || targetProperty.enumValueIndex == selectedIndex)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetProperty.enumValueIndex = selectedIndex;
|
||||||
|
ApplyDetailPropertyChanges();
|
||||||
|
});
|
||||||
|
container.Add(popupField);
|
||||||
|
|
||||||
|
string description = GetFieldDescription(property.name);
|
||||||
|
if (!string.IsNullOrWhiteSpace(description))
|
||||||
|
{
|
||||||
|
Label descriptionLabel = new Label(description);
|
||||||
|
descriptionLabel.style.whiteSpace = WhiteSpace.Normal;
|
||||||
|
descriptionLabel.style.fontSize = 11f;
|
||||||
|
descriptionLabel.style.color = DescriptionColor;
|
||||||
|
descriptionLabel.style.marginLeft = 4f;
|
||||||
|
descriptionLabel.style.marginRight = 4f;
|
||||||
|
descriptionLabel.style.marginTop = -2f;
|
||||||
|
descriptionLabel.style.marginBottom = 6f;
|
||||||
|
container.Add(descriptionLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnDetailPropertyChanged(SerializedPropertyChangeEvent evt)
|
private void OnDetailPropertyChanged(SerializedPropertyChangeEvent evt)
|
||||||
|
{
|
||||||
|
ApplyDetailPropertyChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyDetailPropertyChanges()
|
||||||
{
|
{
|
||||||
if (_asset == null || _serializedObject == null)
|
if (_asset == null || _serializedObject == null)
|
||||||
{
|
{
|
||||||
@ -428,7 +561,7 @@ namespace AlicizaX
|
|||||||
_serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
_serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
||||||
_asset.Normalize();
|
_asset.Normalize();
|
||||||
_hasUnsavedChanges = true;
|
_hasUnsavedChanges = true;
|
||||||
_listView?.RefreshItems();
|
_entryListContainer?.MarkDirtyRepaint();
|
||||||
RefreshTitle();
|
RefreshTitle();
|
||||||
_detailTitleLabel.text = GetPrimaryLabel(GetSelectedProperty());
|
_detailTitleLabel.text = GetPrimaryLabel(GetSelectedProperty());
|
||||||
}
|
}
|
||||||
@ -446,6 +579,7 @@ namespace AlicizaX
|
|||||||
_entriesProperty.InsertArrayElementAtIndex(index);
|
_entriesProperty.InsertArrayElementAtIndex(index);
|
||||||
SerializedProperty property = _entriesProperty.GetArrayElementAtIndex(index);
|
SerializedProperty property = _entriesProperty.GetArrayElementAtIndex(index);
|
||||||
InitializeNewEntry(property, index);
|
InitializeNewEntry(property, index);
|
||||||
|
RefreshEntryPriorities();
|
||||||
|
|
||||||
_serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
_serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
||||||
_asset.Normalize();
|
_asset.Normalize();
|
||||||
@ -453,7 +587,6 @@ namespace AlicizaX
|
|||||||
_selectedIndex = index;
|
_selectedIndex = index;
|
||||||
_hasUnsavedChanges = true;
|
_hasUnsavedChanges = true;
|
||||||
RefreshUi();
|
RefreshUi();
|
||||||
_listView.SetSelectionWithoutNotify(new[] { _selectedIndex });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveEntry()
|
private void RemoveEntry()
|
||||||
@ -465,6 +598,7 @@ namespace AlicizaX
|
|||||||
|
|
||||||
_serializedObject.Update();
|
_serializedObject.Update();
|
||||||
_entriesProperty.DeleteArrayElementAtIndex(_selectedIndex);
|
_entriesProperty.DeleteArrayElementAtIndex(_selectedIndex);
|
||||||
|
RefreshEntryPriorities();
|
||||||
_serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
_serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
||||||
_asset.Normalize();
|
_asset.Normalize();
|
||||||
RebuildEntryIndices();
|
RebuildEntryIndices();
|
||||||
@ -476,38 +610,47 @@ namespace AlicizaX
|
|||||||
|
|
||||||
_hasUnsavedChanges = true;
|
_hasUnsavedChanges = true;
|
||||||
RefreshUi();
|
RefreshUi();
|
||||||
if (_entryIndices.Count > 0)
|
|
||||||
{
|
|
||||||
_listView.SetSelectionWithoutNotify(new[] { _selectedIndex });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeNewEntry(SerializedProperty property, int index)
|
private void InitializeNewEntry(SerializedProperty property, int index)
|
||||||
{
|
{
|
||||||
property.FindPropertyRelative("entryName").stringValue = $"PoolEntry{index + 1}";
|
property.FindPropertyRelative("entryName").stringValue = Utility.Text.Format("对象池规则{0}", index + 1);
|
||||||
property.FindPropertyRelative("group").stringValue = PoolEntry.DefaultGroup;
|
property.FindPropertyRelative("group").stringValue = PoolEntry.DefaultGroup;
|
||||||
property.FindPropertyRelative("assetPath").stringValue = string.Empty;
|
property.FindPropertyRelative("assetPath").stringValue = string.Empty;
|
||||||
property.FindPropertyRelative("matchMode").enumValueIndex = (int)PoolMatchMode.Exact;
|
property.FindPropertyRelative("matchMode").enumValueIndex = (int)PoolMatchMode.Exact;
|
||||||
property.FindPropertyRelative("loaderType").enumValueIndex = (int)PoolResourceLoaderType.AssetBundle;
|
property.FindPropertyRelative("loaderType").enumValueIndex = (int)PoolResourceLoaderType.AssetBundle;
|
||||||
property.FindPropertyRelative("overflowPolicy").enumValueIndex = (int)PoolOverflowPolicy.FailFast;
|
property.FindPropertyRelative("category").enumValueIndex = (int)PoolCategory.Default;
|
||||||
property.FindPropertyRelative("trimPolicy").enumValueIndex = (int)PoolTrimPolicy.IdleOnly;
|
|
||||||
property.FindPropertyRelative("activationMode").enumValueIndex = (int)PoolActivationMode.SetActive;
|
|
||||||
property.FindPropertyRelative("resetMode").enumValueIndex = (int)PoolResetMode.PoolableCallbacks;
|
|
||||||
property.FindPropertyRelative("minRetained").intValue = 0;
|
|
||||||
property.FindPropertyRelative("softCapacity").intValue = 8;
|
property.FindPropertyRelative("softCapacity").intValue = 8;
|
||||||
property.FindPropertyRelative("hardCapacity").intValue = 8;
|
property.FindPropertyRelative("hardCapacity").intValue = 16;
|
||||||
property.FindPropertyRelative("idleTrimDelay").floatValue = 30f;
|
|
||||||
property.FindPropertyRelative("prefabUnloadDelay").floatValue = 60f;
|
|
||||||
property.FindPropertyRelative("autoRecycleDelay").floatValue = 0f;
|
|
||||||
property.FindPropertyRelative("trimBatchPerTick").intValue = 2;
|
|
||||||
property.FindPropertyRelative("warmupBatchPerFrame").intValue = 2;
|
|
||||||
property.FindPropertyRelative("warmupFrameBudgetMs").floatValue = 1f;
|
|
||||||
property.FindPropertyRelative("allowRuntimeExpand").boolValue = false;
|
|
||||||
property.FindPropertyRelative("keepPrefabResident").boolValue = false;
|
|
||||||
property.FindPropertyRelative("aggressiveTrimOnLowMemory").boolValue = false;
|
|
||||||
property.FindPropertyRelative("priority").intValue = index;
|
property.FindPropertyRelative("priority").intValue = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RefreshEntryPriorities()
|
||||||
|
{
|
||||||
|
if (_entriesProperty == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = _entriesProperty.arraySize;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
SerializedProperty entry = _entriesProperty.GetArrayElementAtIndex(i);
|
||||||
|
entry.FindPropertyRelative("priority").intValue = count - i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetAssetGuid(PoolConfigScriptableObject asset)
|
||||||
|
{
|
||||||
|
if (asset == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
string assetPath = AssetDatabase.GetAssetPath(asset);
|
||||||
|
return string.IsNullOrEmpty(assetPath) ? string.Empty : AssetDatabase.AssetPathToGUID(assetPath);
|
||||||
|
}
|
||||||
|
|
||||||
private void SaveAsset()
|
private void SaveAsset()
|
||||||
{
|
{
|
||||||
if (_asset == null || _serializedObject == null)
|
if (_asset == null || _serializedObject == null)
|
||||||
@ -526,10 +669,10 @@ namespace AlicizaX
|
|||||||
|
|
||||||
private void RefreshTitle()
|
private void RefreshTitle()
|
||||||
{
|
{
|
||||||
string assetLabel = _asset == null ? "No PoolConfig Selected" : _asset.name;
|
string assetLabel = _asset == null ? "未选择 PoolConfig" : _asset.name;
|
||||||
if (_hasUnsavedChanges)
|
if (_hasUnsavedChanges)
|
||||||
{
|
{
|
||||||
assetLabel += " *";
|
assetLabel = Utility.Text.Format("{0} *", assetLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
_titleLabel.text = assetLabel;
|
_titleLabel.text = assetLabel;
|
||||||
@ -557,7 +700,7 @@ namespace AlicizaX
|
|||||||
{
|
{
|
||||||
if (property == null)
|
if (property == null)
|
||||||
{
|
{
|
||||||
return "<Missing>";
|
return "<规则缺失>";
|
||||||
}
|
}
|
||||||
|
|
||||||
string entryName = property.FindPropertyRelative("entryName").stringValue;
|
string entryName = property.FindPropertyRelative("entryName").stringValue;
|
||||||
@ -567,19 +710,59 @@ namespace AlicizaX
|
|||||||
return entryName;
|
return entryName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.IsNullOrWhiteSpace(assetPath) ? "<Unnamed Entry>" : assetPath;
|
return string.IsNullOrWhiteSpace(assetPath) ? "<未命名规则>" : assetPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetSecondaryLabel(SerializedProperty property)
|
private static List<string> GetEnumOptions(string propertyName)
|
||||||
{
|
{
|
||||||
if (property == null)
|
return propertyName switch
|
||||||
{
|
{
|
||||||
return string.Empty;
|
"matchMode" => MatchModeOptions,
|
||||||
}
|
"loaderType" => LoaderTypeOptions,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
string group = property.FindPropertyRelative("group").stringValue;
|
private static bool ShouldDisplayField(string propertyName)
|
||||||
string assetPath = property.FindPropertyRelative("assetPath").stringValue;
|
{
|
||||||
return string.IsNullOrWhiteSpace(assetPath) ? group : $"{group} | {assetPath}";
|
return propertyName != "category";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsReadOnlyField(string propertyName)
|
||||||
|
{
|
||||||
|
return propertyName == "priority";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetFieldLabel(string propertyName)
|
||||||
|
{
|
||||||
|
return propertyName switch
|
||||||
|
{
|
||||||
|
"entryName" => "规则名称",
|
||||||
|
"group" => "分组",
|
||||||
|
"assetPath" => "资源路径",
|
||||||
|
"matchMode" => "匹配模式",
|
||||||
|
"loaderType" => "加载器类型",
|
||||||
|
"softCapacity" => "软容量",
|
||||||
|
"hardCapacity" => "容量",
|
||||||
|
"priority" => "优先级",
|
||||||
|
_ => propertyName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetFieldDescription(string propertyName)
|
||||||
|
{
|
||||||
|
return propertyName switch
|
||||||
|
{
|
||||||
|
"entryName" => "规则名称就是主定位信息。列表、调试和问题排查都直接看这个名字。",
|
||||||
|
"group" => "用于 GameObjectPoolManager 下的空闲节点归类。不填或空值会自动回落到 DefaultGroup。",
|
||||||
|
"assetPath" => "要匹配的资源路径。精确匹配填完整路径,前缀匹配可填写目录前缀。",
|
||||||
|
"matchMode" => "精确匹配只命中单一路径,前缀匹配适合同目录或同类资源共用规则。",
|
||||||
|
"loaderType" => "决定 Prefab 从哪个资源通道加载。AssetBundle 走包体资源,Resources 走内置目录。",
|
||||||
|
"softCapacity" => "超过该值后,维护阶段会优先回收空闲实例。",
|
||||||
|
"hardCapacity" => "基础容量。超过这个值会自动扩容并输出警告,后续维护回收会再收回到这个基准。",
|
||||||
|
"priority" => "由左侧拖拽顺序自动维护,越靠上优先级越高。",
|
||||||
|
_ => string.Empty
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClampSelection()
|
private void ClampSelection()
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace AlicizaX
|
namespace AlicizaX
|
||||||
{
|
{
|
||||||
@ -8,10 +7,7 @@ namespace AlicizaX
|
|||||||
{
|
{
|
||||||
public override void OnInspectorGUI()
|
public override void OnInspectorGUI()
|
||||||
{
|
{
|
||||||
if (GUILayout.Button("Open Editor", GUILayout.Width(160f), GUILayout.Height(28f)))
|
DrawDefaultInspector();
|
||||||
{
|
|
||||||
PoolConfigEditorWindow.Open((PoolConfigScriptableObject)target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,7 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace AlicizaX
|
namespace AlicizaX
|
||||||
{
|
{
|
||||||
[DisallowMultipleComponent]
|
|
||||||
public sealed class PoolAutoRecycleAfterSeconds : MonoBehaviour, IPoolAutoRecycle
|
|
||||||
{
|
|
||||||
[Min(0f)]
|
|
||||||
public float delaySeconds = 1f;
|
|
||||||
|
|
||||||
public bool TryGetAutoRecycleDelay(out float delay)
|
|
||||||
{
|
|
||||||
delay = delaySeconds;
|
|
||||||
return delay > 0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
public sealed class PoolAutoRecycleParticleStop : MonoBehaviour, IGameObjectPoolable
|
public sealed class PoolAutoRecycleParticleStop : MonoBehaviour, IGameObjectPoolable
|
||||||
{
|
{
|
||||||
@ -76,168 +61,18 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[DisallowMultipleComponent]
|
|
||||||
public sealed class PoolResetRigidbodyState : MonoBehaviour, IPoolResettablePhysics
|
|
||||||
{
|
|
||||||
[SerializeField] private bool includeChildren = true;
|
|
||||||
private Rigidbody[] _rigidbodies3D;
|
|
||||||
private Rigidbody2D[] _rigidbodies2D;
|
|
||||||
|
|
||||||
private void EnsureCache()
|
|
||||||
{
|
|
||||||
if (_rigidbodies3D == null)
|
|
||||||
{
|
|
||||||
_rigidbodies3D = includeChildren ? GetComponentsInChildren<Rigidbody>(true) : GetComponents<Rigidbody>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_rigidbodies2D == null)
|
|
||||||
{
|
|
||||||
_rigidbodies2D = includeChildren ? GetComponentsInChildren<Rigidbody2D>(true) : GetComponents<Rigidbody2D>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetPhysicsState()
|
|
||||||
{
|
|
||||||
EnsureCache();
|
|
||||||
|
|
||||||
for (int i = 0; i < _rigidbodies3D.Length; i++)
|
|
||||||
{
|
|
||||||
Rigidbody body = _rigidbodies3D[i];
|
|
||||||
if (body == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.velocity = Vector3.zero;
|
|
||||||
body.angularVelocity = Vector3.zero;
|
|
||||||
body.Sleep();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < _rigidbodies2D.Length; i++)
|
|
||||||
{
|
|
||||||
Rigidbody2D body = _rigidbodies2D[i];
|
|
||||||
if (body == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.velocity = Vector2.zero;
|
|
||||||
body.angularVelocity = 0f;
|
|
||||||
body.Sleep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DisallowMultipleComponent]
|
|
||||||
public sealed class PoolResetParticleSystems : MonoBehaviour, IPoolResettableVisual
|
|
||||||
{
|
|
||||||
[SerializeField] private bool includeChildren = true;
|
|
||||||
private ParticleSystem[] _particleSystems;
|
|
||||||
|
|
||||||
private void EnsureCache()
|
|
||||||
{
|
|
||||||
if (_particleSystems == null)
|
|
||||||
{
|
|
||||||
_particleSystems = includeChildren
|
|
||||||
? GetComponentsInChildren<ParticleSystem>(true)
|
|
||||||
: GetComponents<ParticleSystem>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetVisualState()
|
|
||||||
{
|
|
||||||
EnsureCache();
|
|
||||||
|
|
||||||
for (int i = 0; i < _particleSystems.Length; i++)
|
|
||||||
{
|
|
||||||
ParticleSystem particle = _particleSystems[i];
|
|
||||||
if (particle == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
particle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
|
|
||||||
particle.Clear(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DisallowMultipleComponent]
|
|
||||||
public sealed class PoolResetTrailRenderers : MonoBehaviour, IPoolResettableVisual
|
|
||||||
{
|
|
||||||
[SerializeField] private bool includeChildren = true;
|
|
||||||
private TrailRenderer[] _trailRenderers;
|
|
||||||
|
|
||||||
private void EnsureCache()
|
|
||||||
{
|
|
||||||
if (_trailRenderers == null)
|
|
||||||
{
|
|
||||||
_trailRenderers = includeChildren
|
|
||||||
? GetComponentsInChildren<TrailRenderer>(true)
|
|
||||||
: GetComponents<TrailRenderer>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetVisualState()
|
|
||||||
{
|
|
||||||
EnsureCache();
|
|
||||||
|
|
||||||
for (int i = 0; i < _trailRenderers.Length; i++)
|
|
||||||
{
|
|
||||||
TrailRenderer trail = _trailRenderers[i];
|
|
||||||
if (trail == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
trail.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DisallowMultipleComponent]
|
|
||||||
public sealed class PoolResetAnimatorState : MonoBehaviour, IPoolResettableAnimation
|
|
||||||
{
|
|
||||||
[SerializeField] private bool includeChildren = true;
|
|
||||||
private Animator[] _animators;
|
|
||||||
|
|
||||||
private void EnsureCache()
|
|
||||||
{
|
|
||||||
if (_animators == null)
|
|
||||||
{
|
|
||||||
_animators = includeChildren ? GetComponentsInChildren<Animator>(true) : GetComponents<Animator>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetAnimationState()
|
|
||||||
{
|
|
||||||
EnsureCache();
|
|
||||||
|
|
||||||
for (int i = 0; i < _animators.Length; i++)
|
|
||||||
{
|
|
||||||
Animator animator = _animators[i];
|
|
||||||
if (animator == null || !animator.isActiveAndEnabled)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
animator.Rebind();
|
|
||||||
animator.Update(0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
public sealed class PoolSleepableGameObjectGroup : MonoBehaviour, IPoolSleepable
|
public sealed class PoolSleepableGameObjectGroup : MonoBehaviour, IPoolSleepable
|
||||||
{
|
{
|
||||||
[SerializeField] private List<Behaviour> disableOnSleep = new List<Behaviour>();
|
[SerializeField] private Behaviour[] disableOnSleep;
|
||||||
[SerializeField] private List<Renderer> hideOnSleep = new List<Renderer>();
|
[SerializeField] private Renderer[] hideOnSleep;
|
||||||
[SerializeField] private List<Collider> disableCollider3D = new List<Collider>();
|
[SerializeField] private Collider[] disableCollider3D;
|
||||||
[SerializeField] private List<Collider2D> disableCollider2D = new List<Collider2D>();
|
[SerializeField] private Collider2D[] disableCollider2D;
|
||||||
|
|
||||||
public void EnterSleep()
|
public void EnterSleep()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < disableOnSleep.Count; i++)
|
int disableCount = disableOnSleep == null ? 0 : disableOnSleep.Length;
|
||||||
|
for (int i = 0; i < disableCount; i++)
|
||||||
{
|
{
|
||||||
if (disableOnSleep[i] != null)
|
if (disableOnSleep[i] != null)
|
||||||
{
|
{
|
||||||
@ -245,7 +80,8 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < hideOnSleep.Count; i++)
|
int hideCount = hideOnSleep == null ? 0 : hideOnSleep.Length;
|
||||||
|
for (int i = 0; i < hideCount; i++)
|
||||||
{
|
{
|
||||||
if (hideOnSleep[i] != null)
|
if (hideOnSleep[i] != null)
|
||||||
{
|
{
|
||||||
@ -253,7 +89,8 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < disableCollider3D.Count; i++)
|
int collider3DCount = disableCollider3D == null ? 0 : disableCollider3D.Length;
|
||||||
|
for (int i = 0; i < collider3DCount; i++)
|
||||||
{
|
{
|
||||||
if (disableCollider3D[i] != null)
|
if (disableCollider3D[i] != null)
|
||||||
{
|
{
|
||||||
@ -261,7 +98,8 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < disableCollider2D.Count; i++)
|
int collider2DCount = disableCollider2D == null ? 0 : disableCollider2D.Length;
|
||||||
|
for (int i = 0; i < collider2DCount; i++)
|
||||||
{
|
{
|
||||||
if (disableCollider2D[i] != null)
|
if (disableCollider2D[i] != null)
|
||||||
{
|
{
|
||||||
@ -272,7 +110,8 @@ namespace AlicizaX
|
|||||||
|
|
||||||
public void ExitSleep(in PoolSpawnContext context)
|
public void ExitSleep(in PoolSpawnContext context)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < disableOnSleep.Count; i++)
|
int disableCount = disableOnSleep == null ? 0 : disableOnSleep.Length;
|
||||||
|
for (int i = 0; i < disableCount; i++)
|
||||||
{
|
{
|
||||||
if (disableOnSleep[i] != null)
|
if (disableOnSleep[i] != null)
|
||||||
{
|
{
|
||||||
@ -280,7 +119,8 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < hideOnSleep.Count; i++)
|
int hideCount = hideOnSleep == null ? 0 : hideOnSleep.Length;
|
||||||
|
for (int i = 0; i < hideCount; i++)
|
||||||
{
|
{
|
||||||
if (hideOnSleep[i] != null)
|
if (hideOnSleep[i] != null)
|
||||||
{
|
{
|
||||||
@ -288,7 +128,8 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < disableCollider3D.Count; i++)
|
int collider3DCount = disableCollider3D == null ? 0 : disableCollider3D.Length;
|
||||||
|
for (int i = 0; i < collider3DCount; i++)
|
||||||
{
|
{
|
||||||
if (disableCollider3D[i] != null)
|
if (disableCollider3D[i] != null)
|
||||||
{
|
{
|
||||||
@ -296,7 +137,8 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < disableCollider2D.Count; i++)
|
int collider2DCount = disableCollider2D == null ? 0 : disableCollider2D.Length;
|
||||||
|
for (int i = 0; i < collider2DCount; i++)
|
||||||
{
|
{
|
||||||
if (disableCollider2D[i] != null)
|
if (disableCollider2D[i] != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using AlicizaX.ObjectPool;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace AlicizaX
|
namespace AlicizaX
|
||||||
@ -16,84 +16,34 @@ namespace AlicizaX
|
|||||||
Prefix = 1
|
Prefix = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PoolOverflowPolicy
|
public enum PoolCategory
|
||||||
{
|
{
|
||||||
FailFast = 0,
|
Default = 0,
|
||||||
InstantiateOneShot = 1,
|
Effect = 1,
|
||||||
AutoExpand = 2,
|
Monster = 2,
|
||||||
RecycleOldestInactive = 3,
|
Building = 3,
|
||||||
DropNewestRequest = 4
|
UI = 4,
|
||||||
}
|
Custom = 5
|
||||||
|
|
||||||
public enum PoolTrimPolicy
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
IdleOnly = 1,
|
|
||||||
IdleAndPriority = 2,
|
|
||||||
AggressiveOnLowMemory = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PoolActivationMode
|
|
||||||
{
|
|
||||||
SetActive = 0,
|
|
||||||
SleepWake = 1,
|
|
||||||
Custom = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PoolResetMode
|
|
||||||
{
|
|
||||||
TransformOnly = 0,
|
|
||||||
PoolableCallbacks = 1,
|
|
||||||
FullReset = 2,
|
|
||||||
Custom = 3
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public sealed class PoolEntry
|
public sealed class PoolEntry
|
||||||
{
|
{
|
||||||
public const string DefaultGroup = "Default";
|
public const string DefaultGroup = "DefaultGroup";
|
||||||
public const string DefaultEntryName = "DefaultPool";
|
public const string DefaultEntryName = "PoolRule";
|
||||||
|
|
||||||
public string entryName = DefaultEntryName;
|
public string entryName = DefaultEntryName;
|
||||||
public string group = DefaultGroup;
|
public string group = DefaultGroup;
|
||||||
public string assetPath;
|
public string assetPath = string.Empty;
|
||||||
public PoolMatchMode matchMode = PoolMatchMode.Exact;
|
public PoolMatchMode matchMode = PoolMatchMode.Exact;
|
||||||
public PoolResourceLoaderType loaderType = PoolResourceLoaderType.AssetBundle;
|
public PoolResourceLoaderType loaderType = PoolResourceLoaderType.AssetBundle;
|
||||||
public PoolOverflowPolicy overflowPolicy = PoolOverflowPolicy.FailFast;
|
public PoolCategory category = PoolCategory.Default;
|
||||||
public PoolTrimPolicy trimPolicy = PoolTrimPolicy.IdleOnly;
|
|
||||||
public PoolActivationMode activationMode = PoolActivationMode.SetActive;
|
|
||||||
public PoolResetMode resetMode = PoolResetMode.PoolableCallbacks;
|
|
||||||
|
|
||||||
[Min(0)]
|
|
||||||
public int minRetained = 0;
|
|
||||||
|
|
||||||
[Min(1)]
|
[Min(1)]
|
||||||
public int softCapacity = 8;
|
public int softCapacity = 8;
|
||||||
|
|
||||||
[Min(1)]
|
[Min(1)]
|
||||||
public int hardCapacity = 8;
|
public int hardCapacity = 16;
|
||||||
|
|
||||||
[Min(0f)]
|
|
||||||
public float idleTrimDelay = 30f;
|
|
||||||
|
|
||||||
[Min(0f)]
|
|
||||||
public float prefabUnloadDelay = 60f;
|
|
||||||
|
|
||||||
[Min(0f)]
|
|
||||||
public float autoRecycleDelay = 0f;
|
|
||||||
|
|
||||||
[Min(1)]
|
|
||||||
public int trimBatchPerTick = 2;
|
|
||||||
|
|
||||||
[Min(1)]
|
|
||||||
public int warmupBatchPerFrame = 2;
|
|
||||||
|
|
||||||
[Min(0f)]
|
|
||||||
public float warmupFrameBudgetMs = 1f;
|
|
||||||
|
|
||||||
public bool allowRuntimeExpand;
|
|
||||||
public bool keepPrefabResident;
|
|
||||||
public bool aggressiveTrimOnLowMemory;
|
|
||||||
public int priority;
|
public int priority;
|
||||||
|
|
||||||
public void Normalize()
|
public void Normalize()
|
||||||
@ -101,43 +51,26 @@ namespace AlicizaX
|
|||||||
entryName = string.IsNullOrWhiteSpace(entryName) ? DefaultEntryName : entryName.Trim();
|
entryName = string.IsNullOrWhiteSpace(entryName) ? DefaultEntryName : entryName.Trim();
|
||||||
group = string.IsNullOrWhiteSpace(group) ? DefaultGroup : group.Trim();
|
group = string.IsNullOrWhiteSpace(group) ? DefaultGroup : group.Trim();
|
||||||
assetPath = NormalizeAssetPath(assetPath);
|
assetPath = NormalizeAssetPath(assetPath);
|
||||||
minRetained = Mathf.Max(0, minRetained);
|
|
||||||
softCapacity = Mathf.Max(1, softCapacity);
|
softCapacity = Mathf.Max(1, softCapacity);
|
||||||
hardCapacity = Mathf.Max(softCapacity, hardCapacity);
|
hardCapacity = Mathf.Max(softCapacity, hardCapacity);
|
||||||
minRetained = Mathf.Min(minRetained, hardCapacity);
|
|
||||||
idleTrimDelay = Mathf.Max(0f, idleTrimDelay);
|
|
||||||
prefabUnloadDelay = Mathf.Max(0f, prefabUnloadDelay);
|
|
||||||
autoRecycleDelay = Mathf.Max(0f, autoRecycleDelay);
|
|
||||||
trimBatchPerTick = Mathf.Max(1, trimBatchPerTick);
|
|
||||||
warmupBatchPerFrame = Mathf.Max(1, warmupBatchPerFrame);
|
|
||||||
warmupFrameBudgetMs = Mathf.Max(0f, warmupFrameBudgetMs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Matches(string requestedAssetPath, string requestedGroup = null)
|
public bool Matches(string requestedAssetPath, string requestedGroup = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(assetPath) || string.IsNullOrWhiteSpace(requestedAssetPath))
|
if (string.IsNullOrEmpty(assetPath) || string.IsNullOrEmpty(requestedAssetPath))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(requestedGroup) &&
|
if (!string.IsNullOrEmpty(requestedGroup) &&
|
||||||
!string.Equals(group, requestedGroup, StringComparison.Ordinal))
|
!string.Equals(group, requestedGroup, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchMode switch
|
return matchMode == PoolMatchMode.Exact
|
||||||
{
|
? string.Equals(requestedAssetPath, assetPath, StringComparison.Ordinal)
|
||||||
PoolMatchMode.Exact => string.Equals(requestedAssetPath, assetPath, StringComparison.Ordinal),
|
: requestedAssetPath.StartsWith(assetPath, StringComparison.Ordinal);
|
||||||
PoolMatchMode.Prefix => requestedAssetPath.StartsWith(assetPath, StringComparison.Ordinal),
|
|
||||||
_ => false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public string BuildResolvedPoolKey(string resolvedAssetPath)
|
|
||||||
{
|
|
||||||
string concretePath = NormalizeAssetPath(resolvedAssetPath);
|
|
||||||
return $"{group}|{(int)matchMode}|{assetPath}|{(int)loaderType}|{concretePath}|{entryName}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int CompareByPriority(PoolEntry left, PoolEntry right)
|
public static int CompareByPriority(PoolEntry left, PoolEntry right)
|
||||||
@ -169,8 +102,8 @@ namespace AlicizaX
|
|||||||
return modeCompare;
|
return modeCompare;
|
||||||
}
|
}
|
||||||
|
|
||||||
int leftLength = left.assetPath?.Length ?? 0;
|
int leftLength = left.assetPath == null ? 0 : left.assetPath.Length;
|
||||||
int rightLength = right.assetPath?.Length ?? 0;
|
int rightLength = right.assetPath == null ? 0 : right.assetPath.Length;
|
||||||
int pathLengthCompare = rightLength.CompareTo(leftLength);
|
int pathLengthCompare = rightLength.CompareTo(leftLength);
|
||||||
if (pathLengthCompare != 0)
|
if (pathLengthCompare != 0)
|
||||||
{
|
{
|
||||||
@ -191,81 +124,344 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
internal struct PoolCompiledRule
|
||||||
public sealed class ResolvedPoolConfig
|
|
||||||
{
|
{
|
||||||
|
public int ruleIndex;
|
||||||
public string entryName;
|
public string entryName;
|
||||||
public string group;
|
public string group;
|
||||||
public string assetPath;
|
public string assetPath;
|
||||||
public PoolMatchMode matchMode;
|
public PoolMatchMode matchMode;
|
||||||
public PoolResourceLoaderType loaderType;
|
public PoolResourceLoaderType loaderType;
|
||||||
public PoolOverflowPolicy overflowPolicy;
|
public PoolCategory category;
|
||||||
public PoolTrimPolicy trimPolicy;
|
|
||||||
public PoolActivationMode activationMode;
|
|
||||||
public PoolResetMode resetMode;
|
|
||||||
public int minRetained;
|
|
||||||
public int softCapacity;
|
public int softCapacity;
|
||||||
public int hardCapacity;
|
public int hardCapacity;
|
||||||
public float idleTrimDelay;
|
|
||||||
public float prefabUnloadDelay;
|
|
||||||
public float autoRecycleDelay;
|
|
||||||
public int trimBatchPerTick;
|
|
||||||
public int warmupBatchPerFrame;
|
|
||||||
public float warmupFrameBudgetMs;
|
|
||||||
public bool allowRuntimeExpand;
|
|
||||||
public bool keepPrefabResident;
|
|
||||||
public bool aggressiveTrimOnLowMemory;
|
|
||||||
public int priority;
|
public int priority;
|
||||||
|
|
||||||
public string BuildResolvedPoolKey(string resolvedAssetPath)
|
public bool IsPrefix => matchMode == PoolMatchMode.Prefix;
|
||||||
{
|
|
||||||
string concretePath = PoolEntry.NormalizeAssetPath(resolvedAssetPath);
|
|
||||||
return $"{group}|{(int)matchMode}|{assetPath}|{(int)loaderType}|{concretePath}|{entryName}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ResolvedPoolConfig From(PoolEntry entry)
|
public static PoolCompiledRule FromEntry(PoolEntry entry, int ruleIndex)
|
||||||
{
|
{
|
||||||
if (entry == null)
|
return new PoolCompiledRule
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ResolvedPoolConfig
|
|
||||||
{
|
{
|
||||||
|
ruleIndex = ruleIndex,
|
||||||
entryName = entry.entryName,
|
entryName = entry.entryName,
|
||||||
group = entry.group,
|
group = entry.group,
|
||||||
assetPath = entry.assetPath,
|
assetPath = entry.assetPath,
|
||||||
matchMode = entry.matchMode,
|
matchMode = entry.matchMode,
|
||||||
loaderType = entry.loaderType,
|
loaderType = entry.loaderType,
|
||||||
overflowPolicy = entry.overflowPolicy,
|
category = entry.category,
|
||||||
trimPolicy = entry.trimPolicy,
|
|
||||||
activationMode = entry.activationMode,
|
|
||||||
resetMode = entry.resetMode,
|
|
||||||
minRetained = entry.minRetained,
|
|
||||||
softCapacity = entry.softCapacity,
|
softCapacity = entry.softCapacity,
|
||||||
hardCapacity = entry.hardCapacity,
|
hardCapacity = entry.hardCapacity,
|
||||||
idleTrimDelay = entry.idleTrimDelay,
|
|
||||||
prefabUnloadDelay = entry.prefabUnloadDelay,
|
|
||||||
autoRecycleDelay = entry.autoRecycleDelay,
|
|
||||||
trimBatchPerTick = entry.trimBatchPerTick,
|
|
||||||
warmupBatchPerFrame = entry.warmupBatchPerFrame,
|
|
||||||
warmupFrameBudgetMs = entry.warmupFrameBudgetMs,
|
|
||||||
allowRuntimeExpand = entry.allowRuntimeExpand,
|
|
||||||
keepPrefabResident = entry.keepPrefabResident,
|
|
||||||
aggressiveTrimOnLowMemory = entry.aggressiveTrimOnLowMemory,
|
|
||||||
priority = entry.priority
|
priority = entry.priority
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
internal sealed class PoolCompiledCatalog
|
||||||
public sealed class PoolConfigCatalog
|
|
||||||
{
|
{
|
||||||
public readonly List<PoolEntry> entries;
|
private readonly PoolCompiledRule[] _rules;
|
||||||
|
private StringOpenHashMap _groupIndexMap;
|
||||||
|
private PoolCompiledGroup[] _groups;
|
||||||
|
private StringOpenHashMap _globalExactMap;
|
||||||
|
private PoolPrefixTrie _globalPrefixTrie;
|
||||||
|
|
||||||
public PoolConfigCatalog(List<PoolEntry> entries)
|
private PoolCompiledCatalog(
|
||||||
|
PoolCompiledRule[] rules,
|
||||||
|
StringOpenHashMap groupIndexMap,
|
||||||
|
PoolCompiledGroup[] groups,
|
||||||
|
StringOpenHashMap globalExactMap,
|
||||||
|
PoolPrefixTrie globalPrefixTrie)
|
||||||
{
|
{
|
||||||
this.entries = entries ?? throw new ArgumentNullException(nameof(entries));
|
_rules = rules;
|
||||||
|
_groupIndexMap = groupIndexMap;
|
||||||
|
_groups = groups;
|
||||||
|
_globalExactMap = globalExactMap;
|
||||||
|
_globalPrefixTrie = globalPrefixTrie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEmpty => _rules == null || _rules.Length == 0;
|
||||||
|
|
||||||
|
public int RuleCount => _rules == null ? 0 : _rules.Length;
|
||||||
|
|
||||||
|
public ref readonly PoolCompiledRule GetRule(int ruleIndex)
|
||||||
|
{
|
||||||
|
return ref _rules[ruleIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Resolve(string assetPath, string group)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(assetPath) || _rules == null || _rules.Length == 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(group))
|
||||||
|
{
|
||||||
|
if (_groupIndexMap.TryGetValue(group, out int groupIndex))
|
||||||
|
{
|
||||||
|
return _groups[groupIndex].Resolve(assetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_globalExactMap.TryGetValue(assetPath, out int exactRuleIndex))
|
||||||
|
{
|
||||||
|
return exactRuleIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _globalPrefixTrie.Resolve(assetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PoolCompiledCatalog Empty()
|
||||||
|
{
|
||||||
|
return new PoolCompiledCatalog(
|
||||||
|
Array.Empty<PoolCompiledRule>(),
|
||||||
|
new StringOpenHashMap(8),
|
||||||
|
Array.Empty<PoolCompiledGroup>(),
|
||||||
|
new StringOpenHashMap(8),
|
||||||
|
new PoolPrefixTrie(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PoolCompiledCatalog Build(PoolEntry[] entries)
|
||||||
|
{
|
||||||
|
if (entries == null || entries.Length == 0)
|
||||||
|
{
|
||||||
|
return Empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int entryCount = entries.Length;
|
||||||
|
var groupIndexMap = new StringOpenHashMap(entryCount);
|
||||||
|
var groupNames = new string[entryCount];
|
||||||
|
var groupPrefixChars = new int[entryCount];
|
||||||
|
int groupCount = 0;
|
||||||
|
int globalPrefixChars = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < entryCount; i++)
|
||||||
|
{
|
||||||
|
PoolEntry entry = entries[i];
|
||||||
|
if (entry == null || string.IsNullOrEmpty(entry.assetPath))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!groupIndexMap.TryGetValue(entry.group, out int groupIndex))
|
||||||
|
{
|
||||||
|
groupIndex = groupCount;
|
||||||
|
groupIndexMap.AddOrUpdate(entry.group, groupIndex);
|
||||||
|
groupNames[groupCount] = entry.group;
|
||||||
|
groupCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.matchMode == PoolMatchMode.Prefix)
|
||||||
|
{
|
||||||
|
int pathLength = entry.assetPath.Length;
|
||||||
|
groupPrefixChars[groupIndex] += pathLength;
|
||||||
|
globalPrefixChars += pathLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var groups = new PoolCompiledGroup[groupCount];
|
||||||
|
for (int i = 0; i < groupCount; i++)
|
||||||
|
{
|
||||||
|
groups[i] = new PoolCompiledGroup(groupNames[i], entryCount, groupPrefixChars[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rules = new PoolCompiledRule[entryCount];
|
||||||
|
var globalExactMap = new StringOpenHashMap(entryCount);
|
||||||
|
var globalPrefixTrie = new PoolPrefixTrie(globalPrefixChars);
|
||||||
|
|
||||||
|
for (int i = 0; i < entryCount; i++)
|
||||||
|
{
|
||||||
|
PoolEntry entry = entries[i];
|
||||||
|
if (entry == null || string.IsNullOrEmpty(entry.assetPath))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PoolCompiledRule rule = PoolCompiledRule.FromEntry(entry, i);
|
||||||
|
rules[i] = rule;
|
||||||
|
|
||||||
|
groupIndexMap.TryGetValue(rule.group, out int groupIndex);
|
||||||
|
groups[groupIndex].Register(rule);
|
||||||
|
|
||||||
|
if (rule.matchMode == PoolMatchMode.Exact)
|
||||||
|
{
|
||||||
|
if (!globalExactMap.TryGetValue(rule.assetPath, out _))
|
||||||
|
{
|
||||||
|
globalExactMap.AddOrUpdate(rule.assetPath, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
globalPrefixTrie.Register(rule.assetPath, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PoolCompiledCatalog(rules, groupIndexMap, groups, globalExactMap, globalPrefixTrie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class PoolCompiledGroup
|
||||||
|
{
|
||||||
|
private readonly string _name;
|
||||||
|
private StringOpenHashMap _exactMap;
|
||||||
|
private PoolPrefixTrie _prefixTrie;
|
||||||
|
|
||||||
|
public PoolCompiledGroup(string name, int exactCapacity, int prefixChars)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_exactMap = new StringOpenHashMap(exactCapacity);
|
||||||
|
_prefixTrie = new PoolPrefixTrie(prefixChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Register(in PoolCompiledRule rule)
|
||||||
|
{
|
||||||
|
if (rule.matchMode == PoolMatchMode.Exact)
|
||||||
|
{
|
||||||
|
if (!_exactMap.TryGetValue(rule.assetPath, out _))
|
||||||
|
{
|
||||||
|
_exactMap.AddOrUpdate(rule.assetPath, rule.ruleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_prefixTrie.Register(rule.assetPath, rule.ruleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Resolve(string assetPath)
|
||||||
|
{
|
||||||
|
if (_exactMap.TryGetValue(assetPath, out int exactRuleIndex))
|
||||||
|
{
|
||||||
|
return exactRuleIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _prefixTrie.Resolve(assetPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class PoolPrefixTrie
|
||||||
|
{
|
||||||
|
private struct Node
|
||||||
|
{
|
||||||
|
public char character;
|
||||||
|
public int firstChild;
|
||||||
|
public int nextSibling;
|
||||||
|
public int ruleIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node[] _nodes;
|
||||||
|
private int _nodeCount;
|
||||||
|
|
||||||
|
public PoolPrefixTrie(int prefixCharCount)
|
||||||
|
{
|
||||||
|
int capacity = Mathf.Max(1, prefixCharCount + 1);
|
||||||
|
_nodes = new Node[capacity];
|
||||||
|
_nodes[0].firstChild = -1;
|
||||||
|
_nodes[0].nextSibling = -1;
|
||||||
|
_nodes[0].ruleIndex = -1;
|
||||||
|
_nodeCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Register(string prefix, int ruleIndex)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(prefix))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nodeIndex = 0;
|
||||||
|
int length = prefix.Length;
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
nodeIndex = GetOrCreateChild(nodeIndex, prefix[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_nodes[nodeIndex].ruleIndex < 0)
|
||||||
|
{
|
||||||
|
_nodes[nodeIndex].ruleIndex = ruleIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Resolve(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value) || _nodes == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nodeIndex = 0;
|
||||||
|
int bestRuleIndex = -1;
|
||||||
|
int length = value.Length;
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
nodeIndex = FindChild(nodeIndex, value[i]);
|
||||||
|
if (nodeIndex < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int matchedRuleIndex = _nodes[nodeIndex].ruleIndex;
|
||||||
|
if (matchedRuleIndex >= 0)
|
||||||
|
{
|
||||||
|
bestRuleIndex = matchedRuleIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestRuleIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetOrCreateChild(int nodeIndex, char character)
|
||||||
|
{
|
||||||
|
int childIndex = _nodes[nodeIndex].firstChild;
|
||||||
|
while (childIndex >= 0)
|
||||||
|
{
|
||||||
|
if (_nodes[childIndex].character == character)
|
||||||
|
{
|
||||||
|
return childIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
childIndex = _nodes[childIndex].nextSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCapacity(_nodeCount + 1);
|
||||||
|
int newNodeIndex = _nodeCount++;
|
||||||
|
_nodes[newNodeIndex].character = character;
|
||||||
|
_nodes[newNodeIndex].firstChild = -1;
|
||||||
|
_nodes[newNodeIndex].nextSibling = _nodes[nodeIndex].firstChild;
|
||||||
|
_nodes[newNodeIndex].ruleIndex = -1;
|
||||||
|
_nodes[nodeIndex].firstChild = newNodeIndex;
|
||||||
|
return newNodeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int FindChild(int nodeIndex, char character)
|
||||||
|
{
|
||||||
|
int childIndex = _nodes[nodeIndex].firstChild;
|
||||||
|
while (childIndex >= 0)
|
||||||
|
{
|
||||||
|
if (_nodes[childIndex].character == character)
|
||||||
|
{
|
||||||
|
return childIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
childIndex = _nodes[childIndex].nextSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureCapacity(int required)
|
||||||
|
{
|
||||||
|
if (_nodes.Length >= required)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newCapacity = Mathf.Max(required, _nodes.Length << 1);
|
||||||
|
var newNodes = new Node[newCapacity];
|
||||||
|
Array.Copy(_nodes, 0, newNodes, 0, _nodeCount);
|
||||||
|
_nodes = newNodes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,53 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace AlicizaX
|
namespace AlicizaX
|
||||||
{
|
{
|
||||||
[CreateAssetMenu(fileName = "PoolConfig", menuName = "GameplaySystem/PoolConfig", order = 10)]
|
[CreateAssetMenu(fileName = "PoolConfig", menuName = "GameplaySystem/PoolConfig", order = 10)]
|
||||||
public class PoolConfigScriptableObject : ScriptableObject
|
public sealed class PoolConfigScriptableObject : ScriptableObject
|
||||||
{
|
{
|
||||||
public List<PoolEntry> entries = new List<PoolEntry>();
|
public List<PoolEntry> entries = new List<PoolEntry>();
|
||||||
|
|
||||||
public PoolConfigCatalog BuildCatalog()
|
internal PoolCompiledCatalog BuildCatalog()
|
||||||
{
|
{
|
||||||
Normalize();
|
Normalize();
|
||||||
|
|
||||||
var normalizedEntries = new List<PoolEntry>(entries.Count);
|
if (entries == null || entries.Count == 0)
|
||||||
|
{
|
||||||
|
return PoolCompiledCatalog.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int validCount = 0;
|
||||||
for (int i = 0; i < entries.Count; i++)
|
for (int i = 0; i < entries.Count; i++)
|
||||||
{
|
{
|
||||||
PoolEntry entry = entries[i];
|
PoolEntry entry = entries[i];
|
||||||
if (entry == null)
|
if (entry != null && !string.IsNullOrEmpty(entry.assetPath))
|
||||||
|
{
|
||||||
|
validCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validCount == 0)
|
||||||
|
{
|
||||||
|
return PoolCompiledCatalog.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedEntries = new PoolEntry[validCount];
|
||||||
|
int writeIndex = 0;
|
||||||
|
for (int i = 0; i < entries.Count; i++)
|
||||||
|
{
|
||||||
|
PoolEntry entry = entries[i];
|
||||||
|
if (entry == null || string.IsNullOrEmpty(entry.assetPath))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizedEntries.Add(entry);
|
normalizedEntries[writeIndex++] = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizedEntries.Sort(PoolEntry.CompareByPriority);
|
Array.Sort(normalizedEntries, PoolEntry.CompareByPriority);
|
||||||
return new PoolConfigCatalog(normalizedEntries);
|
return PoolCompiledCatalog.Build(normalizedEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Normalize()
|
public void Normalize()
|
||||||
@ -33,6 +55,7 @@ namespace AlicizaX
|
|||||||
if (entries == null)
|
if (entries == null)
|
||||||
{
|
{
|
||||||
entries = new List<PoolEntry>();
|
entries = new List<PoolEntry>();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < entries.Count; i++)
|
for (int i = 0; i < entries.Count; i++)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user