- Created a new markdown file detailing the configuration of InputDeviceIconSetSO. - Included sections on system architecture, field explanations, image specifications, and complete workflow from setup to runtime. - Documented the automatic device recognition logic and provided troubleshooting for common issues. - Added references to relevant files and scripts for easier navigation.
135 lines
5.7 KiB
C#
135 lines
5.7 KiB
C#
using System;
|
||
using UnityEngine;
|
||
using UnityEngine.InputSystem;
|
||
using BaseGames.Core;
|
||
using BaseGames.Core.Events;
|
||
using BaseGames.Input;
|
||
|
||
namespace BaseGames.UI
|
||
{
|
||
/// <summary>
|
||
/// 按键图标服务实现。
|
||
///
|
||
/// 职责:
|
||
/// 1. 侦听 InputDeviceTypeEventChannelSO,更新当前图标集
|
||
/// 2. 侦听 InputSystem.onActionChange(BoundControlsChanged),改键后刷新
|
||
/// 3. 提供 GetActionIcon / GetActionEffectivePath,供 UI 查询
|
||
/// 4. 在 Awake 注册自身到 ServiceLocator
|
||
///
|
||
/// 布置方式:与 InputDeviceDetector 同挂在 UIRoot 上;每场景只需一个实例。
|
||
/// </summary>
|
||
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<IInputIconService>(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<IInputIconService>(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 ───────────────────────────────────────────────────────────
|
||
|
||
/// <summary>将设备类型映射到 InputActionAsset 中配置的控制方案名称。</summary>
|
||
private static string GetControlSchemeForDevice(InputDeviceType device) => device switch
|
||
{
|
||
InputDeviceType.KeyboardMouse => "Keyboard&Mouse",
|
||
_ => "Gamepad", // Xbox / PS / Switch 共用 Gamepad 方案
|
||
};
|
||
}
|
||
}
|