com.alicizax.unity.framework/Runtime/ABase/GameObjectPool/GameObjectPoolEditor.cs

323 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEditor;
using UnityEngine;
#if UNITY_EDITOR
namespace AlicizaX
{
[CustomEditor(typeof(GameObjectPool))]
public class GameObjectPoolEditor : UnityEditor.Editor
{
private bool[] _poolFoldouts;
private bool[] _prefabFoldouts;
private float _lastRefreshTime;
private const float AUTO_REFRESH_INTERVAL = 0.1f;
// 缓存序列化属性,避免重复查找
private SerializedProperty _poolInfosProperty;
private void OnEnable()
{
_poolInfosProperty = serializedObject.FindProperty("poolInfos");
_lastRefreshTime = Time.time;
}
public override void OnInspectorGUI()
{
var pool = (GameObjectPool)target;
// 更新序列化对象
serializedObject.Update();
// 绘制默认Inspector
DrawDefaultInspector();
EditorGUILayout.Space();
// 手动刷新按钮
if (GUILayout.Button("刷新池状态信息"))
{
RefreshPoolInfo(pool);
}
// 检查是否需要自动刷新
bool shouldAutoRefresh = EditorApplication.isPlaying && pool.showDetailedInfo &&
Selection.activeGameObject == pool.gameObject &&
Time.time - _lastRefreshTime > AUTO_REFRESH_INTERVAL;
if (shouldAutoRefresh)
{
RefreshPoolInfo(pool);
}
if (!pool.showDetailedInfo)
{
serializedObject.ApplyModifiedProperties();
return;
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("对象池详细信息", EditorStyles.boldLabel);
// 重新获取属性以确保数据是最新的
_poolInfosProperty = serializedObject.FindProperty("poolInfos");
if (_poolInfosProperty != null && _poolInfosProperty.arraySize > 0)
{
DrawPoolInfos();
}
else
{
EditorGUILayout.HelpBox("暂无池信息,请等待系统初始化或点击刷新按钮", MessageType.Info);
}
// 显示自动刷新状态
if (Selection.activeGameObject == pool.gameObject)
{
EditorGUILayout.HelpBox("Inspector正在自动刷新 (仅在选中时)", MessageType.Info);
}
// 应用修改的属性
serializedObject.ApplyModifiedProperties();
}
private void RefreshPoolInfo(GameObjectPool pool)
{
pool.RefreshInspectorInfo();
_lastRefreshTime = Time.time;
serializedObject.Update(); // 立即更新序列化对象
// 标记需要重绘
if (Selection.activeGameObject == pool.gameObject)
{
EditorUtility.SetDirty(pool);
Repaint();
}
}
private void DrawPoolInfos()
{
int poolCount = _poolInfosProperty.arraySize;
// 确保折叠状态数组大小正确
if (_poolFoldouts == null || _poolFoldouts.Length != poolCount)
{
bool[] oldPoolFoldouts = _poolFoldouts;
bool[] oldPrefabFoldouts = _prefabFoldouts;
_poolFoldouts = new bool[poolCount];
_prefabFoldouts = new bool[poolCount];
// 保持之前的折叠状态
if (oldPoolFoldouts != null)
{
for (int i = 0; i < Mathf.Min(oldPoolFoldouts.Length, poolCount); i++)
{
_poolFoldouts[i] = oldPoolFoldouts[i];
if (oldPrefabFoldouts != null && i < oldPrefabFoldouts.Length)
{
_prefabFoldouts[i] = oldPrefabFoldouts[i];
}
}
}
}
for (int i = 0; i < poolCount; i++)
{
DrawPoolInfo(i);
}
}
private void DrawPoolInfo(int poolIndex)
{
var poolInfo = _poolInfosProperty.GetArrayElementAtIndex(poolIndex);
if (poolInfo == null) return;
var configAssetProp = poolInfo.FindPropertyRelative("configAsset");
var totalObjectsProp = poolInfo.FindPropertyRelative("totalObjects");
var maxCountProp = poolInfo.FindPropertyRelative("maxCount");
var activeObjectsProp = poolInfo.FindPropertyRelative("activeObjects");
if (configAssetProp == null || totalObjectsProp == null || maxCountProp == null || activeObjectsProp == null)
return;
string configAsset = configAssetProp.stringValue;
int totalObjects = totalObjectsProp.intValue;
int maxCount = maxCountProp.intValue;
int activeObjects = activeObjectsProp.intValue;
EditorGUILayout.BeginVertical("box");
// 使用Rect布局来精确控制Foldout的大小
Rect rect = EditorGUILayout.GetControlRect();
Rect foldoutRect = new Rect(rect.x, rect.y, 15, rect.height);
Rect progressRect = new Rect(rect.x + 20, rect.y, rect.width - 120, rect.height);
Rect labelRect = new Rect(rect.x + rect.width - 95, rect.y, 95, rect.height);
// 绘制折叠按钮
_poolFoldouts[poolIndex] = EditorGUI.Foldout(foldoutRect, _poolFoldouts[poolIndex], GUIContent.none);
// 使用率进度条
float usage = maxCount > 0 ? (float)totalObjects / maxCount : 0f;
EditorGUI.ProgressBar(progressRect, usage, $"{configAsset} ({totalObjects}/{maxCount})");
// 活跃对象数
EditorGUI.LabelField(labelRect, $"活跃:{activeObjects}", EditorStyles.miniLabel);
if (_poolFoldouts[poolIndex])
{
EditorGUI.indentLevel++;
DrawPoolDetails(poolInfo, poolIndex);
EditorGUI.indentLevel--;
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
}
private void DrawPoolDetails(SerializedProperty poolInfo, int poolIndex)
{
var configAssetProp = poolInfo.FindPropertyRelative("configAsset");
var maxCountProp = poolInfo.FindPropertyRelative("maxCount");
var expireTimeProp = poolInfo.FindPropertyRelative("expireTime");
var loadedPrefabsProp = poolInfo.FindPropertyRelative("loadedPrefabs");
if (configAssetProp != null)
EditorGUILayout.LabelField($"配置路径: {configAssetProp.stringValue}");
if (maxCountProp != null)
EditorGUILayout.LabelField($"最大数量: {maxCountProp.intValue}");
if (expireTimeProp != null)
EditorGUILayout.LabelField($"过期时间: {expireTimeProp.floatValue}s");
if (loadedPrefabsProp != null)
EditorGUILayout.LabelField($"已加载预制体: {loadedPrefabsProp.intValue}");
EditorGUILayout.Space();
// 绘制预制体引用信息
DrawPrefabRefs(poolInfo, poolIndex);
// 绘制对象详细信息
DrawObjectDetails(poolInfo);
}
private void DrawPrefabRefs(SerializedProperty poolInfo, int poolIndex)
{
var prefabRefsProp = poolInfo.FindPropertyRelative("prefabRefs");
if (prefabRefsProp == null || prefabRefsProp.arraySize <= 0) return;
// 使用简单的Foldout不指定宽度
_prefabFoldouts[poolIndex] = EditorGUILayout.Foldout(_prefabFoldouts[poolIndex], "预制体引用信息:");
if (_prefabFoldouts[poolIndex])
{
EditorGUI.indentLevel++;
for (int j = 0; j < prefabRefsProp.arraySize; j++)
{
DrawPrefabRefInfo(prefabRefsProp.GetArrayElementAtIndex(j));
}
EditorGUI.indentLevel--;
}
EditorGUILayout.Space();
}
private void DrawPrefabRefInfo(SerializedProperty prefabRef)
{
if (prefabRef == null) return;
var assetPathProp = prefabRef.FindPropertyRelative("assetPath");
var refCountProp = prefabRef.FindPropertyRelative("refCount");
var lastAccessTimeProp = prefabRef.FindPropertyRelative("lastAccessTime");
var prefabObjProp = prefabRef.FindPropertyRelative("prefab");
EditorGUILayout.BeginHorizontal("box");
EditorGUILayout.BeginVertical();
if (assetPathProp != null)
EditorGUILayout.LabelField($"{System.IO.Path.GetFileName(assetPathProp.stringValue)}", EditorStyles.boldLabel);
if (refCountProp != null)
EditorGUILayout.LabelField($"引用计数: {refCountProp.intValue}", EditorStyles.miniLabel);
if (lastAccessTimeProp != null)
EditorGUILayout.LabelField($"最后访问: {(Time.time - lastAccessTimeProp.floatValue):F1}秒前", EditorStyles.miniLabel);
EditorGUILayout.EndVertical();
if (prefabObjProp != null)
EditorGUILayout.ObjectField(prefabObjProp.objectReferenceValue, typeof(GameObject), false, GUILayout.Width(100));
EditorGUILayout.EndHorizontal();
}
private void DrawObjectDetails(SerializedProperty poolInfo)
{
var objectsProp = poolInfo.FindPropertyRelative("objects");
if (objectsProp == null || objectsProp.arraySize <= 0) return;
EditorGUILayout.LabelField("对象详情:", EditorStyles.boldLabel);
for (int j = 0; j < objectsProp.arraySize; j++)
{
DrawObjectInfo(objectsProp.GetArrayElementAtIndex(j));
}
}
private void DrawObjectInfo(SerializedProperty obj)
{
if (obj == null) return;
var objNameProp = obj.FindPropertyRelative("objectName");
var objAssetPathProp = obj.FindPropertyRelative("assetPath");
var isActiveProp = obj.FindPropertyRelative("isActive");
var remainingTimeProp = obj.FindPropertyRelative("remainingTime");
var expireProgressProp = obj.FindPropertyRelative("expireProgress");
var gameObjectProp = obj.FindPropertyRelative("gameObject");
EditorGUILayout.BeginHorizontal("box");
// 状态颜色指示器
bool isActive = isActiveProp?.boolValue ?? false;
var statusColor = isActive ? Color.green : Color.yellow;
var prevColor = GUI.color;
GUI.color = statusColor;
EditorGUILayout.LabelField("●", GUILayout.Width(15));
GUI.color = prevColor;
EditorGUILayout.BeginVertical();
// 对象名称和路径
string objName = objNameProp?.stringValue ?? "Unknown";
string objAssetPath = objAssetPathProp?.stringValue ?? "";
EditorGUILayout.LabelField($"{objName} ({System.IO.Path.GetFileName(objAssetPath)})", EditorStyles.boldLabel);
EditorGUILayout.LabelField($"状态: {(isActive ? "" : "")}", EditorStyles.miniLabel);
// 过期进度条
if (!isActive && remainingTimeProp != null && expireProgressProp != null)
{
float remainingTime = remainingTimeProp.floatValue;
float expireProgress = expireProgressProp.floatValue;
if (remainingTime >= 0)
{
Rect expireRect = GUILayoutUtility.GetRect(100, 16, GUILayout.ExpandWidth(true), GUILayout.Height(16));
EditorGUI.ProgressBar(expireRect, expireProgress, $"释放倒计时: {remainingTime:F1}s");
}
}
EditorGUILayout.EndVertical();
// GameObject引用
if (gameObjectProp != null)
EditorGUILayout.ObjectField(gameObjectProp.objectReferenceValue, typeof(GameObject), true, GUILayout.Width(100));
EditorGUILayout.EndHorizontal();
}
public override bool RequiresConstantRepaint()
{
// 只有在选中对象池时才需要持续重绘
var pool = target as GameObjectPool;
return pool != null && pool.showDetailedInfo && Selection.activeGameObject == pool.gameObject;
}
}
}
#endif