Files
zeling_v2/Assets/_Game/Scripts/UI/UISelectionRestorer.cs
2026-06-06 09:00:11 +08:00

55 lines
2.2 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.
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;
}
}
}