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

438 lines
14 KiB
C#
Raw Normal View History

2025-10-13 20:20:01 +08:00
#if INPUTSYSTEM_SUPPORT
2025-12-09 20:30:11 +08:00
using System;
2025-12-24 15:13:57 +08:00
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
2025-10-13 20:20:01 +08:00
using UnityEngine;
using UnityEngine.InputSystem;
2025-12-24 15:13:57 +08:00
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.UI;
namespace UnityEngine.UI
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
internal enum EHotkeyPressType : byte
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
Started = 0,
Performed = 1
2025-12-09 20:30:11 +08:00
}
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
// 优化1: 使用struct减少GC
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct HotkeyRegistration
2025-12-09 20:30:11 +08:00
{
2025-12-24 15:13:57 +08:00
public IHotkeyTrigger button;
public EHotkeyPressType pressType;
2025-12-09 20:30:11 +08:00
2025-12-24 15:13:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HotkeyRegistration(IHotkeyTrigger btn, EHotkeyPressType pressType)
{
this.button = btn;
this.pressType = pressType;
2025-10-13 20:20:01 +08:00
}
2025-12-24 15:13:57 +08:00
}
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
// 优化2: 使用struct存储handler信息
[StructLayout(LayoutKind.Sequential)]
internal struct HandlerInfo
{
public Action<InputAction.CallbackContext> handler;
public InputActionReference action;
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
public HandlerInfo(Action<InputAction.CallbackContext> handler, InputActionReference action)
{
this.handler = handler;
this.action = action;
}
}
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
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];
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
_buttonCapacity = 64;
_buttons = new IHotkeyTrigger[_buttonCapacity];
_buttonToActionIndex = new int[_buttonCapacity];
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
_cachedHandlers = new Action<InputAction.CallbackContext>[INITIAL_CAPACITY];
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
for (int i = 0; i < _actionCapacity; i++)
{
_registrationPool[i] = new HotkeyRegistration[MAX_REGISTRATIONS_PER_ACTION];
_buttonToActionIndex[i] = -1;
}
}
2025-10-13 20:20:01 +08:00
#if UNITY_EDITOR
2025-12-15 19:38:08 +08:00
[UnityEditor.Callbacks.DidReloadScripts]
2025-12-09 20:30:11 +08:00
internal static void ClearHotkeyRegistry()
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
for (int i = 0; i < _buttonCount; i++)
2025-12-09 20:30:11 +08:00
{
2025-12-24 15:13:57 +08:00
if (_buttons[i] != null)
{
UnregisterHotkey(_buttons[i]);
}
2025-12-09 20:30:11 +08:00
}
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
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;
2025-12-09 20:30:11 +08:00
}
2025-10-13 20:20:01 +08:00
#endif
2025-12-24 15:13:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void RegisterHotkey(IHotkeyTrigger button, InputActionReference action, EHotkeyPressType pressType)
2025-12-09 20:30:11 +08:00
{
if (action == null || action.action == null || button == null)
return;
2025-10-13 20:20:01 +08:00
2025-12-09 20:30:11 +08:00
string actionId = action.action.id.ToString();
2025-12-24 15:13:57 +08:00
int actionIndex = FindOrCreateActionIndex(actionId);
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
// 添加注册信息
ref int count = ref _registrationCounts[actionIndex];
if (count >= MAX_REGISTRATIONS_PER_ACTION)
2025-12-09 20:30:11 +08:00
{
2025-12-24 15:13:57 +08:00
Debug.LogWarning($"Max registrations reached for action: {actionId}");
return;
2025-12-09 20:30:11 +08:00
}
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
_registrationPool[actionIndex][count] = new HotkeyRegistration(button, pressType);
count++;
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
// 记录button映射
int buttonIndex = FindOrCreateButtonIndex(button);
_buttonToActionIndex[buttonIndex] = actionIndex;
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
// 优化8: 只在第一次注册时创建handler
if (count == 1)
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
Action<InputAction.CallbackContext> handler = GetOrCreateHandler(actionIndex);
_handlers[actionIndex] = new HandlerInfo(handler, action);
_actionRefs[actionIndex] = action;
2025-12-09 20:30:11 +08:00
switch (pressType)
{
case EHotkeyPressType.Started:
action.action.started += handler;
break;
case EHotkeyPressType.Performed:
action.action.performed += handler;
break;
}
2025-10-13 20:20:01 +08:00
2025-12-09 20:30:11 +08:00
action.action.Enable();
}
2025-10-13 20:20:01 +08:00
}
2025-12-24 15:13:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnregisterHotkey(IHotkeyTrigger button)
2025-12-09 20:30:11 +08:00
{
2025-12-24 15:13:57 +08:00
if (button == null)
2025-12-09 20:30:11 +08:00
return;
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
int buttonIndex = FindButtonIndex(button);
if (buttonIndex < 0)
return;
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
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--)
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
if (ReferenceEquals(registrations[i].button, button))
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
EHotkeyPressType pressType = registrations[i].pressType;
// 优化9: Swap-remove避免数组移动
registrations[i] = registrations[count - 1];
registrations[count - 1] = default;
count--;
// 如果是最后一个注册清理handler
if (count == 0)
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
ref HandlerInfo handlerInfo = ref _handlers[actionIndex];
InputActionReference actionRef = _actionRefs[actionIndex];
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
if (actionRef != null && actionRef.action != null)
2025-12-09 20:30:11 +08:00
{
2025-12-24 15:13:57 +08:00
switch (pressType)
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
case EHotkeyPressType.Started:
actionRef.action.started -= handlerInfo.handler;
break;
case EHotkeyPressType.Performed:
actionRef.action.performed -= handlerInfo.handler;
break;
2025-10-13 20:20:01 +08:00
}
2025-12-24 15:13:57 +08:00
actionRef.action.Disable();
2025-10-13 20:20:01 +08:00
}
2025-12-24 15:13:57 +08:00
handlerInfo = default;
_actionRefs[actionIndex] = null;
_actionIds[actionIndex] = null;
2025-12-09 20:30:11 +08:00
}
2025-12-24 15:13:57 +08:00
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;
2025-10-13 20:20:01 +08:00
}
}
2025-12-24 15:13:57 +08:00
// 扩容
if (_buttonCount >= _buttonCapacity)
{
ExpandButtonCapacity();
}
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
int index = _buttonCount++;
_buttons[index] = button;
return index;
2025-12-09 20:30:11 +08:00
}
2025-10-13 20:20:01 +08:00
2025-12-24 15:13:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int FindButtonIndex(IHotkeyTrigger button)
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
for (int i = 0; i < _buttonCount; i++)
2025-12-09 20:30:11 +08:00
{
2025-12-24 15:13:57 +08:00
if (ReferenceEquals(_buttons[i], button))
return i;
2025-12-09 20:30:11 +08:00
}
2025-12-24 15:13:57 +08:00
return -1;
2025-10-13 20:20:01 +08:00
}
2025-12-24 15:13:57 +08:00
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
2025-10-13 20:20:01 +08:00
}
}
public static class UXHotkeyHotkeyExtension
{
2025-12-24 15:13:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void BindHotKey(this IHotkeyTrigger button)
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
if (button?.HotkeyAction != null)
2025-10-13 20:20:01 +08:00
{
UXHotkeyRegisterManager.RegisterHotkey(
button,
button.HotkeyAction,
button.HotkeyPressType
2025-10-13 20:20:01 +08:00
);
}
}
2025-12-24 15:13:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnBindHotKey(this IHotkeyTrigger button)
2025-10-13 20:20:01 +08:00
{
2025-12-24 15:13:57 +08:00
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();
}
2025-10-13 20:20:01 +08:00
}
}
#endif