19.Unity进阶InputSystem总结

  1. 19.总结
    1. 19.1 知识点
      1. 学习的主要内容
      2. 总结讲什么
      3. 对比
      4. 选择建议
      5. 总结
    2. 19.2 核心要点速览
      1. InputSystem开启方法
      2. 代码检测输入
        1. 键盘输入
        2. 鼠标输入
        3. 触屏输入
        4. 手柄输入
        5. 其他输入
      3. InputAction
        1. InputAction参数设置
        2. InputAction输入绑定类型
        3. InputAction使用方法
        4. InputAction特殊设置及系统设置
        5. InputActions输入操作配置文件
          1. InputSystem中专门用于任意键按下的方案
          2. 通过Json数据加载配置文件
        6. 输入配置文件生成CSharp代码
      4. PlayerInput
        1. PlayerInput介绍
          1. SendMessage示例
          2. BroadcastMessage示例
          3. Invoke Unity Events示例
          4. Invoke C Sharp Events示例
        2. PlayerInput玩家输入管理组件
          1. PlayerInputManager使用示例
      5. UGUI配合使用
      6. InputDebug输入调试器
    3. 19.3 面试题精选
      1. 基础题
        1. 1. InputSystem 与 InputManager 的区别
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. PlayerInput 的四种行为执行模式
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. InputAction 的 Action Type 有哪几种
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      2. 进阶题
        1. 1. InputAction 的 Interactions 机制
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. PlayerInputManager 如何实现本地多人输入
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. 如何实现改键功能
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      3. 深度题
        1. 1. InputSystem 的事件驱动机制与性能优化
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. 多设备输入同步与冲突处理
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章

19.总结


19.1 知识点

学习的主要内容

总结讲什么

对比

选择建议

总结


19.2 核心要点速览

InputSystem开启方法

开启方法:File——>Build Setting——>Player Setting——>Other——>Active Input Handling

代码检测输入

键盘输入

获取方式 操作检测 说明
using UnityEngine.InputSystem; <br> Keyboard keyBoard = Keyboard.current; 单个按键按下 通过keyBoard.具体按键.wasPressedThisFrame判断,如if (Keyboard.current.spaceKey.wasPressedThisFrame) 可检测空格键按下
单个按键抬起 利用keyBoard.具体按键.wasReleasedThisFrame,例如if (Keyboard.current.dKey.wasReleasedThisFrame) 检测D键抬起
单个按键长按 使用keyBoard.具体按键.isPressed,像if (Keyboard.current.spaceKey.isPressed) 检测空格长按状态
文本输入监听 lambda表达式:keyBoard.onTextInput += (c) => {print("通过lambda表达式" + c);}
普通函数:先定义private void TextInput(char c) ,再keyBoard.onTextInput += TextInput;
任意键按下 if (Keyboard.current.anyKey.wasPressedThisFrame) ,但无法得知具体按下的键

鼠标输入

获取方式 操作检测 说明
Mouse mouse = Mouse.current; 各键位按下 以左键为例,if (Mouse.current.leftButton.wasPressedThisFrame) 判断左键按下
各键位抬起 if (Mouse.current.leftButton.wasReleasedThisFrame) 检测左键抬起
各键位长按 例如if (Mouse.current.rightButton.isPressed) 检测右键长按
当前位置 Mouse.current.position.ReadValue() ,原点是左下角,值为屏幕最大分辨率
两帧偏移向量 Mouse.current.delta.ReadValue() ,获取鼠标两帧间移动向量
滚轮方向向量 Mouse.current.scroll.ReadValue() ,往上滚大于0,往下滚小于0

触屏输入

获取方式 操作检测 说明
Touchscreen touchscreen = Touchscreen.current;
使用前判空:if (touchscreen == null) return;
手指数量 print(touchscreen.touches.Count) 输出当前触屏手指数量
单个触屏手指 touchscreen.touches[0] 获取指定索引的手指信息
遍历所有触屏手指 foreach (var item in touchscreen.touches) 遍历所有触屏手指
手指按下 if (touchControl.press.wasPressedThisFrame)touchControl 为指定手指对象
手指抬起 if (touchControl.press.wasReleasedThisFrame) 检测手指抬起
手指长按 if (touchControl.press.isPressed) 判断手指长按状态
点击手势 if(touchControl.tap.isPressed) 检测手指点击
连续点击次数 print(touchControl.tapCount) 输出手指连续点击次数
手指位置 print(touchControl.position.ReadValue()) 获取当前手指位置
首次接触位置 print(touchControl.startPosition.ReadValue()) 得到手指第一次接触时位置
接触区域大小 touchControl.radius.ReadValue() 读取手指接触区域半径大小
偏移位置 touchControl.delta.ReadValue() 获取手指偏移位置
手指状态 UnityEngine.InputSystem.TouchPhase touchPhase = touchControl.phase.ReadValue();
通过switch语句判断:
csharp<br>switch (touchPhase)<br>{<br>//无<br>case UnityEngine.InputSystem.TouchPhase.None:<br>break;<br>//开始接触<br>case UnityEngine.InputSystem.TouchPhase.Began:<br>break;<br>//移动<br>case UnityEngine.InputSystem.TouchPhase.Moved:<br>break;<br>//结束<br>case UnityEngine.InputSystem.TouchPhase.Ended:<br>break;<br>//取消<br>case UnityEngine.InputSystem.TouchPhase.Canceled:<br>break;<br>//静止<br>case UnityEngine.InputSystem.TouchPhase.Stationary:<br>break;<br>default:<br>break;<br>}

手柄输入

获取方式 操作检测 说明
Gamepad gamePad = Gamepad.current;
使用前判空:if (gamePad == null) return;
左摇杆方向向量 print(gamePad.leftStick.ReadValue()) ,右和上是正方向
右摇杆方向向量 print(gamePad.rightStick.ReadValue())
左摇杆按下 if (gamePad.leftStickButton.wasPressedThisFrame) 判断左摇杆按下
左摇杆抬起 if (gamePad.leftStickButton.wasReleasedThisFrame) 检测左摇杆抬起
左摇杆长按 if (gamePad.leftStickButton.isPressed) 检测左摇杆长按
方向键(以左方向键为例) 按下:if (gamePad.dpad.left.wasPressedThisFrame)
抬起:if (gamePad.dpad.left.wasReleasedThisFrame)
长按:if (gamePad.dpad.left.isPressed)
右侧按键(以北向按钮为例) gamePad.buttonNorth ,按下检测如if (Gamepad.current.buttonNorth.wasPressedThisFrame) ,也可按具体名字如gamePad.triangleButton 等监听
中央按键(开始键) gamePad.startButton ,按下检测if (Gamepad.current.startButton.wasPressedThisFrame)
中央按键(选择键) gamePad.selectButton ,按下检测if (Gamepad.current.selectButton.wasPressedThisFrame)
肩部按键(左侧前方) gamePad.leftShoulder ,按下检测if (Gamepad.current.leftShoulder.wasPressedThisFrame)
肩部按键(左侧后方) gamePad.leftTrigger ,按下检测if (Gamepad.current.leftTrigger.wasPressedThisFrame)

其他输入

设备类型 说明 学习方式
Joystick(摇杆) 用于输入操作 查看官方文档或类内部成员,了解属性和方法
Pen(电子笔) 实现特定输入 同上
Sensor(传感器) 包含陀螺仪(UnityEngine.InputSystem.Gyroscope )、重力传感器等 查看官方文档,或通过代码示例如UnityEngine.InputSystem.Gyroscope gyroscope = UnityEngine.InputSystem.Gyroscope.current; gyroscope.angularVelocity.ReadValue(); ,查看类内部成员使用

InputAction

InputAction参数设置

设置项 详情
Actions输入动作设置 设置检测的输入类型
Action Type(动作类型) Value:用于状态连续更改的输入,多个设备绑定时只发送最受控制设备的输入
Button:每次按下时触发的Action
Pass Through:和Value类似,多个设备绑定时发送所有设备的输入
Control Type(控制类型) 选择类型后会筛选输入设备,如Vector2只能选返回Vector2变量类型的设备(手柄摇杆、鼠标位置等),Button只能选按钮返回值设备(鼠标左键、手柄按键等),包括Any、Analog、Axis、Bone、Digital、Double、Dpad、Eyes、Integer、Quaternion、Stick、Touch、Vector2、Vector3等
Interactions相互作用设置 用于特殊输入(长按、多次点击等),满足条件时触发行为,有started(开始)、performed(触发)、canceled(结束)三个事件
相互作用设置参数 Hold:长按操作,按下触发started,按住时间大于等于Hold Time触发performed,否则触发canceled
Tap:点击操作,按下触发started,在Max Tap Duriation时间内松开触发performed,否则触发canceled
SlowTap:类似Hold,按住时间大于等于Max Tap Duriation,松开时触发performed
MultiTap:多次点击操作,Tap Count为点击次数,Max Tap Spacing为点击间隔,Max Tap Duration为每次点击持续时间,满足条件触发performed
Press:类似按钮操作,Trigger Behavior有Press Only(按下触发started和performed,不触发canceled)、Release Only(按下触发started,松开触发performed)、Press And Release(按下和松开都触发started和performed,不触发canceled)、Press Point(利用按钮浮点值区分按下状态)
Processors值处理加工设置 Clamp:将输入值钳制到[min..max]范围
Invert:反转控件值(乘以-1)
Invert Vector 2:反转Vector2轴的值
Invert Vector 3:反转Vector3轴的值
Normalize:将输入值规格化为[0..1]或[-1..1]
Normalize Vector 2:将输入向量规格化为单位长度
Normalize Vector 3:将输入向量规格化为单位长度
Scale:将输入值乘以系数
Scale Vector 2:沿x、y轴乘以相应值
Scale Vector 3:沿x、y、z轴乘以相应值
死区设置 Axis Deadzone:缩放控件值,使绝对值小于最小值为0,大于最大值为1或-1,避免无意输入和确保最大值
Stick Deadzone:缩放Vector2控件值,幅值小于最小值为(0,0),大于最大值规格化为长度1

InputAction输入绑定类型

绑定类型 详情
Add Binding 添加单按键输入绑定,如鼠标左右键按下
Add Positive\Negative Binding Or Add 1D Axis Composite 添加1D轴组合(类似水平竖直热键,返回-1~1值),参数有Negative(负面按键)、Positive(正向按键)、Composite Type(复合类型)、MinValue(最小值)、MaxValue(最大值)、Which Side Wins(同时按下处理方式,Neither:取中间值,Positive:取maxValue,Negative:取minValue)
Add Up\Down\Left\Right Composite Or Add 2D Vector Composite 添加2D向量组合(类似水平竖直热键组合,Vector的x、y表示两个轴),参数有Up、Down、Left、Right、Composite Type(复合类型)、Mode(处理模式,Analog:模拟值,Digital Normalized:单位化向量,Digital:未单位化向量)
Add Up\Down\Left\Right\Forward\Backward Composite 添加3D向量组合,比2D多一维参数,需关联多两个键
Add Button With One Modifier Composite 添加带有一个复合修改器的按钮(双组合键,如Ctrl+C),参数有Modifier(复合输入内容)、Button(输入内容)
Add Button With Two Modifier Composite 添加带有两个复合修改器的按钮(三组合键,如Ctrl+K+U),参数有Modifier(两个复合输入内容)、Button(输入内容)

InputAction使用方法

操作 详情
启用输入检测 move.Enable();
操作监听 - 开始操作 move.started += StartedTestFun;
private void StartedTestFun(InputAction.CallbackContext context) { print("开始事件调用"); }
操作监听 - 真正触发 move.performed += (context) => { print("触发事件调用"); }
操作监听 - 结束操作 move.canceled += (context) => { print("结束事件调用"); }
关键参数CallbackContext 可获取当前状态(context.phase,如Disabled、Waiting、Started、Performed、Canceled)、动作行为信息(context.action.name)、控件(设备)信息(context.control.name)、获取值(context.ReadValue<float>)、持续时间(context.duration)、开始时间(context.startTime

InputAction特殊设置及系统设置

设置项 详情
特殊输入设置 可在Interactions中为整个动作或单个设备设置特殊输入条件,如长按时间等;可点击创建InputSystem默认值设置文件并修改
Input System Package设置 UpdateMode:更新模式(Process Events In Dynamic Update、Process Events In Fixed Update、Process Events Manually)
Background Behavior:后台行为(Reset And Disable Non Background Devices、Reset And Disable All Devices、Ignore Focus、Filter Noise on .current、Compensate Orientation)
Default Deadzone Min:默认死区最小值
Default Deadzone Max:默认死区最大值
Default Button Press Point:默认按钮按下点
Button Release Threshold:按钮释放阈值
Default Tap Time:默认点击时间
Default Slow Tap Time:默认慢速点击时间
Default Hold Time:默认保持时间
Tap Radius:点击半径
MultiTap Delay Time:多次点击延迟时间
Supported Devices:可设置支持的设备,不设置则支持所有可识别设备,设置后缩小编辑器显示范围并避免无关设备输入
Motion Usage:动作使用
Description:描述
Play Mode Input Behavior:播放模式输入行为(Pointers And Keyboards Respect Game View Focus、All Devices Respect Game View Focus、All Device Input Always Goes To Game View)

InputActions输入操作配置文件

类别 详情
定义 输入系统中是 InputAction 的集合,记录想要处理的行为和动作(InputAction 相关信息),可自定义 InputAction(如开火、移动、旋转等)并关联对应的输入动作,与 PlayerInput 关联后,PlayerInput 自动解析文件,触发输入动作时以分发事件形式通知执行行为
创建方式 在 Project 窗口右键选择 Create 创建 InputActions 配置文件,文件后缀为 .inputactions,本质是 json 文件
编辑方式 双击创建出的文件弹出配置窗口进行配置,每次更改后需点击保存配置或勾选自动保存
配置窗口参数 ActionMaps 行动地图窗口:可理解为行为分组配置,能配置多套操作规范,可右键或点击加号创建新配置、移除等
Actions 输入动作窗口:在此可通过加号或右键创建各种 InputAction,无需在代码中声明变量,可直接添加键盘配置
Properties 输入操作绑定的输入属性:与普通 InputAction 点击齿轮的选项相同
工具栏:提供保存、搜索等功能按键
设置编辑器显示的控制方案 选项:Gamepad(手柄)、Joystick(摇杆)、KeyboardMouse(键盘鼠标)、Touch(触屏)、XR(VR/AR 等)、All Devices(设备选择)
作用:切换控制方案后,若该方案由多个设备配合控制,可进一步筛选对应设备,如切换键盘鼠标方案后可二次筛选
保存配置 Auto-Save:自动保存配置
搜索栏:可搜索对应动作行为
编辑配置文件基本操作流程 1. 左上角添加控制方案(如手柄和鼠标键盘),在下面小列表分别选择设备
2. 在每个绑定设备中选择设备的控制方案
3. 左上角选择控制方案可筛选设备,选择后还可二次筛选设备
InputSystem中专门用于任意键按下的方案
InputSystem.onAnyButtonPress.CallOnce
InputSystem.onAnyButtonPress.CallOnce((control) =>
{
    print("InputSystem中专门用于任意键按下的方案 路径" + control.path);
    print("InputSystem中专门用于任意键按下的方案 名字" + control.name);
});
通过Json数据加载配置文件
string json = Resources.Load<TextAsset>("PlayerInputTest").text;

// 使用 InputActionAsset 类的静态方法 FromJson 解析 json 字符串,返回解析后的 InputActionAsset 对象,并将其存储在变量 asset 中
InputActionAsset asset = InputActionAsset.FromJson(json);

// 将解析后的 InputActionAsset 对象赋值给 input 对象的 actions 属性
input.actions = asset;

输入配置文件生成CSharp代码

类别 详情 示例
课前准备 创建配置文件,在配置文件中创建两套行为 假设创建一个游戏相关的输入配置文件,一套行为是角色的战斗操作(如开火等),另一套行为是角色的移动和跳跃操作
根据配置文件生成C#代码 1. 选择 InputActions 文件,勾选 Generate C# Class
2. 在 Inspector 窗口设置生成路径(如 Assets/Scripts/GeneratedInputCode)、类名(如 CustomInputActions)、命名空间(如 MyGame.Input)
3. 应用后生成代码,生成的代码根据 InputAction 配置文件解析 Json 生成类并提供一些方法
例如,在 Unity 项目中,已经创建好名为 MyInputActions.inputactions 的配置文件,选中它后在 Inspector 窗口勾选 Generate C# Class,设置生成路径为 Assets/Scripts/InputCode,类名为 GameInputActions,命名空间为 GameProject.Input,点击应用后,在指定路径下生成对应的 C# 代码文件
使用C#代码进行监听 声明对象 GameInputActions input; 声明一个用于监听输入的配置文件对象(这里以 GameInputActions 为例,对应前面生成的类名)
Start 方法 1. 创建生成的代码对象:input = new GameInputActions(); 实例化配置文件对象
2. 激活配置文件的输入:input.Enable(); 启用输入
3. 添加事件监听:
input.Action1.Fire.performed += (context) => { print("鼠标点击 开火"); };
input.Action2.Space.performed += (context) => { print("空格按下 跳跃"); };
假设 Action1 是战斗操作行为组,Fire 是其中的开火输入动作;Action2 是移动跳跃行为组,Space 是其中的跳跃输入动作
Update 方法 print("wasd移动" + input.Action1.Move.ReadValue<Vector2>()); 监听输入并打印相关信息
假设 Move 是 Action1 行为组中用于控制角色移动的输入动作,通过 ReadValue 获取移动的向量值并打印

PlayerInput

PlayerInput介绍

类别 详情
PlayerInput定义 InputSystem提供的用于接受玩家输入并处理自定义逻辑的组件
PlayerInput工作原理 1. 配置输入文件(InputActions文件)
2. 通过PlayerInput关联配置文件,自动解析
3. 关联响应函数,处理对应逻辑
PlayerInput好处 1. 无需自己书写输入逻辑
2. 通过配置文件配置监听行为
3. 专注于输入事件触发后的逻辑处理
添加PlayerInput组件 选择一个对象(一般为玩家对象),为其添加PlayerInput组件
PlayerInput参数 Actions(行为)
Default Scheme(默认控制方案)
Ui Input Module(UI输入模块)
Camera(摄像机)
Behavior(行为执行模式):
SendMessage:逻辑脚本挂载在与PlayerInput同一对象上,通过SendMessage通知执行函数
BroadcastMessage:逻辑脚本可挂载在自身或子对象上,通过BroadcastMessage通知执行函数
Invoke UnityEvent Actions:在Inspector窗口通过拖拽关联函数,响应函数参数类型为InputAction.CallbackContext,可关联其他对象的脚本函数
Invoke CSharp Events:通过获取PlayerInput进行事件监听,可得到对应行为的值,如input.currentActionMap[“Move”].ReadValue()
SendMessage示例

在自定义脚本中声明名为“On+行为名”的函数,无参数或参数类型为InputValue,挂载到PlayerInput依附对象上。代码如下:

public void OnMove(InputValue value)
{
    print("Move");
    print("Move" + value.Get<Vector2>());
}


public void OnFire(InputValue value)
{
    print("Fire");
    if (value.isPressed)
    {
        print("Fire按下");
    }
}
BroadcastMessage示例

基本规则同SendMessage,自定义脚本还可挂载在子对象上。

Invoke Unity Events示例

在脚本中声明函数:

public void MyFire(InputAction.CallbackContext context)
{
    print("开火1");
}

public void MyMove(InputAction.CallbackContext context)
{
    print("移动1");
}

public void MyLook(InputAction.CallbackContext context)
{
    print("Look1");
}

在Inspector窗口通过拖拽关联函数。

Invoke C Sharp Events示例
  1. 获取PlayerInput组件:
PlayerInput input = this.GetComponent<PlayerInput>();
  1. 添加委托函数:
input.onDeviceLost += OnDeviceLost;
input.onDeviceRegained += OnDeviceRegained;
input.onControlsChanged += OnControlsChanged;
input.onActionTriggered += OnActionTrigger;
  1. 处理事件:
public void OnActionTrigger(InputAction.CallbackContext context)
{
    print("触发OnActionTrigger");
    switch (context.action.name)
    {
        case "Fire":
            if (context.phase == InputActionPhase.Performed)
                print("开火");
            break;
        case "Look":
            print("看向");
            print(context.ReadValue<Vector2>());
            break;
        case "Move":
            print("移动");
            print(context.ReadValue<Vector2>());
            break;
    }
}

PlayerInput玩家输入管理组件

类别 详情
关键参数 InputValue:
判断是否按下:inputValue.isPressed
获取Vector2类型值:inputValue.Get<Vector2>()
InputAction.CallbackContext:
获取Vector2类型值的x和y属性:callbackContext.ReadValue<Vector2>().xcallbackContext.ReadValue<Vector2>().y
获取输入行为名字:callbackContext.action.name
获取输入控件名字:callbackContext.control.name
获取输入控件所属设备名字:callbackContext.control.device.name
获取输入阶段枚举值:callbackContext.phase
获取输入阶段字符串表示:callbackContext.phase.ToString()
PlayerInputManager作用 用于管理本地多人输入的输入管理器,管理玩家加入和离开,如分屏游戏和双手柄游戏
PlayerInputManager组件参数 Notification Behavior:当玩家进入时,PlayerInputManager通知关联对象的方式,与PlayerInput工作方式相同
Join Behavior(玩家加入机制):
Join Players When Button Is Pressed:新设备加入或无玩家时按下任意键加入
Join Players When Join Action Is Triggered:新设备加入时按下指定按键触发加入,一般选择此项
Join Players Manually:手动加入玩家
Player Prefab:挂载PlayerInput组件的游戏对象
Joining Enabled By Default:启用后,新加玩家按JoinBehavior规则加入,默认勾选
Limit Number Of Players:启用后,可限制加入游戏的玩家数量,Max Player Count为允许的最大玩家数
Enable Split_Screen:启用后,自动为每个对象分配屏幕区域用于多人分屏游戏
Maintain Aspect Ratio:控制分屏区域纵横比与实际屏幕分辨率匹配
Set Fixed Number:大于零时,始终将屏幕分割为固定数量矩形
Screen Rectangle:用于分配玩家分屏的规范化屏幕矩形
PlayerInputManager使用示例
  1. 创建预制体方块,添加PlayerInput组件和脚本,创建配置文件并关联到预制体的PlayerInput组件,再关联到PlayerInputManger的玩家预制体上(预制体需有PlayerInput组件)。
  2. 调整方块预制体对象输入行为为手动关联模式,关联脚本上的Move函数,代码如下:
private Vector3 dir;
void Update()
{
    this.transform.Translate(dir * 10 * Time.deltaTime);
}

public void Move(InputAction.CallbackContext context)
{
    dir = context.ReadValue<Vector2>();
    dir.z = dir.y;
    dir.y = 0;
}
  1. 选择模式(如按A键和手柄开始键创建新预制体),PlayerInputManager自动处理输入控制。
  2. 让方块预制体挂载摄像机,勾选分屏选项实现分屏效果。
  3. 使用单例获取PlayerInputManager并添加玩家加入和离开事件:
//获取PlayerInputManager
//PlayerInputManager.instance
//玩家加入时
PlayerInputManager.instance.onPlayerJoined += (playerInput) =>
{
    print("创建了一个玩家");
};
//玩家离开时
PlayerInputManager.instance.onPlayerLeft += (playerInput) =>
{
    print("离开了一个玩家");
};

UGUI配合使用

类别 详情
InputSystem对UI的支持情况 不支持IMGUI(GUI),此限制仅对编辑器代码有效。若激活InputSystem,OnGUI中输入判断内容不触发,让OnGUI内容有效需选“Both”或只激活老输入系统InputManager;支持UGUI,需用新输入系统的输入模块(Input System UI Input Module)处理相关输入
UGUI中新输入系统输入模块参数 Move Repeat Delay
Move Repeat Rate:移动动作保持激活时,生成重复导航事件的间隔(秒),受帧速率限制,每帧不会有多个移动重复事件
XR Tracking Origin:无明确详细说明
Deselect On Background CLick:默认点击指针未击中游戏对象时清除当前选择,可能妨碍键盘和游戏板导航,设为false可防止自动取消选择
Pointer Behavior:
Single Mouse Or Pen BUt Multi Touch And Track:未分类为触摸或跟踪输入的按单个统一指针处理,跟踪和触摸输入按所有指针处理,触摸或跟踪设备输入时,鼠标和笔的单个统一指针会被删除
Single Unified Pointer:所有指针输入统一为一个指针,只有帧中最后一个输入生效
All Pointers As Is:UI输入模块不统一指针输入,每个设备输入都是独立指针,可能出现多个指针同时指向多个对象的情况
Actions Asset:包含控制UI的所有操作的输入操作资产,默认引用DefaultInputActions内置资产,可创建自定义资源并在此分配,编辑器会根据命名约定自动映射操作到UI输入
Point:提供2D屏幕位置的动作,用于鼠标样式的UI交互,设置为传递操作类型和向量2值类型
Left Click:映射到与UI交互的主光标按钮的操作,设置为传递操作类型和按钮值类型
Middle Click:映射到与UI交互的中间光标按钮的操作,设置为传递操作类型和按钮值类型
Right Click:映射到与UI交互的辅助光标按钮的操作,设置为传递操作类型和按钮值类型
Scroll Wheel:提供手势输入以允许在UI中滚动的操作,设置为传递操作类型和向量2值类型
Move:提供用于选择当前活动用户界面的二维矢量的操作,允许游戏板或箭头键样式的UI导航,设置为传递操作类型和向量2值类型
Submit:与当前选择的UI接触或“单击”的操作,设置为按钮动作类型
Cancel:退出与当前选定UI的任何交互的操作,设置为按钮动作类型
Tracked Position:提供一个或多个空间跟踪设备(如XR hand控制器)的3D位置的动作,结合跟踪设备方向实现XR样式的UI交互,设置为传递操作类型和向量3值类型
Tracked Orientation:传递表示一个或多个空间跟踪设备(如XR hand控制器)旋转的四元数的操作,结合跟踪设备位置实现XR样式的UI交互,设置为传递操作类型和四元数值类型
VR项目使用注意事项 在VR项目中用新输入系统配合UGUI,需在Canvas对象上添加Tracked Device Raycaster组件
多人游戏使用多套UI 同一设备多人游戏,每人想用独立UI,需将EventSystem中的EventSystem组件替换为Multiplayer Event System组件(先移除InputSystemUIInputModule和EventSystem组件再添加);可同时激活多个MultiplayerEventSystem,每个玩家有自己的InputSystemUIInputModule和MultiplayerEventSystem组件及操作来驱动UI;若使用PlayerInput组件,可设置其自动配置玩家的InputSystemUIInputModule;MultiplayerEventSystem组件属性与事件系统中属性相同,还添加了playerRoot属性,可设为包含此事件系统应处理的所有UI可选择项的游戏对象
On - Screen组件相关 可模拟UI和用户操作的交互,如On - Screen Button用于按钮交互,On - Screen Stick用于摇杆交互,关联之前输入配置文件设置好逻辑和关联设备的小球输入,如On - Screen Stick关联手柄摇杆可像手柄摇杆一样控制小球移动,On - Screen Button关联空格键可像按空格键一样控制小球跳跃

InputDebug输入调试器

类别 详情
InputDebug定义 即输入调试器,可通过输入调试窗口检测输入相关信息,用于在输入不按预期工作时排查问题
打开InputDebug窗口的方式 1. 通过菜单栏:Window(窗口)-> Analysis(分析)-> Input Debugger(输入调试器)
2. 通过PlayerInput组件:点击PlayerInput组件上的Open Input Debugger
InputDebug窗口信息 - 未运行状态 Options操作下拉框
Devices
Unsupported
Layouts
Controls
Abstract Devices
Specific Devices
Settings
Metrics
InputDebug窗口信息 - 运行状态 双击设备可进行相关操作和查看更详细信息(文档中未明确具体内容,仅提及双击设备这一操作)

19.3 面试题精选

基础题

1. InputSystem 与 InputManager 的区别

题目

请简述 Unity 新输入系统 InputSystem 与旧输入系统 InputManager 的主要区别,以及什么情况下应该选择使用 InputSystem。

深入解析

InputManager 是 Unity 传统的输入系统,通过 Input.GetAxis()Input.GetKeyDown() 等 API 直接检测输入,代码耦合度高,难以支持多设备切换。InputSystem 是 Unity 推出的新一代输入系统,采用事件驱动架构,支持配置文件、多设备自动切换、组合键、长按等复杂交互。

主要区别

对比维度 InputManager(旧) InputSystem(新)
架构模式 轮询式,每帧主动查询 事件驱动,输入触发回调
设备支持 键盘、鼠标、手柄有限支持 统一抽象,支持键盘、鼠标、手柄、触屏、VR 等
配置方式 代码硬编码或 Project Settings .inputactions 配置文件,可视化编辑
多设备切换 需手动处理 自动识别与切换
组合键/长按 需自行实现 Interactions 内置支持
本地多人 实现复杂 PlayerInputManager 原生支持

选择建议

  • 新项目优先使用 InputSystem,尤其是需要多平台、多设备支持的项目
  • 需要本地多人分屏、改键功能的项目,InputSystem 更合适
  • 如果项目只支持单一平台且输入逻辑简单,InputManager 也能满足需求
答题示例

InputSystem 采用事件驱动架构,通过配置文件解耦输入逻辑与业务代码,支持多设备自动切换、组合键、长按等复杂交互,适合多平台项目。InputManager 是轮询式 API,代码耦合度高。新项目建议使用 InputSystem,尤其是需要多设备或本地多人支持的场景。

参考文章
  • 1.概述.md
  • 2.InputSystem导入.md

2. PlayerInput 的四种行为执行模式

题目

PlayerInput 组件提供了哪四种行为执行模式?请简述每种模式的特点和适用场景。

深入解析

PlayerInput 是 InputSystem 提供的输入处理组件,通过 Behavior 属性决定如何将输入事件分发给业务逻辑。

模式 特点 适用场景
Send Messages 通过 SendMessage 调用同名函数,脚本需挂载在同一对象 简单项目,快速原型
Broadcast Messages 通过 BroadcastMessage 向子对象广播,脚本可挂载子对象 玩家对象有层级结构
Invoke Unity Events Inspector 拖拽关联函数,参数为 CallbackContext 需要关联其他对象函数
Invoke C Sharp Events C# 事件监听,代码完全控制 正式项目,性能最优

性能对比:SendMessage/BroadcastMessage 使用反射,性能较低;Unity Events 和 C# Events 直接调用,效率更高。

函数命名规则

  • Send/Broadcast Messages:函数名必须为 On + 行为名,参数为 InputValue 或无参数
  • Unity/C# Events:函数名自定义,参数为 InputAction.CallbackContext
答题示例

四种模式:Send Messages 通过反射调用同名函数,简单但性能低;Broadcast Messages 类似但支持子对象;Invoke Unity Events 通过 Inspector 拖拽关联,灵活直观;Invoke C Sharp Events 通过代码注册事件,性能最优、控制力最强。正式项目推荐后两种。

参考文章
  • 11.PlayerInput-认识PlayerInput.md
  • 12.PlayerInput-PlayerInput行为执行模式.md

3. InputAction 的 Action Type 有哪几种

题目

InputAction 的 Action Type 有哪几种?它们之间有什么区别?

深入解析

Action Type 决定了 InputAction 如何处理多设备绑定时的输入分发。

Action Type 特点 典型用途
Value 连续值输入,多设备绑定时只发送最受控制设备的输入 移动、鼠标位置、摇杆
Button 按钮类型,每次按下触发 开火、跳跃、确认
Pass Through 与 Value 类似,但发送所有绑定设备的输入 多手柄同时输入

Value vs Pass Through 关键区别

  • Value:多个手柄绑定时,只有”主手柄”的输入会触发事件
  • Pass Through:所有手柄的输入都会触发事件,适合需要同时响应多设备的场景
答题示例

Action Type 分三种:Value 用于连续值输入如移动,多设备时只响应主设备;Button 用于按钮触发如开火;Pass Through 与 Value 类似但会响应所有绑定设备。选择时根据是否需要多设备同时输入决定。

参考文章
  • 8.InputAction类.md

进阶题

1. InputAction 的 Interactions 机制

题目

InputAction 的 Interactions 有什么作用?请举例说明 Hold、Tap、MultiTap 三种交互的使用场景和触发条件。

深入解析

Interactions 用于实现复杂输入交互,如长按、双击等。每种 Interaction 都有 started、performed、canceled 三个事件阶段。

Interaction 触发条件 典型场景
Hold 按住时间 ≥ Hold Time 触发 performed,否则 canceled 技能蓄力、长按开镜
Tap 按下后在 Max Tap Duration 内松开触发 performed 快速点击确认
MultiTap 在指定时间内完成 Tap Count 次点击 双击翻滚、三连击连招

事件流程

按下 → started
      ↓
满足条件 → performed
      ↓
不满足条件 → canceled

Hold 示例:设置 Hold Time = 0.5 秒

  • 按下瞬间触发 started
  • 按住 0.5 秒触发 performed
  • 不足 0.5 秒松开触发 canceled

MultiTap 示例:设置 Tap Count = 2,Max Tap Duration = 0.3 秒

  • 第一次点击:started → performed(单次 Tap 完成)
  • 第二次点击(间隔 < Max Tap Spacing):performed(双击完成)
  • 超时未完成第二次点击:canceled
答题示例

Interactions 用于实现长按、双击等复杂交互。Hold 在按住指定时间后触发,适合蓄力技能;Tap 在短时间内按下松开触发,适合快速确认;MultiTap 检测多次点击,适合双击翻滚等。每种交互都有 started、performed、canceled 三个阶段,可通过 context.phase 判断当前状态。

参考文章
  • 8.InputAction类.md

2. PlayerInputManager 如何实现本地多人输入

题目

如何使用 PlayerInputManager 实现本地多人游戏?请说明玩家加入机制和分屏功能的实现方式。

深入解析

PlayerInputManager 是本地多人输入的管理器,负责玩家的动态加入和离开。

玩家加入机制(Join Behavior)

模式 说明
Join Players When Button Is Pressed 任意键按下时加入新玩家
Join Players When Join Action Is Triggered 指定按键触发加入
Join Players Manually 手动调用 API 加入

关键配置

  • Player Prefab:预制体必须挂载 PlayerInput 组件
  • Limit Number Of Players:限制最大玩家数
  • Enable Split-Screen:启用分屏

分屏实现

  1. 玩家预制体挂载 Camera 组件
  2. 勾选 Enable Split-Screen
  3. PlayerInputManager 自动为每个玩家分配屏幕区域

事件监听

PlayerInputManager.instance.onPlayerJoined += (player) => { /* 玩家加入 */ };
PlayerInputManager.instance.onPlayerLeft += (player) => { /* 玩家离开 */ };

设备自动分配:PlayerInputManager 会根据输入设备类型自动分配控制权,键盘鼠标控制一个玩家,不同手柄控制不同玩家。

答题示例

PlayerInputManager 通过 Join Behavior 控制玩家加入时机,支持任意键加入或指定按键加入。预制体需挂载 PlayerInput 组件,启用 Split-Screen 后自动为每个玩家分配屏幕区域。通过 onPlayerJoined/onPlayerLeft 事件监听玩家进出,设备会自动分配给对应玩家。

参考文章
  • 13.PlayerInputManager.md

3. 如何实现改键功能

题目

请说明如何基于 InputSystem 实现游戏内的改键功能。

深入解析

改键的核心思路是动态修改 InputActionAsset 的绑定路径。

实现方案

  1. 准备配置文件:将 .inputactions 文件中的 path 用占位符替换
"path": "<fire>"  // 占位符
  1. 定义键位数据结构
public class InputInfo
{
    public string fire = "<Mouse>/leftButton";
    public string jump = "<Keyboard>/space";
}
  1. 运行时替换并加载
string json = Resources.Load<TextAsset>("InputConfig").text;
json = json.Replace("<fire>", inputInfo.fire);
InputActionAsset asset = InputActionAsset.FromJson(json);
playerInput.actions = asset;
playerInput.actions.Enable();
  1. 获取新按键
InputSystem.onAnyButtonPress.CallOnce((control) =>
{
    string path = control.path; // 如 /Keyboard/q
    // 转换为 <Keyboard>/q 格式并更新 InputInfo
});

注意事项

  • 需要处理键位冲突
  • 改键后需重新启用 actions
  • 可结合数据持久化保存配置
答题示例

改键通过动态修改 InputActionAsset 的绑定路径实现。核心步骤:1. 配置文件中用占位符标记可变键位;2. 运行时用 InputSystem.onAnyButtonPress 获取新按键路径;3. 替换 JSON 中的占位符并通过 FromJson 重新加载;4. 赋值给 PlayerInput 并启用。需注意键位冲突和数据持久化。

参考文章
  • 16.综合练习题_补充知识点_获取任意键输入的信息.md
  • 17.综合练习题_补充知识点_通过Json数据加载配置文件.md
  • 18.综合练习题_改键练习.md

深度题

1. InputSystem 的事件驱动机制与性能优化

题目

InputSystem 的事件驱动机制与旧输入系统的轮询机制有什么本质区别?在性能优化方面有哪些注意事项?

深入解析

架构对比

机制 InputManager(轮询) InputSystem(事件驱动)
检测方式 每帧主动查询输入状态 输入事件触发回调
性能特点 无输入时仍消耗 CPU 无输入时零开销
代码结构 输入逻辑散落在 Update 输入逻辑集中在回调
延迟 取决于检测频率 取决于事件分发速度

性能优化要点

  1. 选择合适的更新模式

    • Process Events In Dynamic Update:默认,适合大多数情况
    • Process Events In Fixed Update:与物理同步,适合物理相关输入
    • Process Events Manually:手动控制,适合需要精确控制输入时机的场景
  2. 避免在回调中执行耗时操作

    • 回调中只做状态标记,具体逻辑在 Update 中处理
    • 避免在 performed 回调中进行复杂计算或 IO 操作
  3. 合理使用 Action Type

    • Value 类型适合连续输入,但会持续触发事件
    • Button 类型只在按下/松开时触发,开销更低
  4. 禁用未使用的 ActionMap

    input.actions.FindActionMap("Menu").Disable();
    
  5. Control Type 筛选

    • 设置正确的 Control Type 可减少无效设备绑定

内存优化

  • 使用 Generate C# Class 生成强类型代码,避免运行时反射
  • 及时取消事件订阅,避免内存泄漏
答题示例

InputSystem 采用事件驱动,输入触发时才执行回调,无输入时零开销,相比轮询机制更高效。优化要点:选择合适的 Update Mode;回调中避免耗时操作;合理选择 Action Type,Button 比 Value 开销低;禁用未使用的 ActionMap;使用生成代码避免反射;及时取消事件订阅防止内存泄漏。

参考文章
  • 8.InputAction类.md
  • 7.代码检测输入-其他输入.md

2. 多设备输入同步与冲突处理

题目

在支持多输入设备(键盘鼠标、手柄、触屏)的游戏中,如何处理设备切换和输入冲突?

深入解析

设备切换处理

  1. 自动切换方案

    • PlayerInput 的 Auto-Switch 功能可自动响应设备切换
    • 配置文件中为不同控制方案设置对应绑定
  2. 手动监听设备变化

    playerInput.onControlsChanged += (input) =>
    {
        // 设备切换时更新 UI 提示
    };
    
  3. 设备丢失处理

    playerInput.onDeviceLost += (input) =>
    {
        // 暂停游戏,提示重新连接
    };
    playerInput.onDeviceRegained += (input) =>
    {
        // 恢复游戏
    };
    

输入冲突处理

  1. Value vs Pass Through 选择

    • Value:多设备时只响应主设备,避免冲突
    • Pass Through:需要同时响应多设备时使用
  2. 控制方案隔离

    • 在 .inputactions 中为不同设备创建独立控制方案
    • 绑定时指定 Control Scheme
  3. 优先级处理

    • 使用 Which Side Wins 参数处理同时按下
    • Neither:取中间值;Positive/Negative:优先某方向
  4. UI 与游戏输入分离

    • 使用 InputSystemUIInputModule 处理 UI 输入
    • 游戏逻辑输入与 UI 输入使用不同 ActionMap

触屏特殊处理

  • 触屏设备需要判空 if (Touchscreen.current == null) return;
  • 多点触控使用 touchscreen.touches 数组遍历
答题示例

多设备处理:1. 使用 PlayerInput 的 Auto-Switch 自动切换;2. 监听 onControlsChanged/onDeviceLost 响应设备变化;3. 选择正确的 Action Type,Value 避免多设备冲突,Pass Through 支持同时输入;4. 为不同设备配置独立 Control Scheme;5. UI 与游戏输入使用不同 ActionMap 分离;6. 触屏需判空并处理多点触控。

参考文章
  • 11.PlayerInput-认识PlayerInput.md
  • 12.PlayerInput-PlayerInput行为执行模式.md
  • 13.PlayerInputManager.md
  • 14.UGUI配合使用.md


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏