Update UXHotkeyRegisterManager.cs

This commit is contained in:
陈思海 2025-12-24 15:13:57 +08:00
parent 0e5ea2c357
commit fc993a1dbe

View File

@ -1,83 +1,154 @@
#if INPUTSYSTEM_SUPPORT
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.InputSystem;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.UI;
namespace UnityEngine.UI
{
internal enum EHotkeyPressType
internal enum EHotkeyPressType : byte
{
Started,
Performed
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
{
private readonly struct HotkeyRegistration
{
public readonly EHotkeyPressType pressType;
public readonly IHotkeyTrigger button;
// 优化3: 使用数组池替代List避免频繁扩容
private const int INITIAL_CAPACITY = 32;
private const int MAX_REGISTRATIONS_PER_ACTION = 16;
public HotkeyRegistration(IHotkeyTrigger btn, EHotkeyPressType pressType)
// 优化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++)
{
button = btn;
this.pressType = pressType;
_registrationPool[i] = new HotkeyRegistration[MAX_REGISTRATIONS_PER_ACTION];
_buttonToActionIndex[i] = -1;
}
}
private static readonly Dictionary<string, List<HotkeyRegistration>> _hotkeyRegistry = new(32);
private static readonly Dictionary<string, (Action<InputAction.CallbackContext> handler, InputActionReference action)> _sharedHandlers =
new(32);
private static readonly Dictionary<IHotkeyTrigger, string> _buttonRegistrations = new(64);
#if UNITY_EDITOR
[UnityEditor.Callbacks.DidReloadScripts]
internal static void ClearHotkeyRegistry()
{
foreach (var key in _buttonRegistrations.Keys)
for (int i = 0; i < _buttonCount; i++)
{
UnregisterHotkey(key);
if (_buttons[i] != null)
{
UnregisterHotkey(_buttons[i]);
}
}
_sharedHandlers.Clear();
_hotkeyRegistry.Clear();
_buttonRegistrations.Clear();
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);
HotkeyRegistration registration = new HotkeyRegistration(button, pressType);
if (!_hotkeyRegistry.TryGetValue(actionId, out var registrations))
// 添加注册信息
ref int count = ref _registrationCounts[actionIndex];
if (count >= MAX_REGISTRATIONS_PER_ACTION)
{
registrations = new List<HotkeyRegistration>(4);
_hotkeyRegistry[actionId] = registrations;
Debug.LogWarning($"Max registrations reached for action: {actionId}");
return;
}
registrations.Add(registration);
_registrationPool[actionIndex][count] = new HotkeyRegistration(button, pressType);
count++;
// 记录button映射
int buttonIndex = FindOrCreateButtonIndex(button);
_buttonToActionIndex[buttonIndex] = actionIndex;
_buttonRegistrations[button] = actionId;
if (!_sharedHandlers.ContainsKey(actionId))
// 优化8: 只在第一次注册时创建handler
if (count == 1)
{
Action<InputAction.CallbackContext> handler = ctx => OnHotkeyTriggered(actionId);
_sharedHandlers[actionId] = (handler, action);
Action<InputAction.CallbackContext> handler = GetOrCreateHandler(actionIndex);
_handlers[actionIndex] = new HandlerInfo(handler, action);
_actionRefs[actionIndex] = action;
switch (pressType)
{
@ -93,71 +164,235 @@ namespace UnityEngine.UI
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnregisterHotkey(IHotkeyTrigger button)
{
if (button == null || !_buttonRegistrations.TryGetValue(button, out var actionId))
if (button == null)
return;
int buttonIndex = FindButtonIndex(button);
if (buttonIndex < 0)
return;
if (_hotkeyRegistry.TryGetValue(actionId, out var registrations))
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--)
{
HotkeyRegistration hotkeyInfo;
for (int i = registrations.Count - 1; i >= 0; i--)
if (ReferenceEquals(registrations[i].button, button))
{
if (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)
{
hotkeyInfo = registrations[i];
registrations.RemoveAt(i);
ref HandlerInfo handlerInfo = ref _handlers[actionIndex];
InputActionReference actionRef = _actionRefs[actionIndex];
if (registrations.Count == 0 && _sharedHandlers.TryGetValue(actionId, out var handlerInfo))
if (actionRef != null && actionRef.action != null)
{
var (handler, actionRef) = handlerInfo;
if (actionRef != null && actionRef.action != null)
switch (pressType)
{
actionRef.action.Disable();
_sharedHandlers.Remove(actionId);
_hotkeyRegistry.Remove(actionId);
switch (hotkeyInfo.pressType)
{
case EHotkeyPressType.Started:
actionRef.action.started -= handler;
break;
case EHotkeyPressType.Performed:
actionRef.action.performed -= handler;
break;
}
case EHotkeyPressType.Started:
actionRef.action.started -= handlerInfo.handler;
break;
case EHotkeyPressType.Performed:
actionRef.action.performed -= handlerInfo.handler;
break;
}
actionRef.action.Disable();
}
break;
handlerInfo = default;
_actionRefs[actionIndex] = null;
_actionIds[actionIndex] = null;
}
break;
}
}
_buttonRegistrations.Remove(button);
// 清理button映射
_buttons[buttonIndex] = null;
_buttonToActionIndex[buttonIndex] = -1;
}
private static void OnHotkeyTriggered(string actionId)
// 优化10: 使用缓存的委托,避免闭包分配
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Action<InputAction.CallbackContext> GetOrCreateHandler(int actionIndex)
{
if (_hotkeyRegistry.TryGetValue(actionId, out var registrations) && registrations.Count > 0)
if (actionIndex < _cachedHandlers.Length && _cachedHandlers[actionIndex] != null)
{
var registration = registrations[^1];
registration.button.HotkeyActionTrigger();
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 == null) return;
if (button.HotkeyAction != null)
if (button?.HotkeyAction != null)
{
UXHotkeyRegisterManager.RegisterHotkey(
button,
@ -167,10 +402,36 @@ public static class UXHotkeyHotkeyExtension
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnBindHotKey(this IHotkeyTrigger button)
{
if (button == null || button.HotkeyAction == null) return;
UXHotkeyRegisterManager.UnregisterHotkey(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