Update UXHotkeyRegisterManager.cs
This commit is contained in:
parent
0e5ea2c357
commit
fc993a1dbe
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user