
1. 项目概述为什么Unity开发者绕不开Input System如果你是从Unity的旧版Input Manager就是那个在Input类里写GetKeyDown和GetAxis的时代一路走过来的老鸟或者你是一个刚入门就被各种输入设备搞得晕头转向的新手那么“Input System”这个词对你来说绝对不是一个陌生的概念。它更像是一个摆在面前、迟早要翻越的山头。我刚开始接触时也犯过嘀咕旧系统用得好好的为什么要换但当你真正开始做一个需要支持手柄、触屏、键盘鼠标无缝切换或者想实现一套复杂的技能连招系统时旧Input Manager那种“硬编码”和“设备强绑定”的弊端就暴露无遗了。简单来说Unity的新Input System是一个现代化、事件驱动、设备无关的输入处理框架。它把“按下A键”或“移动右摇杆”这些底层硬件信号抽象成了更高层级的“动作”Actions比如“跳跃”、“移动”、“攻击”。你的游戏逻辑只关心这些“动作”是否被触发至于这个“跳跃”动作是由空格键、手柄A键还是屏幕上的一个虚拟按钮触发的Input System帮你搞定。这带来的好处是巨大的代码与设备解耦一套逻辑适配所有输入方式输入配置可动态修改甚至允许玩家自定义按键对新型输入设备如VR控制器、方向盘的支持更加优雅。所以这个“进阶”学习系列的第一篇我们就从最实在的地方开始如何把它导入到你的项目里以及如何用最基础的API去“摸到”那些连接着的设备。别看这是基础很多人在第一步配置上就卡住了或者因为不熟悉新的API而觉得无从下手。我会带你绕开这些坑用最直白的方式让你快速上手。2. 核心思路事件驱动与动作映射在深入代码之前我们必须先理解Input System的设计哲学这能帮你少走很多弯路。旧Input Manager是典型的“轮询”Polling模式你在Update()里不停地问“空格键按下了吗”。而新Input System的核心是“事件驱动”Event-driven。你可以把它想象成一个高效的邮局。各种输入设备键盘、鼠标、手柄是发信人它们产生事件“按键按下”、“摇杆移动”。Input System是邮局的分拣中心它根据你预先定义好的“动作映射表”Action Maps将这些原始事件分拣成有意义的“动作”包裹比如“跳跃请求”。最后你的游戏逻辑是收件人它只需要监听并处理这些“动作”包裹完全不用关心包裹是哪个发信人、通过什么路线送来的。这种设计的优势在于高效只在输入事件发生时执行代码避免了Update中的空轮询。清晰输入逻辑定义动作和游戏逻辑响应动作分离项目结构更干净。灵活动态切换输入方案例如从键鼠切换到手柄变得非常简单只需启用不同的“动作映射集”即可。我们本篇要学习的“基础设备调用API”实际上是在和“分拣中心”的基础设施打交道比如查询当前有哪些“发信人”设备在线或者直接监听某个“发信人”的原始事件。这在调试、创建自定义输入设备管理器或处理一些非常规输入时非常有用。3. 第一步导入Input System的三种方式与避坑指南这是所有事情的起点但Unity的包管理有时会给你设点小障碍。以下是三种主流方式我会详细说明每种方式的适用场景和可能遇到的坑。3.1 通过Package Manager安装推荐这是最标准、最推荐的方式能确保你获取到的是经过Unity验证的兼容版本。打开Unity进入顶部菜单Window Package Manager。在Package Manager窗口左上角点击“Packages”下拉菜单选择“Unity Registry”。这是关键一步如果你停留在“My Assets”或“In Project”可能找不到它。在列表中找到“Input System”。你可以使用右上角的搜索框直接搜索。点击它在右侧详情面板中点击“Install”按钮。注意点击安装后Unity会弹出一个警告对话框提示“Input System将禁用旧的Input Manager”。这是正常的也是必须的。两个系统可以共存但同一时间只能有一个处于激活状态。直接点击“Yes”确认。如果你后续需要切换回去可以在Edit Project Settings Player中找到“Active Input Handling”选项进行更改。可能遇到的坑找不到包请确保你的Unity版本是2019.1或更高并且Package Manager源切换到了“Unity Registry”。2019.1是Input System正式发布的最低版本。安装失败检查网络连接或者尝试重启Unity和Package Manager。3.2 通过Unity Registry直接安装命令行/快速对于喜欢终端或者需要自动化脚本的开发者可以使用Unity的命令行工具。打开系统命令行Windows的CMD/PowerShellMac的Terminal导航到你的项目根目录然后执行unity -batchmode -projectPath . -executeMethod UnityEditor.PackageManager.Client.Add -args com.unity.inputsystem这种方式适合CI/CD流水线但在日常开发中不如图形界面直观。3.3 手动修改manifest.json高级/特定版本有时你可能需要锁定一个特定的、非最新的Input System版本。这时可以手动修改项目。在你的Unity项目文件夹中找到Packages/manifest.json文件。用文本编辑器打开它。在dependencies区块中添加或修改这样一行com.unity.inputsystem: 1.7.0,保存文件返回Unity它会自动开始解析和导入这个指定版本的包。实操心得我强烈建议新手使用第一种方式Package Manager。它能最大程度避免版本冲突和依赖问题。只有在你知道自己需要某个特定版本来解决兼容性问题时才考虑第三种方式。安装完成后你会在Package Manager的 “In Project” 列表中看到它并且菜单栏会多出一个“Edit Project Settings Input System Package”的选项这说明导入成功了。4. 基础设备调用API详解与硬件直接对话导入成功后我们就可以开始写代码了。Input System的核心命名空间是UnityEngine.InputSystem。我们先从最基础的——获取和监听输入设备开始。这部分API让你能直接接触到“邮局”的“发信人列表”。4.1 枚举所有已连接的设备InputSystem.devices是一个静态列表包含了当前所有被Input System识别并连接的输入设备。using UnityEngine; using UnityEngine.InputSystem; // 关键引入命名空间 public class DeviceLister : MonoBehaviour { void Start() { // 遍历所有设备并打印信息 foreach (var device in InputSystem.devices) { Debug.Log($设备名称: {device.displayName}, 类型: {device.GetType()}, 设备ID: {device.deviceId}); // 你可以进一步判断设备具体类型 if (device is Gamepad) { Debug.Log( 这是一个手柄); Gamepad gamepad device as Gamepad; // 现在你可以访问gamepad特有的属性如 gamepad.aButton } else if (device is Keyboard) { Debug.Log( 这是一个键盘); } else if (device is Mouse) { Debug.Log( 这是一个鼠标); } // 还可以判断Touchscreen, Pen, Sensor等 } } }为什么需要这个在游戏开始时的输入设备检测、创建动态的键位设置界面显示当前连接了哪些手柄、或者为不同设备初始化不同的默认配置时这个API非常有用。4.2 监听设备的连接与断开事件游戏运行时玩家可能会插拔手柄。我们需要动态响应这些变化。public class DeviceMonitor : MonoBehaviour { private void OnEnable() { // 订阅设备连接事件 InputSystem.onDeviceChange OnDeviceChange; } private void OnDisable() { // 取消订阅防止内存泄漏 InputSystem.onDeviceChange - OnDeviceChange; } private void OnDeviceChange(InputDevice device, InputDeviceChange change) { switch (change) { case InputDeviceChange.Added: Debug.Log($设备已连接: {device.displayName}); // 例如自动将新连接的手柄设为玩家2 break; case InputDeviceChange.Removed: Debug.Log($设备已断开: {device.displayName}); // 例如处理玩家掉线逻辑 break; case InputDeviceChange.ConfigurationChanged: Debug.Log($设备配置已更改: {device.displayName}); // 例如手柄电量低或配置文件切换 break; // 还有其他枚举值如Enabled, Disabled, UsageChanged等 } } }注意事项一定要订阅和取消订阅配对出现。在OnEnable中订阅在OnDisable或OnDestroy中取消订阅这是Unity事件系统的良好实践避免脚本禁用或物体销毁后仍被调用导致空引用或逻辑错误。InputDeviceChange.ConfigurationChanged对于支持宏或配置切换的专业设备如一些高端手柄或方向盘很有用。4.3 获取特定类型的默认设备通常我们关心的是“玩家1用的手柄”或“主键盘”。Input System提供了便捷的获取方式。void CheckDefaultDevices() { // 获取当前平台默认的游戏手柄通常是最近连接或第一个识别的手柄 Gamepad currentGamepad Gamepad.current; if (currentGamepad ! null) { Debug.Log($当前默认手柄: {currentGamepad.displayName}); // 直接读取状态 if (currentGamepad.aButton.wasPressedThisFrame) { Debug.Log(默认手柄的A键被按下了); } } else { Debug.LogWarning(未检测到连接的手柄。); } // 同样可以获取键盘和鼠标 Keyboard keyboard Keyboard.current; Mouse mouse Mouse.current; if (keyboard ! null keyboard.spaceKey.wasPressedThisFrame) { Debug.Log(空格键被按下通过.current); } }核心要点.current属性是获取“主要”或“最相关”设备的最快方式。对于鼠标键盘在PC上基本总是有效的。对于手柄它通常指向第一个可用的手柄。在访问.current或任何设备属性前进行空值检查 (! null) 是一个好习惯因为设备可能在任何时候被拔掉。4.4 直接读取设备状态低层级API虽然Input System鼓励使用“动作”Action但有时你需要直接读取原始数据比如做一个设备测试工具。public class RawInputReader : MonoBehaviour { void Update() { var gamepad Gamepad.current; if (gamepad ! null) { // 读取左摇杆的二维向量值 Vector2 leftStickValue gamepad.leftStick.ReadValue(); if (leftStickValue.magnitude 0.1f) // 加入一个死区阈值 { Debug.Log($左摇杆: {leftStickValue}); } // 读取右扳机键的压力值0到1 float rightTriggerValue gamepad.rightTrigger.ReadValue(); if (rightTriggerValue 0.2f) { Debug.Log($右扳机压力: {rightTriggerValue}); } // 判断按钮是否在本帧被按下 if (gamepad.buttonSouth.wasPressedThisFrame) // 通常是A键Xbox布局或Cross键PlayStation布局 { Debug.Log(A/X键被按下); } } // 直接读取键盘事件 var keyboard Keyboard.current; if (keyboard ! null) { // 同时检测多个按键 if (keyboard.wKey.isPressed keyboard.leftShiftKey.isPressed) { Debug.Log(正在冲刺WShift); } } } }与旧Input Manager的对比这里gamepad.leftStick.ReadValue()类似于旧的Input.GetAxis(“Horizontal”)但它是直接从设备对象获取更面向对象。wasPressedThisFrame和isPressed则对应GetKeyDown和GetKey。重要区别Input System中的wasPressedThisFrame等状态其“帧”的判断是基于Input System自己的更新时机默认与MonoBehaviour.Update同步但也可以配置为固定更新或手动更新这比旧系统更灵活。5. 从设备API到动作映射的桥梁PlayerInput组件初探学完基础设备API你可能会觉得“这好像和旧系统差不多啊还是要手动查设备、读数值。” 别急这只是为了让你理解底层。Input System的威力在于上层的“动作映射”。Unity提供了一个非常方便的组件PlayerInput来帮你快速搭建这座桥梁。虽然本系列后续会深入讲解自己创建Input Action Asset但这里可以先感受一下PlayerInput的便捷创建动作资源在Project窗口右键Create Input Actions命名为“PlayerControls”。双击打开编辑器创建一个“Gameplay” Action Map在里面添加两个Action“Move”2D Vector类型绑定到Gamepad的左摇杆和键盘的WASD。“Jump”Button类型绑定到Gamepad的South按钮和键盘空格键。保存资源。在游戏对象上添加PlayerInput组件。将“PlayerControls”这个Asset拖拽到组件的“Actions”属性上。在“Behavior”属性中选择“Invoke Unity Events”。现在你不需要在Update里写任何检测代码。只需要在PlayerInput组件暴露出来的UnityEvent中关联你的处理函数public class PlayerController : MonoBehaviour { public void OnMove(InputAction.CallbackContext context) { // context.ReadValueVector2() 直接给你移动向量 Vector2 direction context.ReadValueVector2(); Debug.Log($移动输入: {direction}); // 这里可以驱动角色移动 } public void OnJump(InputAction.CallbackContext context) { // context.started 表示按钮按下开始的瞬间 if (context.started) { Debug.Log(跳跃); // 这里触发跳跃逻辑 } } }PlayerInput组件自动帮你处理了所有设备的绑定、事件监听和路由你的代码只需要关心高层的“移动”和“跳跃”动作。这才是Input System的正确打开方式。基础设备API是你需要知道的“后台知识”而动作映射才是你日常开发的“主力工具”。6. 常见问题与排查技巧实录在实际使用基础API时我踩过不少坑这里总结一下希望能帮你节省时间。问题1InputSystem.devices列表为空或者Gamepad.current为null。可能原因1项目设置中未启用Input System。去Edit Project Settings Player Other Settings查看“Active Input Handling”是否选择了“Input System Package (New)”或“Both”。必须选前两者之一。可能原因2脚本执行顺序问题。在Awake中访问设备可能太早Input System尚未初始化完毕。将设备访问代码移到Start或OnEnable中更安全。可能原因3真的没有设备连接或者设备驱动异常。尝试插拔设备或检查系统设备管理器。问题2手柄输入有延迟或不跟手。排查检查Input System的更新模式。在Edit Project Settings Input System Package中有一个“Update Mode”选项。Fixed Update与物理更新同步输入采样频率固定适合物理相关的输入但可能感觉延迟。Dynamic Update默认选项。尽可能快地处理输入通常感觉最跟手。Manual Update需要手动调用InputSystem.Update()适用于特定框架如ECS。新手不要选这个。建议对于大多数动作游戏保持“Dynamic Update”即可。问题3同时连接多个同型号手柄时如何区分玩家1和玩家2解决方案基础API中InputSystem.devices列表的顺序并不总是代表玩家顺序。更可靠的做法是利用InputSystem.onDeviceChange事件监听设备连接。当手柄连接时InputDeviceChange.Added将其加入一个你自己管理的“可用手柄”列表。在游戏逻辑中如角色选择界面让玩家按手柄上的某个键如Start键来“认领”这个手柄并将其与你游戏中的“玩家索引”绑定。之后通过你绑定的索引来访问特定的手柄对象而不是依赖.current。问题4在编辑器里运行正常打包后输入无效。排查这是最常见的问题之一。首先检查打包设置中是否包含了Input System需要的资源。确保你的Input Action Asset.inputactions文件在打包的Resources文件夹或Addressables加载路径中。最简单的方法是将它放在Resources文件夹下或者确保它在Project Settings Player Publishing Settings的Managed Stripping Level设为Low或Medium防止代码剥离过度。检查依赖Input System包本身已经包含在构建中但如果你用了任何自定义的输入设备插件需要确保它们也被正确打包。问题5如何模拟输入用于测试或录制回放技巧Input System提供了强大的模拟输入API。你可以创建一个InputDevice的模拟器。// 创建一个虚拟手柄 var virtualGamepad InputSystem.AddDeviceGamepad(); // 然后你可以通过代码“按下”它的按钮 InputSystem.QueueStateEvent(virtualGamepad, new GamepadState { .buttonSouth true }); // 这会在下一帧触发所有绑定到“South按钮”的动作非常适合自动化测试。掌握了这些基础设备API你就有了直接与硬件对话的能力。它们是构建更复杂输入逻辑的基石。但请记住在大多数实际游戏开发中你应该尽快过渡到使用Input Action Asset和PlayerInput组件或自己管理InputAction这种更高层、更声明式的模式这才是发挥Input System全部威力的地方。下一篇我们将深入那个强大的可视化配置工具——Input Action Editor学习如何优雅地定义和管理你所有的游戏输入。