# 25 · 输入重映射 UI > **命名空间** `BaseGames.Input` > **所属文档集** [← 返回索引](./README.md) · [总览](./00_Overview.md) > **依赖** `BaseGames.Input`(InputReaderSO)· `BaseGames.UI` · Unity Input System --- ## 目录 1. [系统总览](#1-系统总览) 2. [重映射架构](#2-重映射架构) 3. [RebindPanel — UI 面板](#3-rebindpanel--ui-面板) 4. [RebindActionRow — 单行绑定控件](#4-rebindactionrow--单行绑定控件) 5. [冲突检测](#5-冲突检测) 6. [按键名称显示(Key-to-String)](#6-按键名称显示key-to-string) 7. [持久化存储](#7-持久化存储) 8. [设备切换后自动刷新](#8-设备切换后自动刷新) 9. [完整实现示例](#9-完整实现示例) 10. [编辑器友好设计](#10-编辑器友好设计) --- ## 1. 系统总览 ``` 输入重映射职责: ├─ RebindPanel → 重映射面板(SettingsPanel 的子页签) ├─ RebindActionRow → 单条 Action 的绑定显示 + 重映射按钮 ├─ ConflictDetector → 检测新绑定与现有绑定是否冲突 ├─ KeyDisplayNameResolver → 将 InputControl.path 转换为可读字符串 └─ RebindPersistence → 将覆盖数据保存至 PlayerPrefs(JSON 格式) ``` **设计原则**:重映射仅修改 `InputActionAsset` 的运行时 Override,不改动资产本身;游戏启动时从 `PlayerPrefs` 读取并重新应用 Override。 --- ## 2. 重映射架构 ### 数据流 ``` 玩家点击 [重新绑定] 按钮 │ ▼ InputActionRebindingExtensions .PerformInteractiveRebinding(action, bindingIndex) │ ▼ 等待玩家按键(屏蔽菜单键/退出键) │ ├─ 冲突检测 → 若冲突:弹出警告,允许覆盖或取消 │ ▼ 应用 Override InputBinding.overridePath = newPath │ ▼ 持久化:SaveOverrides() │ ▼ UI 刷新:所有 RebindActionRow 更新显示 ``` ### 核心 Unity API | API | 用途 | |-----|------| | `action.PerformInteractiveRebinding(index)` | 启动交互式重映射 | | `action.ApplyBindingOverride(index, path)` | 直接应用 Override | | `action.RemoveBindingOverride(index)` | 移除某条 Override(恢复默认)| | `asset.RemoveAllBindingOverrides()` | 全部恢复默认 | | `asset.SaveBindingOverridesAsJson()` | 序列化所有 Override 为 JSON | | `asset.LoadBindingOverridesFromJson(json)` | 从 JSON 恢复 Override | --- ## 3. RebindPanel — UI 面板 ### 面板结构(UXML) ``` ``` ### RebindPanel.cs ```csharp namespace BaseGames.Input { public class RebindPanel : MonoBehaviour { [SerializeField] InputReaderSO _inputReader; [SerializeField] VisualTreeAsset _rowTemplate; // RebindActionRow.uxml UIDocument _doc; VisualElement _listeningOverlay; Label _listeningLabel; // 需要在面板中显示的 Action 名单(顺序即显示顺序) static readonly string[] ShownActions = { "Move", "Jump", "Attack", "Parry", "Dash", "Heal", "Interact", "Pause" }; void Awake() { _doc = GetComponent(); } void OnEnable() { var root = _doc.rootVisualElement; _listeningOverlay = root.Q("ListeningOverlay"); _listeningLabel = root.Q