Files
zeling_v2/Assets/_Game/Scripts/UI/InputIconService.cs
Joywayer e879efaa89 Add InputDeviceIconSetSO configuration guide and related documentation
- 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.
2026-05-23 00:10:23 +08:00

135 lines
5.7 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 System;
using UnityEngine;
using UnityEngine.InputSystem;
using BaseGames.Core;
using BaseGames.Core.Events;
using BaseGames.Input;
namespace BaseGames.UI
{
/// <summary>
/// 按键图标服务实现。
///
/// 职责:
/// 1. 侦听 InputDeviceTypeEventChannelSO更新当前图标集
/// 2. 侦听 InputSystem.onActionChangeBoundControlsChanged改键后刷新
/// 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 方案
};
}
}