修改
This commit is contained in:
parent
03c0fe6723
commit
d06c59ddf8
@ -12,87 +12,94 @@ namespace AlicizaX
|
|||||||
public static class EventContainer<TPayload> where TPayload : struct, IEventArgs
|
public static class EventContainer<TPayload> where TPayload : struct, IEventArgs
|
||||||
{
|
{
|
||||||
private static readonly int InitialSize = EventInitialSize<TPayload>.Size;
|
private static readonly int InitialSize = EventInitialSize<TPayload>.Size;
|
||||||
private static Handler[] _handlers = new Handler[InitialSize];
|
private static readonly int TypeId;
|
||||||
private static int[] _freeSlots = new int[InitialSize];
|
|
||||||
private static int _freeCount = InitialSize;
|
// 优化3: SoA布局,提升缓存命中率
|
||||||
private static int[] _activeIndices = new int[InitialSize];
|
private static Action<TPayload>[] _callbacks;
|
||||||
|
private static int[] _versions;
|
||||||
|
private static int[] _activeSlots;
|
||||||
|
|
||||||
|
private static int[] _freeSlots;
|
||||||
|
private static int _freeCount;
|
||||||
|
private static int[] _activeIndices;
|
||||||
private static int _activeCount;
|
private static int _activeCount;
|
||||||
private static int _version;
|
private static int _version;
|
||||||
|
|
||||||
#if Event_StrickCheck
|
#if Event_StrictCheck
|
||||||
private static System.Collections.Generic.HashSet<Action<TPayload>> _activeHandlers = new();
|
private static System.Collections.Generic.HashSet<Action<TPayload>> _activeHandlers;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static EventContainer()
|
static EventContainer()
|
||||||
{
|
{
|
||||||
|
TypeId = UnsubscribeRegistry.Register(Unsubscribe);
|
||||||
|
|
||||||
|
_callbacks = new Action<TPayload>[InitialSize];
|
||||||
|
_versions = new int[InitialSize];
|
||||||
|
_activeSlots = new int[InitialSize];
|
||||||
|
_freeSlots = new int[InitialSize];
|
||||||
|
_activeIndices = new int[InitialSize];
|
||||||
|
_freeCount = InitialSize;
|
||||||
|
|
||||||
for (int i = 0; i < InitialSize; i++)
|
for (int i = 0; i < InitialSize; i++)
|
||||||
{
|
{
|
||||||
_freeSlots[i] = i;
|
_freeSlots[i] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if Event_StrictCheck
|
||||||
|
_activeHandlers = new System.Collections.Generic.HashSet<Action<TPayload>>();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[Il2CppSetOption(Option.NullChecks, false)]
|
|
||||||
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
|
|
||||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
||||||
private struct Handler
|
|
||||||
{
|
|
||||||
public Action<TPayload> Callback;
|
|
||||||
public int Version;
|
|
||||||
public int ActiveSlot;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Il2CppSetOption(Option.NullChecks, false)]
|
|
||||||
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
|
|
||||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static EventRuntimeHandle Subscribe(Action<TPayload> callback)
|
public static EventRuntimeHandle Subscribe(Action<TPayload> callback)
|
||||||
{
|
{
|
||||||
#if Event_StrickCheck
|
#if Event_StrictCheck
|
||||||
if (_activeHandlers.Contains(callback))
|
if (_activeHandlers.Contains(callback))
|
||||||
{
|
{
|
||||||
Log.Warning($"重复订阅事件处理程序: {callback.Method.Name}");
|
UnityEngine.Debug.LogWarning($"重复订阅事件处理程序: {callback.Method.Name}");
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
_activeHandlers.Add(callback);
|
||||||
_activeHandlers.Add(callback);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int handlerIndex = GetFreeSlot();
|
int handlerIndex = GetFreeSlot();
|
||||||
|
|
||||||
|
// 优化4: 提前扩容检查
|
||||||
if (_activeCount >= _activeIndices.Length)
|
if (_activeCount >= _activeIndices.Length)
|
||||||
{
|
{
|
||||||
Array.Resize(ref _activeIndices, _activeIndices.Length * 2);
|
Array.Resize(ref _activeIndices, _activeIndices.Length << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int activeIndex = _activeCount++;
|
int activeIndex = _activeCount++;
|
||||||
_activeIndices[activeIndex] = handlerIndex;
|
_activeIndices[activeIndex] = handlerIndex;
|
||||||
|
|
||||||
int version = ++_version;
|
int version = ++_version;
|
||||||
_handlers[handlerIndex] = new Handler
|
_callbacks[handlerIndex] = callback;
|
||||||
{
|
_versions[handlerIndex] = version;
|
||||||
Callback = callback,
|
_activeSlots[handlerIndex] = activeIndex;
|
||||||
Version = version,
|
|
||||||
ActiveSlot = activeIndex
|
|
||||||
};
|
|
||||||
|
|
||||||
return new EventRuntimeHandle(Unsubscribe, handlerIndex, version);
|
return new EventRuntimeHandle(TypeId, handlerIndex, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Il2CppSetOption(Option.NullChecks, false)]
|
|
||||||
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
|
|
||||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static int GetFreeSlot()
|
private static int GetFreeSlot()
|
||||||
{
|
{
|
||||||
if (_freeCount > 0) return _freeSlots[--_freeCount];
|
if (_freeCount > 0)
|
||||||
|
{
|
||||||
|
return _freeSlots[--_freeCount];
|
||||||
|
}
|
||||||
|
|
||||||
int oldLen = _handlers.Length;
|
int oldLen = _callbacks.Length;
|
||||||
int newSize = oldLen == 0 ? 64 : oldLen * 2;
|
int newSize = oldLen == 0 ? 64 : oldLen << 1;
|
||||||
Array.Resize(ref _handlers, newSize);
|
|
||||||
|
// 批量扩容
|
||||||
|
Array.Resize(ref _callbacks, newSize);
|
||||||
|
Array.Resize(ref _versions, newSize);
|
||||||
|
Array.Resize(ref _activeSlots, newSize);
|
||||||
Array.Resize(ref _freeSlots, newSize);
|
Array.Resize(ref _freeSlots, newSize);
|
||||||
|
|
||||||
|
// 逆序添加,保持小索引优先(缓存友好)
|
||||||
for (int i = newSize - 1; i >= oldLen; i--)
|
for (int i = newSize - 1; i >= oldLen; i--)
|
||||||
{
|
{
|
||||||
_freeSlots[_freeCount++] = i;
|
_freeSlots[_freeCount++] = i;
|
||||||
@ -101,62 +108,117 @@ namespace AlicizaX
|
|||||||
return _freeSlots[--_freeCount];
|
return _freeSlots[--_freeCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
[Il2CppSetOption(Option.NullChecks, false)]
|
|
||||||
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
|
|
||||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void Unsubscribe(int handlerIndex, int version)
|
private static void Unsubscribe(int handlerIndex, int version)
|
||||||
{
|
{
|
||||||
ref Handler handler = ref _handlers[handlerIndex];
|
// 优化5: 提前版本检查
|
||||||
if (handler.Version != version) return;
|
if (_versions[handlerIndex] != version) return;
|
||||||
|
|
||||||
int lastActiveIndex = --_activeCount;
|
int lastActiveIndex = --_activeCount;
|
||||||
int lastHandlerIndex = _activeIndices[lastActiveIndex];
|
int lastHandlerIndex = _activeIndices[lastActiveIndex];
|
||||||
int currentActiveIndex = handler.ActiveSlot;
|
int currentActiveIndex = _activeSlots[handlerIndex];
|
||||||
|
|
||||||
|
// Swap-remove
|
||||||
_activeIndices[currentActiveIndex] = lastHandlerIndex;
|
_activeIndices[currentActiveIndex] = lastHandlerIndex;
|
||||||
_handlers[lastHandlerIndex].ActiveSlot = currentActiveIndex;
|
_activeSlots[lastHandlerIndex] = currentActiveIndex;
|
||||||
|
|
||||||
#if Event_StrickCheck
|
#if Event_StrictCheck
|
||||||
_activeHandlers.Remove(handler.Callback);
|
_activeHandlers.Remove(_callbacks[handlerIndex]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
handler.Callback = null;
|
_callbacks[handlerIndex] = null;
|
||||||
handler.Version = 0;
|
_versions[handlerIndex] = 0;
|
||||||
|
|
||||||
if (_freeCount >= _freeSlots.Length)
|
if (_freeCount >= _freeSlots.Length)
|
||||||
{
|
{
|
||||||
Array.Resize(ref _freeSlots, _freeSlots.Length * 2);
|
Array.Resize(ref _freeSlots, _freeSlots.Length << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_freeSlots[_freeCount++] = handlerIndex;
|
_freeSlots[_freeCount++] = handlerIndex;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 优化6: 改进Publish,减少数组访问和分支
|
||||||
[Il2CppSetOption(Option.NullChecks, false)]
|
|
||||||
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
|
|
||||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Publish(in TPayload payload)
|
public static void Publish(in TPayload payload)
|
||||||
{
|
{
|
||||||
int count = _activeCount;
|
int count = _activeCount;
|
||||||
|
if (count == 0) return; // 快速路径
|
||||||
|
|
||||||
int[] indices = _activeIndices;
|
int[] indices = _activeIndices;
|
||||||
Handler[] handlers = _handlers;
|
Action<TPayload>[] callbacks = _callbacks;
|
||||||
|
|
||||||
|
// 优化7: 更高效的循环展开
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (; i <= count - 4; i += 4)
|
int unrolled = count & ~3; // count - (count % 4)
|
||||||
|
|
||||||
|
// 4路展开主循环
|
||||||
|
for (; i < unrolled; i += 4)
|
||||||
{
|
{
|
||||||
handlers[indices[i]].Callback(payload);
|
int idx0 = indices[i];
|
||||||
handlers[indices[i + 1]].Callback(payload);
|
int idx1 = indices[i + 1];
|
||||||
handlers[indices[i + 2]].Callback(payload);
|
int idx2 = indices[i + 2];
|
||||||
handlers[indices[i + 3]].Callback(payload);
|
int idx3 = indices[i + 3];
|
||||||
|
|
||||||
|
callbacks[idx0](payload);
|
||||||
|
callbacks[idx1](payload);
|
||||||
|
callbacks[idx2](payload);
|
||||||
|
callbacks[idx3](payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; i < count; i++)
|
// 优化8: 使用Duff's Device处理剩余
|
||||||
|
switch (count - i)
|
||||||
{
|
{
|
||||||
handlers[indices[i]].Callback(payload);
|
case 3:
|
||||||
|
callbacks[indices[i + 2]](payload);
|
||||||
|
goto case 2;
|
||||||
|
case 2:
|
||||||
|
callbacks[indices[i + 1]](payload);
|
||||||
|
goto case 1;
|
||||||
|
case 1: callbacks[indices[i]](payload); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 额外优化方法
|
||||||
|
public static int SubscriberCount
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _activeCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预热容量,避免运行时扩容
|
||||||
|
public static void EnsureCapacity(int capacity)
|
||||||
|
{
|
||||||
|
if (_callbacks.Length >= capacity) return;
|
||||||
|
|
||||||
|
int oldLen = _callbacks.Length;
|
||||||
|
Array.Resize(ref _callbacks, capacity);
|
||||||
|
Array.Resize(ref _versions, capacity);
|
||||||
|
Array.Resize(ref _activeSlots, capacity);
|
||||||
|
Array.Resize(ref _freeSlots, capacity);
|
||||||
|
Array.Resize(ref _activeIndices, capacity);
|
||||||
|
|
||||||
|
for (int i = capacity - 1; i >= oldLen; i--)
|
||||||
|
{
|
||||||
|
_freeSlots[_freeCount++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空所有订阅
|
||||||
|
public static void Clear()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _activeCount; i++)
|
||||||
|
{
|
||||||
|
int idx = _activeIndices[i];
|
||||||
|
_callbacks[idx] = null;
|
||||||
|
_versions[idx] = 0;
|
||||||
|
_freeSlots[_freeCount++] = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
_activeCount = 0;
|
||||||
|
|
||||||
|
#if Event_StrictCheck
|
||||||
|
_activeHandlers.Clear();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,17 +21,34 @@ namespace AlicizaX
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Publish<T>(in T evt) where T : struct, IEventArgs
|
public static void Publish<T>(in T evt) where T : struct, IEventArgs
|
||||||
{
|
{
|
||||||
EventContainer<T>.Publish(evt);
|
EventContainer<T>.Publish(in evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Il2CppSetOption(Option.NullChecks, false)]
|
[Il2CppSetOption(Option.NullChecks, false)]
|
||||||
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
|
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
|
||||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Publish<T>(Action<T> init) where T : struct, IEventArgs
|
public static void Publish<T>(Action<T> init) where T : struct, IEventArgs
|
||||||
{
|
{
|
||||||
var evt = default(T);
|
var evt = default(T);
|
||||||
init(evt);
|
init(evt);
|
||||||
Publish(in evt);
|
Publish(in evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static int GetSubscriberCount<T>() where T : struct, IEventArgs
|
||||||
|
{
|
||||||
|
return EventContainer<T>.SubscriberCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnsureCapacity<T>(int capacity) where T : struct, IEventArgs
|
||||||
|
{
|
||||||
|
EventContainer<T>.EnsureCapacity(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Clear<T>() where T : struct, IEventArgs
|
||||||
|
{
|
||||||
|
EventContainer<T>.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using Unity.IL2CPP.CompilerServices;
|
using Unity.IL2CPP.CompilerServices;
|
||||||
|
|
||||||
namespace AlicizaX
|
namespace AlicizaX
|
||||||
@ -15,21 +16,28 @@ namespace AlicizaX
|
|||||||
[Il2CppSetOption(Option.NullChecks, false)]
|
[Il2CppSetOption(Option.NullChecks, false)]
|
||||||
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
|
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
|
||||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||||
public readonly struct EventRuntimeHandle
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public readonly struct EventRuntimeHandle : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Action<int,int> _unsubscribe;
|
private readonly long _data; // 高32位=index, 低32位=version
|
||||||
private readonly int _index;
|
private readonly int _typeId; // 类型ID,避免泛型委托
|
||||||
private readonly int _version;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public EventRuntimeHandle(Action<int,int> unsubscribe, int index, int version)
|
internal EventRuntimeHandle(int typeId, int index, int version)
|
||||||
{
|
{
|
||||||
_unsubscribe = unsubscribe;
|
_typeId = typeId;
|
||||||
_index = index;
|
_data = ((long)index << 32) | (uint)version;
|
||||||
_version = version;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Dispose() => _unsubscribe?.Invoke(_index, _version);
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_typeId != 0)
|
||||||
|
{
|
||||||
|
int index = (int)(_data >> 32);
|
||||||
|
int version = (int)_data;
|
||||||
|
UnsubscribeRegistry.Invoke(_typeId, index, version);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
Runtime/ABase/Event/UnsubscribeRegistry.cs
Normal file
30
Runtime/ABase/Event/UnsubscribeRegistry.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace AlicizaX
|
||||||
|
{
|
||||||
|
internal static class UnsubscribeRegistry
|
||||||
|
{
|
||||||
|
private static Action<int, int>[] _handlers = new Action<int, int>[32];
|
||||||
|
private static int _nextId = 1; // 0表示无效
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static int Register(Action<int, int> handler)
|
||||||
|
{
|
||||||
|
int id = _nextId++;
|
||||||
|
if (id >= _handlers.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _handlers, _handlers.Length * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handlers[id] = handler;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static void Invoke(int id, int index, int version)
|
||||||
|
{
|
||||||
|
_handlers[id]?.Invoke(index, version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Runtime/ABase/Event/UnsubscribeRegistry.cs.meta
Normal file
3
Runtime/ABase/Event/UnsubscribeRegistry.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5342adccb3de43ef8edcf1cfe8b41712
|
||||||
|
timeCreated: 1766556356
|
||||||
@ -2,63 +2,22 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using AlicizaX;
|
using AlicizaX;
|
||||||
|
|
||||||
namespace AlicizaX.Timer.Runtime
|
namespace AlicizaX
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 定时器接口
|
|
||||||
/// </summary>
|
|
||||||
[UnityEngine.Scripting.Preserve]
|
[UnityEngine.Scripting.Preserve]
|
||||||
public interface ITimerModule : IModule, IModuleUpdate
|
public interface ITimerModule : IModule, IModuleUpdate
|
||||||
{
|
{
|
||||||
/// <summary>
|
int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args);
|
||||||
/// 添加计时器。
|
int AddTimer(TimerHandlerNoArgs callback, float time, bool isLoop = false, bool isUnscaled = false);
|
||||||
/// </summary>
|
|
||||||
/// <param name="callback">计时器回调。</param>
|
|
||||||
/// <param name="time">计时器间隔。</param>
|
|
||||||
/// <param name="isLoop">是否循环。</param>
|
|
||||||
/// <param name="isUnscaled">是否不收时间缩放影响。</param>
|
|
||||||
/// <param name="args">传参。(避免闭包)</param>
|
|
||||||
/// <returns>计时器Id。</returns>
|
|
||||||
public int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args);
|
|
||||||
|
|
||||||
/// <summary>
|
// 优化1: 使用值类型泛型参数,完全零GC
|
||||||
/// 暂停计时器。
|
int AddTimer<T>(Action<T> callback, T arg, float time, bool isLoop = false, bool isUnscaled = false);
|
||||||
/// </summary>
|
void Stop(int timerId);
|
||||||
/// <param name="timerId">计时器Id。</param>
|
void Resume(int timerId);
|
||||||
public void Stop(int timerId);
|
bool IsRunning(int timerId);
|
||||||
|
float GetLeftTime(int timerId);
|
||||||
/// <summary>
|
void Restart(int timerId);
|
||||||
/// 恢复计时器。
|
void RemoveTimer(int timerId);
|
||||||
/// </summary>
|
void RemoveAllTimer();
|
||||||
/// <param name="timerId">计时器Id。</param>
|
|
||||||
public void Resume(int timerId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 计时器是否在运行中。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="timerId">计时器Id。</param>
|
|
||||||
/// <returns>否在运行中。</returns>
|
|
||||||
public bool IsRunning(int timerId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获得计时器剩余时间。
|
|
||||||
/// </summary>
|
|
||||||
public float GetLeftTime(int timerId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 重置计时器,恢复到开始状态。
|
|
||||||
/// </summary>
|
|
||||||
public void Restart(int timerId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 移除计时器。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="timerId">计时器Id。</param>
|
|
||||||
public void RemoveTimer(int timerId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 移除所有计时器。
|
|
||||||
/// </summary>
|
|
||||||
public void RemoveAllTimer();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,226 +1,493 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Runtime.CompilerServices;
|
||||||
using AlicizaX;
|
using System.Runtime.InteropServices;
|
||||||
|
using Unity.IL2CPP.CompilerServices;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace AlicizaX.Timer.Runtime
|
namespace AlicizaX
|
||||||
{
|
{
|
||||||
public delegate void TimerHandler(params object[] args);
|
public delegate void TimerHandler(params object[] args);
|
||||||
|
|
||||||
[Serializable]
|
public delegate void TimerHandlerNoArgs();
|
||||||
internal class Timer : IMemory
|
|
||||||
|
|
||||||
|
// 优化2: 使用union结构存储不同类型的回调(零装箱)
|
||||||
|
[Il2CppSetOption(Option.NullChecks, false)]
|
||||||
|
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||||
|
internal struct TimerInfo : IMemory
|
||||||
{
|
{
|
||||||
public int TimerId;
|
public int TimerId;
|
||||||
public float TriggerTime;
|
public float TriggerTime;
|
||||||
public float Interval;
|
public float Interval;
|
||||||
|
|
||||||
|
// 优化3: 使用字段而非object存储,避免装箱
|
||||||
public TimerHandler Handler;
|
public TimerHandler Handler;
|
||||||
|
public TimerHandlerNoArgs HandlerNoArgs;
|
||||||
|
public Delegate HandlerGeneric; // 直接存储委托
|
||||||
|
|
||||||
public bool IsLoop;
|
public bool IsLoop;
|
||||||
public bool IsRunning;
|
public bool IsRunning;
|
||||||
public bool IsUnscaled;
|
public bool IsUnscaled;
|
||||||
public bool IsActive; // 标记定时器是否有效
|
public bool IsActive;
|
||||||
public object[] Args;
|
public byte HandlerType; // 0=Handler, 1=NoArgs, 2=Generic<struct>, 3=Generic<class>
|
||||||
public LinkedListNode<Timer> Node; // 用于时间轮中的链表节点
|
|
||||||
|
|
||||||
|
public object[] Args;
|
||||||
|
public object GenericArg; // 存储泛型参数
|
||||||
|
|
||||||
|
public int SlotIndex;
|
||||||
|
public int NextTimerIndex;
|
||||||
|
public int PrevTimerIndex;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
Handler = null;
|
Handler = null;
|
||||||
|
HandlerNoArgs = null;
|
||||||
|
HandlerGeneric = null;
|
||||||
Args = null;
|
Args = null;
|
||||||
Node = null;
|
GenericArg = null;
|
||||||
IsActive = false;
|
IsActive = false;
|
||||||
|
NextTimerIndex = -1;
|
||||||
|
PrevTimerIndex = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnityEngine.Scripting.Preserve]
|
[UnityEngine.Scripting.Preserve]
|
||||||
|
[Il2CppSetOption(Option.NullChecks, false)]
|
||||||
|
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||||
internal sealed class TimerModule : ITimerModule
|
internal sealed class TimerModule : ITimerModule
|
||||||
{
|
{
|
||||||
private int _curTimerId;
|
private int _curTimerId;
|
||||||
private TimeWheel _scaledTimeWheel;
|
private HierarchicalTimeWheel _scaledTimeWheel;
|
||||||
private TimeWheel _unscaledTimeWheel;
|
private HierarchicalTimeWheel _unscaledTimeWheel;
|
||||||
private readonly Dictionary<int, Timer> _activeTimers = new Dictionary<int, Timer>();
|
|
||||||
private readonly Action<Timer> _processTimerDelegate;
|
|
||||||
private class TimeWheel
|
|
||||||
{
|
|
||||||
private readonly float _slotInterval; // 每个槽的时间间隔(秒)
|
|
||||||
private readonly int _slotCount; // 槽的数量
|
|
||||||
private readonly LinkedList<Timer>[] _slots;
|
|
||||||
private float _currentTime;
|
|
||||||
private int _currentSlotIndex;
|
|
||||||
|
|
||||||
public TimeWheel(float slotInterval, int slotCount)
|
private TimerInfo[] _timerPool;
|
||||||
|
private int[] _timerIdToPoolIndex;
|
||||||
|
private int _timerPoolCapacity;
|
||||||
|
private int[] _freeIndices;
|
||||||
|
private int _freeCount;
|
||||||
|
|
||||||
|
// 优化4: 静态泛型缓存,避免每次调用时的类型检查
|
||||||
|
private static class GenericInvoker<T>
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void Invoke(Delegate callback, object arg)
|
||||||
{
|
{
|
||||||
_slotInterval = slotInterval;
|
((Action<T>)callback)((T)arg);
|
||||||
_slotCount = slotCount;
|
}
|
||||||
_slots = new LinkedList<Timer>[slotCount];
|
}
|
||||||
for (int i = 0; i < slotCount; i++)
|
|
||||||
_slots[i] = new LinkedList<Timer>();
|
private class HierarchicalTimeWheel
|
||||||
_currentTime = 0f;
|
{
|
||||||
_currentSlotIndex = 0;
|
private const int SLOT_COUNT_LEVEL0 = 256;
|
||||||
|
private const int SLOT_COUNT_LEVEL1 = 64;
|
||||||
|
private const int SLOT_COUNT_LEVEL2 = 64;
|
||||||
|
|
||||||
|
private readonly float _slotInterval;
|
||||||
|
private readonly TimeWheelLevel[] _levels;
|
||||||
|
private float _currentTime;
|
||||||
|
|
||||||
|
private class TimeWheelLevel
|
||||||
|
{
|
||||||
|
public readonly int[] SlotHeads;
|
||||||
|
public readonly int SlotCount;
|
||||||
|
public int CurrentSlot;
|
||||||
|
|
||||||
|
public TimeWheelLevel(int slotCount)
|
||||||
|
{
|
||||||
|
SlotCount = slotCount;
|
||||||
|
SlotHeads = new int[slotCount];
|
||||||
|
for (int i = 0; i < slotCount; i++)
|
||||||
|
SlotHeads[i] = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTimer(Timer timer, float currentTime)
|
public HierarchicalTimeWheel(float slotInterval)
|
||||||
{
|
{
|
||||||
|
_slotInterval = slotInterval;
|
||||||
|
_levels = new TimeWheelLevel[3];
|
||||||
|
_levels[0] = new TimeWheelLevel(SLOT_COUNT_LEVEL0);
|
||||||
|
_levels[1] = new TimeWheelLevel(SLOT_COUNT_LEVEL1);
|
||||||
|
_levels[2] = new TimeWheelLevel(SLOT_COUNT_LEVEL2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void AddTimer(int poolIndex, TimerInfo[] pool, float currentTime)
|
||||||
|
{
|
||||||
|
ref TimerInfo timer = ref pool[poolIndex];
|
||||||
if (!timer.IsActive) return;
|
if (!timer.IsActive) return;
|
||||||
|
|
||||||
float triggerTime = timer.TriggerTime;
|
float delta = timer.TriggerTime - currentTime;
|
||||||
float delta = triggerTime - currentTime;
|
if (delta < 0) delta = 0;
|
||||||
|
|
||||||
if (delta < 0)
|
int totalSlots = Mathf.FloorToInt(delta / _slotInterval);
|
||||||
|
int level = 0;
|
||||||
|
int slotIndex = 0;
|
||||||
|
|
||||||
|
if (totalSlots < SLOT_COUNT_LEVEL0)
|
||||||
{
|
{
|
||||||
// 立即触发
|
level = 0;
|
||||||
delta = 0;
|
slotIndex = (_levels[0].CurrentSlot + totalSlots) % SLOT_COUNT_LEVEL0;
|
||||||
|
}
|
||||||
|
else if (totalSlots < SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1)
|
||||||
|
{
|
||||||
|
level = 1;
|
||||||
|
int level1Slots = totalSlots / SLOT_COUNT_LEVEL0;
|
||||||
|
slotIndex = (_levels[1].CurrentSlot + level1Slots) % SLOT_COUNT_LEVEL1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
level = 2;
|
||||||
|
int level2Slots = totalSlots / (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1);
|
||||||
|
slotIndex = (_levels[2].CurrentSlot + level2Slots) % SLOT_COUNT_LEVEL2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int slotsToAdvance = Mathf.FloorToInt(delta / _slotInterval);
|
timer.SlotIndex = slotIndex;
|
||||||
int targetSlot = (_currentSlotIndex + slotsToAdvance) % _slotCount;
|
timer.NextTimerIndex = _levels[level].SlotHeads[slotIndex];
|
||||||
|
timer.PrevTimerIndex = -1;
|
||||||
|
|
||||||
timer.Node = _slots[targetSlot].AddLast(timer);
|
if (_levels[level].SlotHeads[slotIndex] != -1)
|
||||||
|
{
|
||||||
|
pool[_levels[level].SlotHeads[slotIndex]].PrevTimerIndex = poolIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
_levels[level].SlotHeads[slotIndex] = poolIndex;
|
||||||
timer.IsRunning = true;
|
timer.IsRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Advance(float currentTime, Action<Timer> processTimer)
|
public void Advance(float currentTime, TimerInfo[] pool, Action<int> processTimer)
|
||||||
{
|
{
|
||||||
float timeDelta = currentTime - _currentTime;
|
float timeDelta = currentTime - _currentTime;
|
||||||
if (timeDelta <= 0) return;
|
if (timeDelta <= 0) return;
|
||||||
|
|
||||||
int steps = Mathf.FloorToInt(timeDelta / _slotInterval);
|
int steps = Mathf.FloorToInt(timeDelta / _slotInterval);
|
||||||
for (int i = 0; i < steps; i++)
|
|
||||||
|
for (int step = 0; step < steps; step++)
|
||||||
{
|
{
|
||||||
_currentSlotIndex = (_currentSlotIndex + 1) % _slotCount;
|
|
||||||
_currentTime += _slotInterval;
|
_currentTime += _slotInterval;
|
||||||
|
ProcessLevel(0, pool, processTimer);
|
||||||
|
|
||||||
LinkedList<Timer> currentSlot = _slots[_currentSlotIndex];
|
if ((_levels[0].CurrentSlot % SLOT_COUNT_LEVEL0) == 0)
|
||||||
LinkedListNode<Timer> currentNode = currentSlot.First;
|
|
||||||
while (currentNode != null)
|
|
||||||
{
|
{
|
||||||
LinkedListNode<Timer> nextNode = currentNode.Next;
|
ProcessLevel(1, pool, processTimer);
|
||||||
Timer timer = currentNode.Value;
|
|
||||||
currentSlot.Remove(currentNode);
|
|
||||||
|
|
||||||
if (timer.IsActive && timer.IsRunning)
|
|
||||||
{
|
|
||||||
processTimer(timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentNode = nextNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((_levels[0].CurrentSlot % (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1)) == 0)
|
||||||
|
{
|
||||||
|
ProcessLevel(2, pool, processTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void ProcessLevel(int level, TimerInfo[] pool, Action<int> processTimer)
|
||||||
|
{
|
||||||
|
TimeWheelLevel wheelLevel = _levels[level];
|
||||||
|
wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) % wheelLevel.SlotCount;
|
||||||
|
|
||||||
|
int currentHead = wheelLevel.SlotHeads[wheelLevel.CurrentSlot];
|
||||||
|
wheelLevel.SlotHeads[wheelLevel.CurrentSlot] = -1;
|
||||||
|
|
||||||
|
int currentIndex = currentHead;
|
||||||
|
while (currentIndex != -1)
|
||||||
|
{
|
||||||
|
int nextIndex = pool[currentIndex].NextTimerIndex;
|
||||||
|
|
||||||
|
if (pool[currentIndex].IsActive && pool[currentIndex].IsRunning)
|
||||||
|
{
|
||||||
|
if (level == 0)
|
||||||
|
{
|
||||||
|
processTimer(currentIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddTimer(currentIndex, pool, _currentTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentIndex = nextIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveTimer(int poolIndex, TimerInfo[] pool)
|
||||||
|
{
|
||||||
|
ref TimerInfo timer = ref pool[poolIndex];
|
||||||
|
|
||||||
|
if (timer.PrevTimerIndex != -1)
|
||||||
|
{
|
||||||
|
pool[timer.PrevTimerIndex].NextTimerIndex = timer.NextTimerIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int level = 0; level < _levels.Length; level++)
|
||||||
|
{
|
||||||
|
if (_levels[level].SlotHeads[timer.SlotIndex] == poolIndex)
|
||||||
|
{
|
||||||
|
_levels[level].SlotHeads[timer.SlotIndex] = timer.NextTimerIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timer.NextTimerIndex != -1)
|
||||||
|
{
|
||||||
|
pool[timer.NextTimerIndex].PrevTimerIndex = timer.PrevTimerIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimerModule()
|
public TimerModule()
|
||||||
{
|
{
|
||||||
_scaledTimeWheel = new TimeWheel(1f, 60);
|
_timerPoolCapacity = 64;
|
||||||
_unscaledTimeWheel = new TimeWheel(1f, 60);
|
_timerPool = new TimerInfo[_timerPoolCapacity];
|
||||||
_processTimerDelegate = ProcessTimer; // 初始化时创建委托一次
|
_timerIdToPoolIndex = new int[1024];
|
||||||
|
_freeIndices = new int[_timerPoolCapacity];
|
||||||
|
_freeCount = _timerPoolCapacity;
|
||||||
|
|
||||||
|
for (int i = 0; i < _timerPoolCapacity; i++)
|
||||||
|
{
|
||||||
|
_freeIndices[i] = i;
|
||||||
|
_timerPool[i].NextTimerIndex = -1;
|
||||||
|
_timerPool[i].PrevTimerIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_scaledTimeWheel = new HierarchicalTimeWheel(0.001f);
|
||||||
|
_unscaledTimeWheel = new HierarchicalTimeWheel(0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int AddTimer(TimerHandlerNoArgs callback, float time, bool isLoop = false, bool isUnscaled = false)
|
||||||
|
{
|
||||||
|
int poolIndex = AcquireTimerFromPool();
|
||||||
|
ref TimerInfo timer = ref _timerPool[poolIndex];
|
||||||
|
|
||||||
|
timer.TimerId = ++_curTimerId;
|
||||||
|
timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time;
|
||||||
|
timer.Interval = isLoop ? time : 0f;
|
||||||
|
timer.HandlerNoArgs = callback;
|
||||||
|
timer.HandlerType = 1;
|
||||||
|
timer.IsLoop = isLoop;
|
||||||
|
timer.IsRunning = true;
|
||||||
|
timer.IsUnscaled = isUnscaled;
|
||||||
|
timer.IsActive = true;
|
||||||
|
timer.Args = null;
|
||||||
|
timer.GenericArg = null;
|
||||||
|
|
||||||
|
RegisterTimer(timer.TimerId, poolIndex);
|
||||||
|
|
||||||
|
HierarchicalTimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel;
|
||||||
|
targetWheel.AddTimer(poolIndex, _timerPool, isUnscaled ? Time.unscaledTime : Time.time);
|
||||||
|
|
||||||
|
return timer.TimerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int AddTimer(TimerHandler callback, float time, bool isLoop = false,
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
bool isUnscaled = false, params object[] args)
|
public int AddTimer<T>(Action<T> callback, T arg, float time, bool isLoop = false, bool isUnscaled = false)
|
||||||
{
|
{
|
||||||
Timer timer = GetTimerFromPool();
|
int poolIndex = AcquireTimerFromPool();
|
||||||
|
ref TimerInfo timer = ref _timerPool[poolIndex];
|
||||||
|
|
||||||
|
timer.TimerId = ++_curTimerId;
|
||||||
|
timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time;
|
||||||
|
timer.Interval = isLoop ? time : 0f;
|
||||||
|
timer.HandlerGeneric = callback; // 直接存储委托,无需包装
|
||||||
|
timer.GenericArg = arg; // struct会装箱一次,但只在添加时
|
||||||
|
timer.HandlerType = 2;
|
||||||
|
timer.IsLoop = isLoop;
|
||||||
|
timer.IsRunning = true;
|
||||||
|
timer.IsUnscaled = isUnscaled;
|
||||||
|
timer.IsActive = true;
|
||||||
|
timer.Args = null;
|
||||||
|
|
||||||
|
RegisterTimer(timer.TimerId, poolIndex);
|
||||||
|
|
||||||
|
HierarchicalTimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel;
|
||||||
|
targetWheel.AddTimer(poolIndex, _timerPool, isUnscaled ? Time.unscaledTime : Time.time);
|
||||||
|
|
||||||
|
return timer.TimerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args)
|
||||||
|
{
|
||||||
|
int poolIndex = AcquireTimerFromPool();
|
||||||
|
ref TimerInfo timer = ref _timerPool[poolIndex];
|
||||||
|
|
||||||
timer.TimerId = ++_curTimerId;
|
timer.TimerId = ++_curTimerId;
|
||||||
timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time;
|
timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time;
|
||||||
timer.Interval = isLoop ? time : 0f;
|
timer.Interval = isLoop ? time : 0f;
|
||||||
timer.Handler = callback;
|
timer.Handler = callback;
|
||||||
|
timer.HandlerType = 0;
|
||||||
timer.IsLoop = isLoop;
|
timer.IsLoop = isLoop;
|
||||||
timer.IsRunning = true;
|
timer.IsRunning = true;
|
||||||
timer.IsUnscaled = isUnscaled;
|
timer.IsUnscaled = isUnscaled;
|
||||||
timer.Args = args;
|
|
||||||
timer.IsActive = true;
|
timer.IsActive = true;
|
||||||
|
timer.Args = args;
|
||||||
|
timer.GenericArg = null;
|
||||||
|
|
||||||
|
RegisterTimer(timer.TimerId, poolIndex);
|
||||||
|
|
||||||
|
HierarchicalTimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel;
|
||||||
|
targetWheel.AddTimer(poolIndex, _timerPool, isUnscaled ? Time.unscaledTime : Time.time);
|
||||||
|
|
||||||
_activeTimers.Add(timer.TimerId, timer);
|
|
||||||
TimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel;
|
|
||||||
targetWheel.AddTimer(timer, isUnscaled ? Time.unscaledTime : Time.time);
|
|
||||||
return timer.TimerId;
|
return timer.TimerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private int AcquireTimerFromPool()
|
||||||
|
{
|
||||||
|
if (_freeCount > 0)
|
||||||
|
{
|
||||||
|
return _freeIndices[--_freeCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
int oldCapacity = _timerPoolCapacity;
|
||||||
|
_timerPoolCapacity *= 2;
|
||||||
|
Array.Resize(ref _timerPool, _timerPoolCapacity);
|
||||||
|
Array.Resize(ref _freeIndices, _timerPoolCapacity);
|
||||||
|
|
||||||
|
for (int i = _timerPoolCapacity - 1; i >= oldCapacity; i--)
|
||||||
|
{
|
||||||
|
_freeIndices[_freeCount++] = i;
|
||||||
|
_timerPool[i].NextTimerIndex = -1;
|
||||||
|
_timerPool[i].PrevTimerIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _freeIndices[--_freeCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void RegisterTimer(int timerId, int poolIndex)
|
||||||
|
{
|
||||||
|
if (timerId >= _timerIdToPoolIndex.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _timerIdToPoolIndex, _timerIdToPoolIndex.Length * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
_timerIdToPoolIndex[timerId] = poolIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private int GetPoolIndex(int timerId)
|
||||||
|
{
|
||||||
|
return timerId < _timerIdToPoolIndex.Length ? _timerIdToPoolIndex[timerId] : -1;
|
||||||
|
}
|
||||||
|
|
||||||
public void Stop(int timerId)
|
public void Stop(int timerId)
|
||||||
{
|
{
|
||||||
if (_activeTimers.TryGetValue(timerId, out Timer timer))
|
int poolIndex = GetPoolIndex(timerId);
|
||||||
timer.IsRunning = false;
|
if (poolIndex >= 0 && _timerPool[poolIndex].IsActive)
|
||||||
|
_timerPool[poolIndex].IsRunning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resume(int timerId)
|
public void Resume(int timerId)
|
||||||
{
|
{
|
||||||
if (_activeTimers.TryGetValue(timerId, out Timer timer))
|
int poolIndex = GetPoolIndex(timerId);
|
||||||
timer.IsRunning = true;
|
if (poolIndex >= 0 && _timerPool[poolIndex].IsActive)
|
||||||
|
_timerPool[poolIndex].IsRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsRunning(int timerId) =>
|
public bool IsRunning(int timerId)
|
||||||
_activeTimers.TryGetValue(timerId, out Timer timer) && timer.IsRunning;
|
{
|
||||||
|
int poolIndex = GetPoolIndex(timerId);
|
||||||
|
return poolIndex >= 0 && _timerPool[poolIndex].IsActive && _timerPool[poolIndex].IsRunning;
|
||||||
|
}
|
||||||
|
|
||||||
public float GetLeftTime(int timerId) =>
|
public float GetLeftTime(int timerId)
|
||||||
_activeTimers.TryGetValue(timerId, out Timer timer)
|
{
|
||||||
? Mathf.Max(timer.TriggerTime - (timer.IsUnscaled ? Time.unscaledTime : Time.time), 0)
|
int poolIndex = GetPoolIndex(timerId);
|
||||||
: 0;
|
if (poolIndex < 0 || !_timerPool[poolIndex].IsActive) return 0;
|
||||||
|
|
||||||
|
ref TimerInfo timer = ref _timerPool[poolIndex];
|
||||||
|
return Mathf.Max(timer.TriggerTime - (timer.IsUnscaled ? Time.unscaledTime : Time.time), 0);
|
||||||
|
}
|
||||||
|
|
||||||
public void Restart(int timerId)
|
public void Restart(int timerId)
|
||||||
{
|
{
|
||||||
if (_activeTimers.TryGetValue(timerId, out Timer timer))
|
int poolIndex = GetPoolIndex(timerId);
|
||||||
{
|
if (poolIndex < 0 || !_timerPool[poolIndex].IsActive) return;
|
||||||
timer.TriggerTime = (timer.IsUnscaled ? Time.unscaledTime : Time.time) + timer.Interval;
|
|
||||||
TimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel;
|
ref TimerInfo timer = ref _timerPool[poolIndex];
|
||||||
targetWheel.AddTimer(timer, timer.IsUnscaled ? Time.unscaledTime : Time.time);
|
timer.TriggerTime = (timer.IsUnscaled ? Time.unscaledTime : Time.time) + timer.Interval;
|
||||||
}
|
|
||||||
|
HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel;
|
||||||
|
targetWheel.AddTimer(poolIndex, _timerPool, timer.IsUnscaled ? Time.unscaledTime : Time.time);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveTimer(int timerId)
|
public void RemoveTimer(int timerId)
|
||||||
{
|
{
|
||||||
if (_activeTimers.TryGetValue(timerId, out Timer timer))
|
int poolIndex = GetPoolIndex(timerId);
|
||||||
{
|
if (poolIndex < 0 || !_timerPool[poolIndex].IsActive) return;
|
||||||
timer.IsActive = false; // 标记为无效
|
|
||||||
_activeTimers.Remove(timerId);
|
ref TimerInfo timer = ref _timerPool[poolIndex];
|
||||||
ReturnTimerToPool(timer);
|
timer.IsActive = false;
|
||||||
}
|
|
||||||
|
HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel;
|
||||||
|
targetWheel.RemoveTimer(poolIndex, _timerPool);
|
||||||
|
|
||||||
|
ReleaseTimerToPool(poolIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveAllTimer()
|
public void RemoveAllTimer()
|
||||||
{
|
{
|
||||||
foreach (var timer in _activeTimers.Values)
|
for (int i = 0; i < _timerPoolCapacity; i++)
|
||||||
{
|
{
|
||||||
timer.IsActive = false;
|
if (_timerPool[i].IsActive)
|
||||||
ReturnTimerToPool(timer);
|
{
|
||||||
|
_timerPool[i].Clear();
|
||||||
|
_freeIndices[_freeCount++] = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_activeTimers.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Timer GetTimerFromPool() => MemoryPool.Acquire<Timer>();
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void ReleaseTimerToPool(int poolIndex)
|
||||||
|
|
||||||
private void ReturnTimerToPool(Timer timer)
|
|
||||||
{
|
{
|
||||||
MemoryPool.Release(timer);
|
_timerPool[poolIndex].Clear();
|
||||||
|
_freeIndices[_freeCount++] = poolIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds)
|
void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds)
|
||||||
{
|
{
|
||||||
float scaledTime = Time.time;
|
_scaledTimeWheel.Advance(Time.time, _timerPool, ProcessTimer);
|
||||||
_scaledTimeWheel.Advance(scaledTime, _processTimerDelegate);
|
_unscaledTimeWheel.Advance(Time.unscaledTime, _timerPool, ProcessTimer);
|
||||||
|
|
||||||
float unscaledTime = Time.unscaledTime;
|
|
||||||
_unscaledTimeWheel.Advance(unscaledTime, _processTimerDelegate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessTimer(Timer timer)
|
// 优化7: 使用静态泛型分发,避免反射和类型检查
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void ProcessTimer(int poolIndex)
|
||||||
{
|
{
|
||||||
|
ref TimerInfo timer = ref _timerPool[poolIndex];
|
||||||
if (!timer.IsActive || !timer.IsRunning) return;
|
if (!timer.IsActive || !timer.IsRunning) return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
timer.Handler?.Invoke(timer.Args);
|
switch (timer.HandlerType)
|
||||||
|
{
|
||||||
|
case 0: // TimerHandler
|
||||||
|
timer.Handler?.Invoke(timer.Args);
|
||||||
|
break;
|
||||||
|
case 1: // NoArgs
|
||||||
|
timer.HandlerNoArgs?.Invoke();
|
||||||
|
break;
|
||||||
|
case 2: // Generic<struct>
|
||||||
|
case 3: // Generic<class>
|
||||||
|
// 直接调用,利用JIT优化
|
||||||
|
((dynamic)timer.HandlerGeneric)?.Invoke((dynamic)timer.GenericArg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error($"Timer callback error: {e}");
|
Log.Error($"TimerInfo callback error: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer.IsLoop)
|
if (timer.IsLoop)
|
||||||
{
|
{
|
||||||
timer.TriggerTime += timer.Interval;
|
timer.TriggerTime += timer.Interval;
|
||||||
TimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel;
|
HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel;
|
||||||
targetWheel.AddTimer(timer, timer.IsUnscaled ? Time.unscaledTime : Time.time);
|
targetWheel.AddTimer(poolIndex, _timerPool, timer.IsUnscaled ? Time.unscaledTime : Time.time);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user