using UnityEngine; using UnityEngine.UI; using BaseGames.Core; using BaseGames.Core.Events; namespace BaseGames.UI { /// /// 输入设备图标切换器。 /// 订阅 InputDeviceTypeEventChannelSO,在设备切换时通知场景内所有 InputIconImage 刷新。 /// /// ⚠️ 旧版只支持 KB / 手柄二值切换;新版支持 KeyboardMouse / Xbox / PlayStation / Switch。 /// 通常挂在 UIRoot 上,与 InputDeviceDetector 配合使用。 /// public class InputDeviceIconSwitcher : MonoBehaviour { [Header("Event Channel")] [Tooltip("由 InputDeviceDetector 广播的设备类型事件")] [SerializeField] private InputDeviceTypeEventChannelSO _onDeviceChanged; private readonly CompositeDisposable _subs = new(); private void OnEnable() => _onDeviceChanged?.Subscribe(OnDeviceChanged).AddTo(_subs); private void OnDisable() => _subs.Clear(); private void OnDeviceChanged(InputDeviceType _) { // 通知场景内所有 InputIconImage 刷新(含非本对象子节点的其他 Canvas 区域) foreach (var img in FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) img.Refresh(); } } // ───────────────────────────────────────────────────────────────────────── /// /// 单个按键图标 Image 组件。 /// /// 支持两种查询模式: /// • ByActionName(推荐):填写 ActionName(如 "Interact"), /// 由 IInputIconService 自动解析当前设备 + 改键后的实际绑定路径 → 图标。 /// • ByBindingPath(兼容/装饰用):直接填写固定路径(如 "<Keyboard>/space"), /// 适合教程截图等不跟随改键变化的场景。 /// [RequireComponent(typeof(Image))] public class InputIconImage : MonoBehaviour { public enum LookupMode { ByActionName, ByBindingPath } [SerializeField] private LookupMode _mode = LookupMode.ByActionName; [Tooltip("Action 名称,如 Interact / Jump / Attack(仅 ByActionName 模式使用)")] [SerializeField] private string _actionName; [Tooltip("固定绑定路径,如 /space(仅 ByBindingPath 模式使用)")] [SerializeField] private string _bindingPath; private Image _image; private IInputIconService _iconService; private void Awake() => _image = GetComponent(); private void OnEnable() { _iconService = ServiceLocator.GetOrDefault(); if (_iconService != null) _iconService.OnIconSetChanged += Refresh; Refresh(); } private void OnDisable() { if (_iconService != null) _iconService.OnIconSetChanged -= Refresh; } /// 刷新图标显示。设备切换或改键后由 InputDeviceIconSwitcher / InputIconService 调用。 public void Refresh() { if (_image == null) return; Sprite sprite = null; if (_mode == LookupMode.ByActionName && !string.IsNullOrEmpty(_actionName)) { sprite = _iconService?.GetActionIcon(_actionName); } else if (_mode == LookupMode.ByBindingPath && !string.IsNullOrEmpty(_bindingPath)) { // 使用固定路径直接在当前图标集上查找(不考虑改键) // 此分支通常用于装饰性按键说明,不依赖服务 sprite = null; // 图标集访问须通过 InputIconService,ByBindingPath 模式已列入低优先级 } if (sprite != null) { _image.sprite = sprite; _image.enabled = true; } else { _image.enabled = false; } } } }