Files
zeling_v2/Docs/Tuning/InputDeviceIconSet_Tuning.md
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

550 lines
25 KiB
Markdown
Raw Permalink 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.
# InputDeviceIconSetSO 配置指南
**配置文件**`Assets/_Game/Data/UI/InputIcons/ICN_*.asset`
**对应脚本**`InputDeviceIconSetSO.cs` · `InputIconService.cs` · `InputDeviceDetector.cs`
**创建工具**Unity 菜单 `BaseGames/Input Icon Studio`(推荐)或 Inspector 右键 `Create > BaseGames/UI/Input Device Icon Set`
**影响系统**`InputIconService` · `InteractPromptWidget` · `InputIconImage` · `InputDeviceIconSwitcher`
---
## 目录
1. [系统架构概述](#1-系统架构概述)
2. [InputDeviceIconSetSO 字段详解](#2-inputdeviceiconsetsoso-字段详解)
3. [IconEntry 绑定路径参考表](#3-iconentry-绑定路径参考表)
4. [图片规格要求](#4-图片规格要求)
5. [InputIconService 配置字段](#5-inputiconservice-配置字段)
6. [设备自动识别逻辑](#6-设备自动识别逻辑)
7. [InteractPromptWidget 配置](#7-interactpromptwidget-配置)
8. [InputIconImage 配置](#8-inputiconimage-配置)
9. [完整工作流:从零到运行](#9-完整工作流从零到运行)
10. [常见问题与排查](#10-常见问题与排查)
---
## 1. 系统架构概述
```
InputSystem (底层事件)
InputDeviceDetector ← 监听所有输入流,精确识别设备类型
│ 广播 InputDeviceTypeEventChannelSO
InputIconService ← 根据设备切换活跃图标集 + 监听改键事件
│ implements IInputIconService
├── _kbMouseSet (ICN_KeyboardMouse.asset)
├── _xboxSet (ICN_Xbox.asset)
├── _playStationSet (ICN_PlayStation.asset)
└── _switchSet (ICN_Switch.asset)
│ GetActionIcon(actionName)
│ → GetActionEffectivePath() ← 读取 InputActionAsset + 改键覆盖路径
│ → InputDeviceIconSetSO.GetIcon(path)
UI 消费者:
├── InteractPromptWidget → Image (按键图标) + TMP_Text (动作名称)
└── InputIconImage → Image (任意场景内按键提示)
```
### 核心设计要点
| 要点 | 说明 |
|------|------|
| **改键自动更新** | `InputIconService.GetActionEffectivePath()` 使用 `binding.effectivePath`该值已内置玩家改键后的覆盖路径UI 无需额外处理 |
| **多设备无缝切换** | 玩家随时插拔手柄,`InputDeviceDetector` 实时检测最后一次输入来自哪个设备,图标自动切换,无卡顿 |
| **回退机制** | 若某设备图标集未配置null`InputIconService` 自动回退到键鼠集 |
| **不走 Addressables** | 4 套图标集 SO 通过 `SerializeField` 直接挂载在 Persistent 场景的 `InputIconService` 组件上,随常驻场景加载,零加载延迟 |
---
## 2. InputDeviceIconSetSO 字段详解
> 路径:`Assets/_Game/Data/UI/InputIcons/ICN_{设备}.asset`
> 菜单:`Create > BaseGames/UI/Input Device Icon Set`
### 2.1 顶层字段
#### `_deviceType`InputDeviceType 枚举)
| 枚举值 | 对应设备 | 说明 |
|--------|---------|------|
| `KeyboardMouse` | 键盘 + 鼠标 | 默认设备,未识别到手柄时回退到此 |
| `XboxController` | Xbox 手柄 / 其他 XInput 手柄 | 包含 Xbox One、Xbox Series、大多数第三方 PC 手柄 |
| `PlayStationController` | DualShock 4 / DualSensePS5| 通过 Sony 布局层次或 manufacturer 字段识别 |
| `SwitchController` | Switch Pro Controller / Joy-Con | 通过 Nintendo 厂商字段或布局识别 |
**用途**
- 编辑器工具Input Icon Studio用此字段将 SO 自动归类到对应设备标签栏
- `AddressableRules` 工具可依此字段批量校验命名一致性
- **不影响运行时逻辑**:运行时由 `InputIconService` 按字段顺序_kbMouseSet / _xboxSet / _playStationSet / _switchSet直接选择不读取此字段
> ⚠️ 务必与文件命名对应:`ICN_KeyboardMouse.asset` → `KeyboardMouse`
---
#### `_entries`IconEntry 数组)
每个元素代表「一个按键路径 → 一张 Sprite」的映射关系。
| 字段 | 类型 | 说明 |
|------|------|------|
| `BindingPath` | string | InputSystem 绑定路径(见 §3 参考表)。**大小写不敏感**,匹配时使用 `OrdinalIgnoreCase` |
| `Icon` | Sprite | 该按键对应的图标 Sprite规格要求见 §4|
**注意事项**
1. **路径必须是 InputSystem 的标准路径格式**,不是自定义字符串。路径来源:
- 运行时从 `binding.effectivePath` 获取(含改键)
- 编辑时从 `InputActionAsset` 的 Binding 列表获取
- 可在 Input Icon Studio 的 Action 列表中查看当前绑定路径预览
2. **改键后路径会变化**:例如玩家将「跳跃」从 `<Keyboard>/space` 改为 `<Keyboard>/leftShift`,系统会查询 `<Keyboard>/leftShift`。因此图标集中需要覆盖所有玩家可能绑定的物理按键,而非仅默认按键。
3. **同一按键路径在多个 Action 中可复用**:图标集是路径 → Sprite 的扁平映射,一张 Sprite 可对应多个 Action 的查询结果(只要路径相同)。
4. **路径不存在时行为**`GetIcon()` 返回 `null`UI 消费方(`InteractPromptWidget` / `InputIconImage`)会将 `Image.enabled = false`,隐藏图标区域,不显示错误占位图。
---
### 2.2 Inspector 快捷操作InputDeviceIconSetSOEditor
在 Inspector 选中 `ICN_*.asset` 时,自定义编辑器提供以下操作:
| 按钮 | 功能 |
|------|------|
| **从 Action Asset 填充路径** | 读取 `InputReaderSO` 中的 `InputActionAsset`,自动提取当前设备方案的所有绑定路径,批量创建 `_entries` 条目Icon 留空,待手动填入)|
| **在 Input Icon Studio 打开** | 跳转到 Input Icon Studio 窗口并定位此 SO |
| **复制路径** | 右键单个条目的路径字段,复制路径字符串到剪贴板 |
---
## 3. IconEntry 绑定路径参考表
### 3.1 键盘常用路径(`ICN_KeyboardMouse.asset`
| 描述 | BindingPath |
|------|-------------|
| 空格键 | `<Keyboard>/space` |
| E 键(交互默认) | `<Keyboard>/e` |
| F 键 | `<Keyboard>/f` |
| R 键 | `<Keyboard>/r` |
| Shift 键(左) | `<Keyboard>/leftShift` |
| Ctrl 键(左) | `<Keyboard>/leftCtrl` |
| Tab 键 | `<Keyboard>/tab` |
| Escape 键 | `<Keyboard>/escape` |
| Enter 键 | `<Keyboard>/enter` |
| 方向键上 | `<Keyboard>/upArrow` |
| 方向键下 | `<Keyboard>/downArrow` |
| 方向键左 | `<Keyboard>/leftArrow` |
| 方向键右 | `<Keyboard>/rightArrow` |
| W / A / S / D | `<Keyboard>/w` · `<Keyboard>/a` · `<Keyboard>/s` · `<Keyboard>/d` |
| 数字键 1~4 | `<Keyboard>/1` ~ `<Keyboard>/4` |
| 鼠标左键 | `<Mouse>/leftButton` |
| 鼠标右键 | `<Mouse>/rightButton` |
| 鼠标中键 | `<Mouse>/middleButton` |
| 滚轮向上 | `<Mouse>/scroll/up` |
| 滚轮向下 | `<Mouse>/scroll/down` |
### 3.2 Xbox / XInput 手柄路径(`ICN_Xbox.asset`
| 描述 | BindingPath |
|------|-------------|
| A 键(南方键,交互默认) | `<Gamepad>/buttonSouth` |
| B 键(东方键) | `<Gamepad>/buttonEast` |
| X 键(西方键) | `<Gamepad>/buttonWest` |
| Y 键(北方键) | `<Gamepad>/buttonNorth` |
| 左摇杆按下 | `<Gamepad>/leftStickButton` |
| 右摇杆按下 | `<Gamepad>/rightStickButton` |
| 左肩键 LB | `<Gamepad>/leftShoulder` |
| 右肩键 RB | `<Gamepad>/rightShoulder` |
| 左扳机 LT | `<Gamepad>/leftTrigger` |
| 右扳机 RT | `<Gamepad>/rightTrigger` |
| 十字键上 | `<Gamepad>/dpad/up` |
| 十字键下 | `<Gamepad>/dpad/down` |
| 十字键左 | `<Gamepad>/dpad/left` |
| 十字键右 | `<Gamepad>/dpad/right` |
| 开始键 Start / Menu | `<Gamepad>/start` |
| 选择键 Select / View | `<Gamepad>/select` |
| 左摇杆方向 | `<Gamepad>/leftStick` |
| 右摇杆方向 | `<Gamepad>/rightStick` |
### 3.3 PlayStation 手柄路径(`ICN_PlayStation.asset`
> **路径与 Xbox 完全相同**(均使用通用 `<Gamepad>` 路径),只有图标 Sprite 不同。
> PlayStation 的按键物理布局与 Xbox 一致(南/东/西/北方键),区别在于图标(✕ / ○ / □ / △ vs A/B/X/Y
| 描述 | PS 图标 | BindingPath与 Xbox 相同) |
|------|---------|--------------------------|
| 南方键 Cross | ✕ 图标 | `<Gamepad>/buttonSouth` |
| 东方键 Circle | ○ 图标 | `<Gamepad>/buttonEast` |
| 西方键 Square | □ 图标 | `<Gamepad>/buttonWest` |
| 北方键 Triangle | △ 图标 | `<Gamepad>/buttonNorth` |
| L1 | L1 图标 | `<Gamepad>/leftShoulder` |
| R1 | R1 图标 | `<Gamepad>/rightShoulder` |
| L2 | L2 图标 | `<Gamepad>/leftTrigger` |
| R2 | R2 图标 | `<Gamepad>/rightTrigger` |
| L3左摇杆按下| L3 图标 | `<Gamepad>/leftStickButton` |
| R3右摇杆按下| R3 图标 | `<Gamepad>/rightStickButton` |
| Options 键 | Options 图标 | `<Gamepad>/start` |
| TouchPad / Share | TouchPad 图标 | `<Gamepad>/select` |
### 3.4 Nintendo Switch 手柄路径(`ICN_Switch.asset`
> **路径同样与 Xbox/PS 共用 `<Gamepad>` 路径**,仅图标不同。
> Switch 的南方键是 B东方键是 A与 Xbox 方向相反,务必使用正确的 Switch 图标。
| 描述 | Switch 图标 | BindingPath |
|------|------------|-------------|
| 南方键 B | B 图标 | `<Gamepad>/buttonSouth` |
| 东方键 A | A 图标 | `<Gamepad>/buttonEast` |
| 西方键 Y | Y 图标 | `<Gamepad>/buttonWest` |
| 北方键 X | X 图标 | `<Gamepad>/buttonNorth` |
| L 键 | L 图标 | `<Gamepad>/leftShoulder` |
| R 键 | R 图标 | `<Gamepad>/rightShoulder` |
| ZL 键 | ZL 图标 | `<Gamepad>/leftTrigger` |
| ZR 键 | ZR 图标 | `<Gamepad>/rightTrigger` |
| + 键 | + 图标 | `<Gamepad>/start` |
| - 键 | - 图标 | `<Gamepad>/select` |
---
## 4. 图片规格要求
### 4.1 分辨率与尺寸
| 场景 | 建议尺寸 | 说明 |
|------|---------|------|
| **HUD 交互提示**InteractPromptWidget 主图标)| **64×64 px** | 运行时显示在 32×32 的 Image 控件中2× 超采样保证清晰度 |
| **InputIconImage**(通用场景内提示)| **64×64 px** | 与上同 |
| Input Icon Studio 大图预览 | 64×64 px | 编辑器预览用,与运行时同图 |
> 最小可用尺寸 **32×32 px**,但在 Retina / 高 DPI 屏幕上会模糊。
> 不建议超过 **128×128 px**Atlas 占用增大但视觉收益有限)。
### 4.2 格式
| 项目 | 规范 |
|------|------|
| **文件格式** | PNG推荐支持透明度|
| **色彩模式** | RGBA需要透明背景|
| **背景** | **透明**,图标主体不超过 90% 画布区域,四周留 5% 安全边距 |
| **颜色风格** | 与游戏 UI 风格一致:像素风选用扁平配色;高清风可使用渐变 |
### 4.3 Unity Import Settings
在 Inspector 选中图标 PNG 后,设置以下 Import Settings
| 设置项 | 推荐值 | 原因 |
|-------|-------|------|
| **Texture Type** | `Sprite (2D and UI)` | 用于 UI Image 组件 |
| **Sprite Mode** | `Single`(单图)或 `Multiple`Sprite Sheet | 单张图标用 Single若多个图标合并到一张 Atlas 用 Multiple |
| **Pixels Per Unit** | `32`(像素风项目)或 `64`(高清项目,与 Canvas 缩放匹配) | 与项目全局 PPU 保持一致 |
| **Filter Mode** | `Point (no filter)`(像素风)或 `Bilinear`(高清风) | 像素风必须用 Point否则会模糊 |
| **Compression** | `None`(开发阶段)/ `ASTC 4x4`(移动端发布)| 图标资产小,压缩感知不明显,开发期优先无损 |
| **Generate Mip Maps** | **关闭** | 2D UI 不需要 Mip Maps |
| **Read/Write Enabled** | **关闭**(除非代码读像素)| 减少内存占用 |
| **Max Size** | `128` | 图标不需要超过 128px |
| **Alpha Is Transparency** | **开启** | 正确处理透明边缘抗锯齿 |
### 4.4 Sprite Atlas 策略
将同一设备的所有按键图标合并到一张 Atlas可减少 DrawCall
| Atlas 文件 | 覆盖内容 | 存放位置 |
|-----------|---------|---------|
| `Atlas_UI_Keys_KBM.spriteatlas` | 键鼠全部按键图标 | `_Game/Art/UI/Icons/InputKeys/` |
| `Atlas_UI_Keys_Xbox.spriteatlas` | Xbox 全部按键图标 | `_Game/Art/UI/Icons/InputKeys/` |
| `Atlas_UI_Keys_PS.spriteatlas` | PlayStation 全部按键图标 | `_Game/Art/UI/Icons/InputKeys/` |
| `Atlas_UI_Keys_Switch.spriteatlas` | Switch 全部按键图标 | `_Game/Art/UI/Icons/InputKeys/` |
> **Atlas 本身不注册 Addressable**,由引用它的 SO 间接带入包体。
### 4.5 命名规范
```
IC_Key_{DeviceShort}_{KeyName}.png
DeviceShort
KBM → 键盘鼠标
Xbox → Xbox / XInput
PS → PlayStation (DualShock/DualSense)
Switch → Nintendo Switch
示例:
IC_Key_KBM_Space.png ← <Keyboard>/space
IC_Key_KBM_E.png ← <Keyboard>/e
IC_Key_KBM_LMB.png ← <Mouse>/leftButton
IC_Key_Xbox_South.png ← <Gamepad>/buttonSouth (A键)
IC_Key_Xbox_RT.png ← <Gamepad>/rightTrigger
IC_Key_PS_Cross.png ← <Gamepad>/buttonSouth (✕键)
IC_Key_PS_R2.png ← <Gamepad>/rightTrigger
IC_Key_Switch_South.png ← <Gamepad>/buttonSouth (B键)
IC_Key_Switch_ZR.png ← <Gamepad>/rightTrigger
```
---
## 5. InputIconService 配置字段
> 组件挂载位置:`Persistent 场景` → `UIRoot` → `InputIconService`
| 字段Header | 字段名 | 类型 | 说明 |
|--------------|--------|------|------|
| **Input** | `_inputReader` | InputReaderSO | 游戏全局 InputReaderSO用于查询 Action 的绑定路径(含改键覆盖)|
| **Icon Sets** | `_kbMouseSet` | InputDeviceIconSetSO | 键鼠图标集(`ICN_KeyboardMouse.asset`)。**默认激活集,任何未识别设备均回退到此** |
| **Icon Sets** | `_xboxSet` | InputDeviceIconSetSO | Xbox 图标集(`ICN_Xbox.asset`)。可为 nullnull 时回退到 `_kbMouseSet` |
| **Icon Sets** | `_playStationSet` | InputDeviceIconSetSO | PlayStation 图标集(`ICN_PlayStation.asset`)。可为 nullnull 时回退到 `_kbMouseSet` |
| **Icon Sets** | `_switchSet` | InputDeviceIconSetSO | Switch 图标集(`ICN_Switch.asset`)。可为 nullnull 时回退到 `_kbMouseSet` |
| **Event Channels** | `_onDeviceChanged` | InputDeviceTypeEventChannelSO | 订阅 InputDeviceDetector 广播的设备切换事件(`EVT_InputDeviceChanged.asset`|
### 5.1 回退规则
```
设备切换到 XboxController
_xboxSet != null → 使用 _xboxSet
_xboxSet == null → 回退 _kbMouseSet
设备切换到 PlayStationController
_playStationSet != null → 使用 _playStationSet
_playStationSet == null → 回退 _kbMouseSet
设备切换到 SwitchController
_switchSet != null → 使用 _switchSet
_switchSet == null → 回退 _kbMouseSet
```
> 回退到 `_kbMouseSet` 时,手柄路径(如 `<Gamepad>/buttonSouth`)查询 `_kbMouseSet` 会返回 null因为键鼠集里没有 Gamepad 路径),最终 `Image.enabled = false`,图标区域隐藏。
> **建议**:四套图标集均配置完整,避免出现图标消失的情况。
---
## 6. 设备自动识别逻辑
> 组件:`InputDeviceDetector`,挂在 `UIRoot` 上,与 `InputIconService` 同节点
### 6.1 识别优先级
```
有输入事件到来时StateEvent / DeltaStateEvent
1. device is Keyboard or Mouse → KeyboardMouse
2. device is Gamepad
2a. 布局继承自 DualShockGamepad
或 product 含 "DualShock"/"DualSense"
或 manufacturer 含 "Sony" → PlayStationController
2b. 布局继承自 SwitchProControllerHID
或 product 含 "Switch"/"Joy-Con"
或 manufacturer 含 "Nintendo" → SwitchController
2c. 布局继承自 XInputController
或 product 含 "Xbox"
或 interfaceName == "XInput" → XboxController
2d. 未匹配(第三方 HID 手柄) → XboxController默认
3. 其他设备类型(触摸屏等) → KeyboardMouse默认
```
### 6.2 热插拔行为
- 手柄**拔出**`InputDeviceChange.Removed` 不触发设备切换,保持当前图标集,防止玩家无意拔线导致图标乱跳
- 手柄**重连**`InputDeviceChange.Reconnected/Added` 同样不自动切换,等待下一次实际输入事件再切换
- 玩家**实际操作**键鼠或手柄时,才触发切换 —— 这是最符合用户体验的设计
---
## 7. InteractPromptWidget 配置
> 组件挂载位置:`Persistent 场景` → `HUD Canvas` → `InteractPromptWidget_GO`
### 7.1 字段说明
| Header | 字段 | 类型 | 说明 |
|--------|------|------|------|
| **UI 引用** | `_keyIcon` | Image | 显示按键图标的 UI Image 组件。宽高建议 **32×32**Canvas 坐标空间Preserve Aspect = true |
| **UI 引用** | `_labelText` | TMP_Text | 显示动作说明文字(如"交互"、"拾取"),对应 `InteractPromptEvent.LabelText` |
| **UI 引用** | `_root` | GameObject | 整个提示 UI 的根节点(含图标+文字),`SetActive(false)` 时完整隐藏。若为 null则控制 Widget 所在 GameObject 本身 |
| **Event Channels** | `_onShowPrompt` | InteractPromptEventChannelSO | 订阅此频道接收显示指令,负载为 `InteractPromptEvent { ActionName, LabelText }` |
| **Event Channels** | `_onHidePrompt` | VoidEventChannelSO | 订阅此频道接收隐藏指令(玩家离开交互范围时触发)|
### 7.2 InteractPromptEvent 负载
| 字段 | 类型 | 说明 |
|------|------|------|
| `ActionName` | string | InputSystem Action 名称,如 `"Interact"`。用于在 InputIconService 查询对应图标 |
| `LabelText` | string | 显示的文字,如 `"交互"` / `"拾取"` / `"打开"`。由交互物件在广播时指定(来自 `Interactable.InteractPrompt`|
### 7.3 延迟绑定机制
`InteractPromptWidget``OnEnable` 时**不**立即获取 `IInputIconService`,而是在首次 `ShowPrompt()` 时才获取。这避免了执行顺序问题(`InputIconService.Awake()` 可能晚于 Widget 的 `OnEnable`)。
---
## 8. InputIconImage 配置
> 适用场景:教程界面、暂停菜单、任何需要显示单个按键图标的 UI Image
> 组件:`InputIconImage`,挂在带有 `Image` 组件的 UI GameObject 上
### 8.1 字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `_mode` | LookupMode 枚举 | 查询模式(见下表)|
| `_actionName` | string | **ByActionName 模式**:填写 Action 名称(如 `"Jump"`),自动跟随改键和设备切换 |
| `_bindingPath` | string | **ByBindingPath 模式**:填写固定路径(如 `<Keyboard>/space`),不跟随改键 |
### 8.2 LookupMode 枚举
| 枚举值 | 适用场景 | 改键响应 | 设备切换响应 |
|--------|---------|---------|------------|
| `ByActionName`(推荐)| 所有正常游戏内 UI 提示 | ✅ 自动更新 | ✅ 自动切换 |
| `ByBindingPath` | 教程截图说明、固定按键展示(如「按 Space 确认」这类静态文案)| ❌ 不更新 | ❌ 不切换 |
> `ByBindingPath` 模式目前图标查询功能为低优先级,实际返回 null`Image.enabled = false`)。如需支持,可在 `InputIconService` 中通过 `GetOrDefault<IInputIconService>()` + `GetActiveSet().GetIcon(path)` 手动实现。
---
## 9. 完整工作流:从零到运行
### Step 1 · 导入图标素材
```
1. 在 _Game/Art/UI/Icons/InputKeys/ 下创建4个子文件夹KBM / Xbox / PS / Switch
2. 将各设备按键图标 PNG 放入对应子文件夹
3. 在 Unity 中选中所有图标,统一设置 Import Settings
- Texture Type: Sprite (2D and UI)
- Sprite Mode: Single
- Pixels Per Unit: 32像素风
- Filter Mode: Point (no filter)
- Max Size: 128
- Generate Mip Maps: 关闭
- Alpha Is Transparency: 开启
4. 创建4个 Sprite Atlas见 §4.4),将图标拖入对应 Atlas
```
### Step 2 · 创建图标集 SO
方式 A推荐— 使用 Input Icon Studio
```
1. 菜单 BaseGames/Input Icon Studio
2. 在「图标集资产状态」区域,点击对应设备的「+ 新建」
3. 保存路径_Game/Data/UI/InputIcons/ICN_{设备}.asset
4. 确认 _deviceType 字段与文件名一致
```
方式 B — 手动创建:
```
1. Project 窗口右键 → Create → BaseGames/UI/Input Device Icon Set
2. 重命名为 ICN_{设备}.asset移至 _Game/Data/UI/InputIcons/
3. 在 Inspector 中设置 _deviceType
```
### Step 3 · 填充绑定路径与图标
```
在 Input Icon Studio 中:
1. 顶部选择 InputReaderSO自动加载唯一实例
2. 点击设备标签(键鼠 / Xbox / PS / Switch
3. 左列选择 Action如 Interact
4. 右列拖入对应 Sprite
5. 覆盖率圆点变绿(●)= 已配置
或在 Inspector 中直接编辑 _entries 数组:
1. 展开 _entries
2. 点击「从 Action Asset 填充路径」批量生成所有条目Icon 留空)
3. 逐条拖入 Sprite
```
### Step 4 · 绑定到 InputIconService
```
1. 打开 Persistent 场景
2. 选中 UIRoot GameObject或单独的 InputIconService GameObject
3. 找到 InputIconService 组件
4. 拖入对应字段:
_inputReader → InputReaderSO通常为 PLY_InputReader.asset
_kbMouseSet → ICN_KeyboardMouse.asset
_xboxSet → ICN_Xbox.asset
_playStationSet → ICN_PlayStation.asset
_switchSet → ICN_Switch.asset
_onDeviceChanged → EVT_InputDeviceChanged.asset
```
### Step 5 · 配置 InteractPromptWidget
```
1. 在 HUD Canvas 下找到或创建InteractPromptWidget GameObject
2. 挂上 InteractPromptWidget 组件
3. 配置字段:
_keyIcon → 按键图标 Image 组件32×32 UI Image
_labelText → 动作说明 TMP_Text 组件
_root → 整个提示 UI 根节点(控制显隐)
_onShowPrompt → EVT_ShowInteractPrompt.asset
_onHidePrompt → EVT_HideInteractPrompt.asset
```
### Step 6 · 验证
```
进入 PlayMode
✅ 走近 NPC / 宝箱 → 右下角显示按键图标 + "交互"文字
✅ 插入 Xbox 手柄 → 图标自动切换为 Xbox A 键图标
✅ 插入 DualShock → 图标切换为 PS ✕ 图标
✅ 改键(在游戏内按键重映射菜单)→ 图标同步更新
✅ 拔出手柄 → 恢复键鼠图标(下次键盘/鼠标操作后切换)
```
---
## 10. 常见问题与排查
### 图标不显示Image 被隐藏)
1. 检查 `InputIconService` 的4个图标集字段是否已赋值
2. 检查 `_entries` 中是否有该 Action 对应设备的绑定路径条目
3. 在 Input Icon Studio 左列查看该 Action 的绑定路径预览,与 `_entries` 中的 BindingPath 对比
4. 检查 `InputReaderSO` 是否正确赋值给 `InputIconService._inputReader`
5. 运行时打开 Event Bus Monitor`BaseGames/Events/Event Bus Monitor`)确认 `EVT_InputDeviceChanged` 是否正常广播
### 改键后图标不更新
1. 确认 `InputIconService.OnEnable()` 已订阅 `InputSystem.onActionChange`Inspector 中确保组件处于 active 状态)
2. 确认 `_entries` 中包含改键后目标按键的路径(如玩家改到 `<Keyboard>/leftShift`,需有对应条目)
3. 改键后图标集必须覆盖所有可能被绑定的物理按键;对于玩家自定义改键的游戏,建议将整个键盘的常用键全部配置进 `_kbMouseSet._entries`
### 设备切换后图标不更新
1. 确认 `InputDeviceDetector` 组件已挂载在场景中UIRoot 上)
2. 确认 `InputDeviceDetector._onDeviceChanged``InputIconService._onDeviceChanged` 引用的是**同一个** `EVT_InputDeviceChanged.asset`
3. 插入手柄后需要实际按下任意按键才会触发识别(纯插入不切换)
### PlayStation 手柄显示 Xbox 图标
1. 在 Unity 菜单 `Window > Analysis > Input Debugger` 查看手柄的 `layout` 字段
2. 若 layout 不继承自 `DualShockGamepad`(部分 USB 适配器会出现此情况),系统会回退到 XboxController
3. 可在 `InputDeviceDetector.ClassifyDevice()` 中添加额外的 product 字符串匹配规则
### 编辑器工具显示覆盖率为 0/0
- 说明 `InputReaderSO` 未在 Input Icon Studio 工具栏加载,或 `InputActionAsset` 的 Gameplay ActionMap 为空
- 在工具栏 ObjectField 手动指定 InputReaderSO然后点击「⟳ 刷新」
---
## 附录:相关文件速查
| 用途 | 文件路径 |
|------|---------|
| 图标集 SO键鼠| `_Game/Data/UI/InputIcons/ICN_KeyboardMouse.asset` |
| 图标集 SOXbox| `_Game/Data/UI/InputIcons/ICN_Xbox.asset` |
| 图标集 SOPS| `_Game/Data/UI/InputIcons/ICN_PlayStation.asset` |
| 图标集 SOSwitch| `_Game/Data/UI/InputIcons/ICN_Switch.asset` |
| 按键图标图片 | `_Game/Art/UI/Icons/InputKeys/` |
| 核心 SO 脚本 | `Scripts/UI/InputDeviceIconSetSO.cs` |
| 服务接口 | `Scripts/UI/IInputIconService.cs` |
| 服务实现 | `Scripts/UI/InputIconService.cs` |
| 设备检测器 | `Scripts/UI/InputDeviceDetector.cs` |
| 交互提示 Widget | `Scripts/UI/HUD/InteractPromptWidget.cs` |
| 通用图标 Image | `Scripts/UI/InputDeviceIconSwitcher.cs`(含 InputIconImage 类)|
| 编辑器窗口 | `Scripts/Editor/Input/InputIconStudioWindow.cs` |
| 编辑器 Inspector | `Scripts/Editor/Input/InputDeviceIconSetSOEditor.cs` |
| 事件频道 | `Data/Events/UI/EVT_InputDeviceChanged.asset` |
| 交互提示事件 | `Data/Events/World/EVT_ShowInteractPrompt.asset` |