com.alicizax.unity.ui.exten.../Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs

438 lines
14 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.

#if INPUTSYSTEM_SUPPORT
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.InputSystem;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.UI;
namespace UnityEngine.UI
{
internal enum EHotkeyPressType : byte
{
Started = 0,
Performed = 1
}
// 优化1: 使用struct减少GC
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct HotkeyRegistration
{
public IHotkeyTrigger button;
public EHotkeyPressType pressType;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HotkeyRegistration(IHotkeyTrigger btn, EHotkeyPressType pressType)
{
this.button = btn;
this.pressType = pressType;
}
}
// 优化2: 使用struct存储handler信息
[StructLayout(LayoutKind.Sequential)]
internal struct HandlerInfo
{
public Action<InputAction.CallbackContext> handler;
public InputActionReference action;
public HandlerInfo(Action<InputAction.CallbackContext> handler, InputActionReference action)
{
this.handler = handler;
this.action = action;
}
}
internal static class UXHotkeyRegisterManager
{
// 优化3: 使用数组池替代List避免频繁扩容
private const int INITIAL_CAPACITY = 32;
private const int MAX_REGISTRATIONS_PER_ACTION = 16;
// 优化4: 使用固定大小的数组池
private static HotkeyRegistration[][] _registrationPool;
private static int[] _registrationCounts;
private static string[] _actionIds;
private static int _actionCount;
private static int _actionCapacity;
// 优化5: 使用数组替代Dictionary更快的查找
private static HandlerInfo[] _handlers;
private static InputActionReference[] _actionRefs;
// 优化6: Button到ActionId的映射使用数组索引
private static IHotkeyTrigger[] _buttons;
private static int[] _buttonToActionIndex;
private static int _buttonCount;
private static int _buttonCapacity;
// 优化7: 缓存委托,避免每次创建
private static Action<InputAction.CallbackContext>[] _cachedHandlers;
private static int _cachedHandlerCount;
static UXHotkeyRegisterManager()
{
_actionCapacity = INITIAL_CAPACITY;
_registrationPool = new HotkeyRegistration[_actionCapacity][];
_registrationCounts = new int[_actionCapacity];
_actionIds = new string[_actionCapacity];
_handlers = new HandlerInfo[_actionCapacity];
_actionRefs = new InputActionReference[_actionCapacity];
_buttonCapacity = 64;
_buttons = new IHotkeyTrigger[_buttonCapacity];
_buttonToActionIndex = new int[_buttonCapacity];
_cachedHandlers = new Action<InputAction.CallbackContext>[INITIAL_CAPACITY];
for (int i = 0; i < _actionCapacity; i++)
{
_registrationPool[i] = new HotkeyRegistration[MAX_REGISTRATIONS_PER_ACTION];
_buttonToActionIndex[i] = -1;
}
}
#if UNITY_EDITOR
[UnityEditor.Callbacks.DidReloadScripts]
internal static void ClearHotkeyRegistry()
{
for (int i = 0; i < _buttonCount; i++)
{
if (_buttons[i] != null)
{
UnregisterHotkey(_buttons[i]);
}
}
Array.Clear(_registrationCounts, 0, _actionCount);
Array.Clear(_actionIds, 0, _actionCount);
Array.Clear(_handlers, 0, _actionCount);
Array.Clear(_actionRefs, 0, _actionCount);
Array.Clear(_buttons, 0, _buttonCount);
Array.Clear(_buttonToActionIndex, 0, _buttonCount);
_actionCount = 0;
_buttonCount = 0;
_cachedHandlerCount = 0;
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void RegisterHotkey(IHotkeyTrigger button, InputActionReference action, EHotkeyPressType pressType)
{
if (action == null || action.action == null || button == null)
return;
string actionId = action.action.id.ToString();
int actionIndex = FindOrCreateActionIndex(actionId);
// 添加注册信息
ref int count = ref _registrationCounts[actionIndex];
if (count >= MAX_REGISTRATIONS_PER_ACTION)
{
Debug.LogWarning($"Max registrations reached for action: {actionId}");
return;
}
_registrationPool[actionIndex][count] = new HotkeyRegistration(button, pressType);
count++;
// 记录button映射
int buttonIndex = FindOrCreateButtonIndex(button);
_buttonToActionIndex[buttonIndex] = actionIndex;
// 优化8: 只在第一次注册时创建handler
if (count == 1)
{
Action<InputAction.CallbackContext> handler = GetOrCreateHandler(actionIndex);
_handlers[actionIndex] = new HandlerInfo(handler, action);
_actionRefs[actionIndex] = action;
switch (pressType)
{
case EHotkeyPressType.Started:
action.action.started += handler;
break;
case EHotkeyPressType.Performed:
action.action.performed += handler;
break;
}
action.action.Enable();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnregisterHotkey(IHotkeyTrigger button)
{
if (button == null)
return;
int buttonIndex = FindButtonIndex(button);
if (buttonIndex < 0)
return;
int actionIndex = _buttonToActionIndex[buttonIndex];
if (actionIndex < 0)
return;
// 从注册列表中移除
ref int count = ref _registrationCounts[actionIndex];
HotkeyRegistration[] registrations = _registrationPool[actionIndex];
for (int i = count - 1; i >= 0; i--)
{
if (ReferenceEquals(registrations[i].button, button))
{
EHotkeyPressType pressType = registrations[i].pressType;
// 优化9: Swap-remove避免数组移动
registrations[i] = registrations[count - 1];
registrations[count - 1] = default;
count--;
// 如果是最后一个注册清理handler
if (count == 0)
{
ref HandlerInfo handlerInfo = ref _handlers[actionIndex];
InputActionReference actionRef = _actionRefs[actionIndex];
if (actionRef != null && actionRef.action != null)
{
switch (pressType)
{
case EHotkeyPressType.Started:
actionRef.action.started -= handlerInfo.handler;
break;
case EHotkeyPressType.Performed:
actionRef.action.performed -= handlerInfo.handler;
break;
}
actionRef.action.Disable();
}
handlerInfo = default;
_actionRefs[actionIndex] = null;
_actionIds[actionIndex] = null;
}
break;
}
}
// 清理button映射
_buttons[buttonIndex] = null;
_buttonToActionIndex[buttonIndex] = -1;
}
// 优化10: 使用缓存的委托,避免闭包分配
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Action<InputAction.CallbackContext> GetOrCreateHandler(int actionIndex)
{
if (actionIndex < _cachedHandlers.Length && _cachedHandlers[actionIndex] != null)
{
return _cachedHandlers[actionIndex];
}
if (actionIndex >= _cachedHandlers.Length)
{
Array.Resize(ref _cachedHandlers, actionIndex + 1);
}
// 优化11: 使用静态方法 + 参数传递,避免闭包
int capturedIndex = actionIndex;
_cachedHandlers[actionIndex] = ctx => OnHotkeyTriggered(capturedIndex);
return _cachedHandlers[actionIndex];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void OnHotkeyTriggered(int actionIndex)
{
int count = _registrationCounts[actionIndex];
if (count > 0)
{
// 触发最后一个注册的button栈顶
ref HotkeyRegistration registration = ref _registrationPool[actionIndex][count - 1];
registration.button?.HotkeyActionTrigger();
}
}
// 优化12: 线性查找小数据集比Dictionary更快
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int FindOrCreateActionIndex(string actionId)
{
// 查找现有
for (int i = 0; i < _actionCount; i++)
{
if (_actionIds[i] == actionId)
return i;
}
// 创建新的
if (_actionCount >= _actionCapacity)
{
ExpandActionCapacity();
}
int index = _actionCount++;
_actionIds[index] = actionId;
return index;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int FindOrCreateButtonIndex(IHotkeyTrigger button)
{
// 查找现有
for (int i = 0; i < _buttonCount; i++)
{
if (ReferenceEquals(_buttons[i], button))
return i;
}
// 查找空槽
for (int i = 0; i < _buttonCapacity; i++)
{
if (_buttons[i] == null)
{
_buttons[i] = button;
if (i >= _buttonCount)
_buttonCount = i + 1;
return i;
}
}
// 扩容
if (_buttonCount >= _buttonCapacity)
{
ExpandButtonCapacity();
}
int index = _buttonCount++;
_buttons[index] = button;
return index;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int FindButtonIndex(IHotkeyTrigger button)
{
for (int i = 0; i < _buttonCount; i++)
{
if (ReferenceEquals(_buttons[i], button))
return i;
}
return -1;
}
private static void ExpandActionCapacity()
{
int newCapacity = _actionCapacity * 2;
Array.Resize(ref _registrationPool, newCapacity);
Array.Resize(ref _registrationCounts, newCapacity);
Array.Resize(ref _actionIds, newCapacity);
Array.Resize(ref _handlers, newCapacity);
Array.Resize(ref _actionRefs, newCapacity);
for (int i = _actionCapacity; i < newCapacity; i++)
{
_registrationPool[i] = new HotkeyRegistration[MAX_REGISTRATIONS_PER_ACTION];
}
_actionCapacity = newCapacity;
}
private static void ExpandButtonCapacity()
{
int newCapacity = _buttonCapacity * 2;
Array.Resize(ref _buttons, newCapacity);
Array.Resize(ref _buttonToActionIndex, newCapacity);
for (int i = _buttonCapacity; i < newCapacity; i++)
{
_buttonToActionIndex[i] = -1;
}
_buttonCapacity = newCapacity;
}
// 优化13: 批量操作API
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RegisterHotkeyBatch(Span<IHotkeyTrigger> buttons, InputActionReference action, EHotkeyPressType pressType)
{
for (int i = 0; i < buttons.Length; i++)
{
RegisterHotkey(buttons[i], action, pressType);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnregisterHotkeyBatch(Span<IHotkeyTrigger> buttons)
{
for (int i = 0; i < buttons.Length; i++)
{
UnregisterHotkey(buttons[i]);
}
}
// 调试信息
#if UNITY_EDITOR
public static string GetDebugInfo()
{
return $"Actions: {_actionCount}/{_actionCapacity}, Buttons: {_buttonCount}/{_buttonCapacity}";
}
#endif
}
}
public static class UXHotkeyHotkeyExtension
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void BindHotKey(this IHotkeyTrigger button)
{
if (button?.HotkeyAction != null)
{
UXHotkeyRegisterManager.RegisterHotkey(
button,
button.HotkeyAction,
button.HotkeyPressType
);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnBindHotKey(this IHotkeyTrigger button)
{
if (button?.HotkeyAction != null)
{
UXHotkeyRegisterManager.UnregisterHotkey(button);
}
}
// 优化14: 批量绑定
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void BindHotKeyBatch(this IHotkeyTrigger[] buttons)
{
if (buttons == null) return;
for (int i = 0; i < buttons.Length; i++)
{
buttons[i]?.BindHotKey();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnBindHotKeyBatch(this IHotkeyTrigger[] buttons)
{
if (buttons == null) return;
for (int i = 0; i < buttons.Length; i++)
{
buttons[i]?.UnBindHotKey();
}
}
}
#endif