com.alicizax.unity.framework/Editor/Timer/TimerComponentInspector.cs
陈思海 f4f0ea1754 [Optimization] TimerService&TimerDebug&AudioService
[Optimization] TimerService&TimerDebug&AudioService
2026-04-24 20:50:13 +08:00

170 lines
5.8 KiB
C#

using AlicizaX.Editor;
using AlicizaX.Timer.Runtime;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Timer.Editor
{
[CustomEditor(typeof(TimerComponent))]
internal sealed class TimerComponentInspector : GameFrameworkInspector
{
private const double UPDATE_INTERVAL = 0.001d;
private const int MAX_DISPLAY_COUNT = 20;
private TimerDebugInfo[] _timerBuffer;
#if UNITY_EDITOR
private TimerDebugInfo[] _leakBuffer;
#endif
private double _lastUpdateTime;
private int _cachedActiveCount;
private int _cachedPoolCapacity;
private int _cachedPeakActiveCount;
private int _cachedFreeCount;
private string _cachedUsageText;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
serializedObject.ApplyModifiedProperties();
DrawRuntimeDebugInfo();
if (EditorApplication.isPlaying)
{
double currentTime = EditorApplication.timeSinceStartup;
if (currentTime - _lastUpdateTime >= UPDATE_INTERVAL)
{
_lastUpdateTime = currentTime;
Repaint();
}
}
}
private void DrawRuntimeDebugInfo()
{
if (!EditorApplication.isPlaying)
{
EditorGUILayout.HelpBox("Available during runtime only.", MessageType.Info);
return;
}
if (!AppServices.TryGet<ITimerService>(out ITimerService timerService))
{
EditorGUILayout.HelpBox("Timer service is not initialized.", MessageType.Info);
return;
}
if (timerService is not ITimerServiceDebugView debugView)
{
return;
}
debugView.GetStatistics(out _cachedActiveCount, out _cachedPoolCapacity, out _cachedPeakActiveCount, out _cachedFreeCount);
_cachedUsageText = _cachedPoolCapacity > 0
? Utility.Text.Format("{0:F1}%", (float)_cachedActiveCount / _cachedPoolCapacity * 100f)
: "0.0%";
EditorGUILayout.Space();
EditorGUILayout.LabelField("Runtime Debug", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Active Timers", _cachedActiveCount.ToString());
EditorGUILayout.LabelField("Pool Capacity", _cachedPoolCapacity.ToString());
EditorGUILayout.LabelField("Peak Active Count", _cachedPeakActiveCount.ToString());
EditorGUILayout.LabelField("Free Slots", _cachedFreeCount.ToString());
EditorGUILayout.LabelField("Pool Usage", _cachedUsageText);
DrawTimerList(debugView, _cachedActiveCount);
#if UNITY_EDITOR
DrawLeakDetection(debugView, _cachedActiveCount);
#endif
}
private void DrawTimerList(ITimerServiceDebugView debugView, int activeCount)
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Active Timers", EditorStyles.boldLabel);
if (activeCount <= 0)
{
EditorGUILayout.LabelField("No active timers.");
return;
}
EnsureTimerBuffer(activeCount);
int timerCount = debugView.GetAllTimers(_timerBuffer);
int displayCount = Mathf.Min(timerCount, MAX_DISPLAY_COUNT);
if (displayCount < timerCount)
{
EditorGUILayout.HelpBox(Utility.Text.Format("Showing first {0} timers of {1}.", displayCount, timerCount), MessageType.Info);
}
for (int i = 0; i < displayCount; i++)
{
TimerDebugInfo timer = _timerBuffer[i];
string label = Utility.Text.Format(
"ID {0} | {1} | {2} | {3}",
timer.TimerId,
timer.IsLoop ? "Loop" : "Once",
timer.IsUnscaled ? "Unscaled" : "Scaled",
timer.IsRunning ? "Running" : "Paused");
string value = Utility.Text.Format(
"Left {0:F2}s | Duration {1:F2}s",
timer.LeftTime,
timer.Duration);
EditorGUILayout.LabelField(label, value);
}
}
#if UNITY_EDITOR
private void DrawLeakDetection(ITimerServiceDebugView debugView, int activeCount)
{
if (activeCount <= 0)
{
return;
}
EnsureLeakBuffer(activeCount);
int staleCount = debugView.GetStaleOneShotTimers(_leakBuffer);
if (staleCount <= 0)
{
return;
}
EditorGUILayout.Space();
EditorGUILayout.LabelField(Utility.Text.Format("Stale One-Shot Timers ({0})", staleCount), EditorStyles.boldLabel);
EditorGUILayout.HelpBox("Non-loop timers older than 5 minutes. This may indicate long-delay tasks or paused timers.", MessageType.Warning);
for (int i = 0; i < staleCount; i++)
{
TimerDebugInfo staleTimer = _leakBuffer[i];
EditorGUILayout.LabelField(
Utility.Text.Format("ID {0}", staleTimer.TimerId),
Utility.Text.Format("Created {0:F1}s ago", staleTimer.CreationTime));
}
}
#endif
private void EnsureTimerBuffer(int count)
{
if (_timerBuffer == null || _timerBuffer.Length < count)
{
_timerBuffer = new TimerDebugInfo[count];
}
}
#if UNITY_EDITOR
private void EnsureLeakBuffer(int count)
{
if (_leakBuffer == null || _leakBuffer.Length < count)
{
_leakBuffer = new TimerDebugInfo[count];
}
}
#endif
}
}