using System;
using UnityEngine;
using UnityEngine.InputSystem;
using BaseGames.Core;
using BaseGames.Core.Events;
using BaseGames.Input;
namespace BaseGames.UI
{
///
/// 按键图标服务实现。
///
/// 职责:
/// 1. 侦听 InputDeviceTypeEventChannelSO,更新当前图标集
/// 2. 侦听 InputSystem.onActionChange(BoundControlsChanged),改键后刷新
/// 3. 提供 GetActionIcon / GetActionEffectivePath,供 UI 查询
/// 4. 在 Awake 注册自身到 ServiceLocator
///
/// 布置方式:与 InputDeviceDetector 同挂在 UIRoot 上;每场景只需一个实例。
///
public sealed class InputIconService : MonoBehaviour, IInputIconService
{
[Header("Input")]
[SerializeField] private InputReaderSO _inputReader;
[Header("Icon Sets — 按设备类型配置")]
[SerializeField] private InputDeviceIconSetSO _kbMouseSet;
[SerializeField] private InputDeviceIconSetSO _xboxSet;
[SerializeField] private InputDeviceIconSetSO _playStationSet;
[SerializeField] private InputDeviceIconSetSO _switchSet;
[Header("Event Channels")]
[SerializeField] private InputDeviceTypeEventChannelSO _onDeviceChanged;
// ── IInputIconService ─────────────────────────────────────────────────
public InputDeviceType CurrentDevice { get; private set; } = InputDeviceType.KeyboardMouse;
public event Action OnIconSetChanged;
private InputDeviceIconSetSO _activeSet;
private readonly CompositeDisposable _subs = new();
// ── Lifecycle ─────────────────────────────────────────────────────────
private void Awake()
{
ServiceLocator.RegisterIfAbsent(this);
_activeSet = _kbMouseSet;
}
private void OnEnable()
{
_onDeviceChanged?.Subscribe(HandleDeviceChanged).AddTo(_subs);
// 改键后 InputSystem 会广播 BoundControlsChanged
InputSystem.onActionChange += HandleActionChange;
}
private void OnDisable()
{
_subs.Clear();
InputSystem.onActionChange -= HandleActionChange;
}
private void OnDestroy()
{
ServiceLocator.Unregister(this);
}
// ── Event Handlers ────────────────────────────────────────────────────
private void HandleDeviceChanged(InputDeviceType deviceType)
{
CurrentDevice = deviceType;
_activeSet = deviceType switch
{
InputDeviceType.XboxController => _xboxSet ?? _kbMouseSet,
InputDeviceType.PlayStationController => _playStationSet ?? _kbMouseSet,
InputDeviceType.SwitchController => _switchSet ?? _kbMouseSet,
_ => _kbMouseSet,
};
OnIconSetChanged?.Invoke();
}
private void HandleActionChange(object obj, InputActionChange change)
{
if (change == InputActionChange.BoundControlsChanged)
OnIconSetChanged?.Invoke();
}
// ── IInputIconService impl ────────────────────────────────────────────
public Sprite GetActionIcon(string actionName)
{
var path = GetActionEffectivePath(actionName);
if (path == null || _activeSet == null) return null;
return _activeSet.GetIcon(path);
}
public string GetActionEffectivePath(string actionName)
{
if (_inputReader == null) return null;
var action = _inputReader.FindAction(actionName);
if (action == null) return null;
// 通过 binding.groups 过滤,只返回匹配当前设备控制方案的绑定路径
string schemeFilter = GetControlSchemeForDevice(CurrentDevice);
foreach (var binding in action.bindings)
{
// 跳过复合绑定的父条目(无实际路径)
if (binding.isComposite) continue;
// 若 binding.groups 不含当前方案,则跳过(允许空 groups 的绑定匹配所有设备)
if (!string.IsNullOrEmpty(schemeFilter)
&& !string.IsNullOrEmpty(binding.groups)
&& !binding.groups.Contains(schemeFilter, StringComparison.OrdinalIgnoreCase))
continue;
// effectivePath 已自动合并 overridePath(改键后的路径)
var path = binding.effectivePath;
if (!string.IsNullOrEmpty(path)) return path;
}
return null;
}
// ── Helpers ───────────────────────────────────────────────────────────
/// 将设备类型映射到 InputActionAsset 中配置的控制方案名称。
private static string GetControlSchemeForDevice(InputDeviceType device) => device switch
{
InputDeviceType.KeyboardMouse => "Keyboard&Mouse",
_ => "Gamepad", // Xbox / PS / Switch 共用 Gamepad 方案
};
}
}