UI系统组件

This commit is contained in:
2026-06-06 09:00:11 +08:00
parent fe4fd60083
commit d794b83ebe
107 changed files with 25690 additions and 476 deletions

View File

@@ -0,0 +1,54 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.UI;
namespace BaseGames.UI
{
/// <summary>
/// 多设备 UI 焦点守护:挂在持有 EventSystem 的对象上。
///
/// 解决"鼠标点击空白处 → 当前选中被清空 → 切换到键盘/手柄时无选中项可导航"的问题:
/// 记录最近一个有效选中项;当选中丢失且玩家按下导航/确认键(键盘方向键 / 手柄摇杆·十字键 / Submit
/// 自动把焦点恢复到上一个仍可交互的选中项。仅在"导航意图"出现时恢复,不与鼠标悬停/点击冲突。
///
/// 设备无关:依赖 <see cref="InputSystemUIInputModule"/> 的 move/submit Action
/// 因此键盘、Xbox、Switch、PlayStation 手柄(均归一为 Gamepad 布局)统一生效。
/// </summary>
[DefaultExecutionOrder(100)]
public class UISelectionRestorer : MonoBehaviour
{
private GameObject _lastSelected;
private void Update()
{
var es = EventSystem.current;
if (es == null) return;
var current = es.currentSelectedGameObject;
if (current != null && current.activeInHierarchy)
{
_lastSelected = current; // 记录最近的有效选中
return;
}
// 选中已丢失:仅当出现导航/确认意图时才恢复,避免抢占鼠标操作
if (!NavigationIntentThisFrame(es)) return;
if (_lastSelected == null || !_lastSelected.activeInHierarchy) return;
var sel = _lastSelected.GetComponent<Selectable>();
if (sel == null || !sel.IsInteractable()) return;
es.SetSelectedGameObject(_lastSelected);
}
private static bool NavigationIntentThisFrame(EventSystem es)
{
if (es.currentInputModule is not InputSystemUIInputModule m) return false;
if (m.move != null && m.move.action != null && m.move.action.WasPerformedThisFrame()) return true;
if (m.submit != null && m.submit.action != null && m.submit.action.WasPerformedThisFrame()) return true;
return false;
}
}
}