323 lines
12 KiB
C#
323 lines
12 KiB
C#
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
|