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示例
- 获取PlayerInput组件:
PlayerInput input = this.GetComponent<PlayerInput>();
- 添加委托函数:
input.onDeviceLost += OnDeviceLost;
input.onDeviceRegained += OnDeviceRegained;
input.onControlsChanged += OnControlsChanged;
input.onActionTriggered += OnActionTrigger;
- 处理事件:
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>().x、callbackContext.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使用示例
- 创建预制体方块,添加PlayerInput组件和脚本,创建配置文件并关联到预制体的PlayerInput组件,再关联到PlayerInputManger的玩家预制体上(预制体需有PlayerInput组件)。
- 调整方块预制体对象输入行为为手动关联模式,关联脚本上的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;
}
- 选择模式(如按A键和手柄开始键创建新预制体),PlayerInputManager自动处理输入控制。
- 让方块预制体挂载摄像机,勾选分屏选项实现分屏效果。
- 使用单例获取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:启用分屏
分屏实现:
- 玩家预制体挂载 Camera 组件
- 勾选 Enable Split-Screen
- PlayerInputManager 自动为每个玩家分配屏幕区域
事件监听:
PlayerInputManager.instance.onPlayerJoined += (player) => { /* 玩家加入 */ };
PlayerInputManager.instance.onPlayerLeft += (player) => { /* 玩家离开 */ };
设备自动分配:PlayerInputManager 会根据输入设备类型自动分配控制权,键盘鼠标控制一个玩家,不同手柄控制不同玩家。
答题示例
PlayerInputManager 通过 Join Behavior 控制玩家加入时机,支持任意键加入或指定按键加入。预制体需挂载 PlayerInput 组件,启用 Split-Screen 后自动为每个玩家分配屏幕区域。通过 onPlayerJoined/onPlayerLeft 事件监听玩家进出,设备会自动分配给对应玩家。
参考文章
- 13.PlayerInputManager.md
3. 如何实现改键功能
题目
请说明如何基于 InputSystem 实现游戏内的改键功能。
深入解析
改键的核心思路是动态修改 InputActionAsset 的绑定路径。
实现方案:
- 准备配置文件:将 .inputactions 文件中的 path 用占位符替换
"path": "<fire>" // 占位符
- 定义键位数据结构:
public class InputInfo
{
public string fire = "<Mouse>/leftButton";
public string jump = "<Keyboard>/space";
}
- 运行时替换并加载:
string json = Resources.Load<TextAsset>("InputConfig").text;
json = json.Replace("<fire>", inputInfo.fire);
InputActionAsset asset = InputActionAsset.FromJson(json);
playerInput.actions = asset;
playerInput.actions.Enable();
- 获取新按键:
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 | 输入逻辑集中在回调 |
| 延迟 | 取决于检测频率 | 取决于事件分发速度 |
性能优化要点:
选择合适的更新模式
- Process Events In Dynamic Update:默认,适合大多数情况
- Process Events In Fixed Update:与物理同步,适合物理相关输入
- Process Events Manually:手动控制,适合需要精确控制输入时机的场景
避免在回调中执行耗时操作
- 回调中只做状态标记,具体逻辑在 Update 中处理
- 避免在 performed 回调中进行复杂计算或 IO 操作
合理使用 Action Type
- Value 类型适合连续输入,但会持续触发事件
- Button 类型只在按下/松开时触发,开销更低
禁用未使用的 ActionMap
input.actions.FindActionMap("Menu").Disable();Control Type 筛选
- 设置正确的 Control Type 可减少无效设备绑定
内存优化:
- 使用 Generate C# Class 生成强类型代码,避免运行时反射
- 及时取消事件订阅,避免内存泄漏
答题示例
InputSystem 采用事件驱动,输入触发时才执行回调,无输入时零开销,相比轮询机制更高效。优化要点:选择合适的 Update Mode;回调中避免耗时操作;合理选择 Action Type,Button 比 Value 开销低;禁用未使用的 ActionMap;使用生成代码避免反射;及时取消事件订阅防止内存泄漏。
参考文章
- 8.InputAction类.md
- 7.代码检测输入-其他输入.md
2. 多设备输入同步与冲突处理
题目
在支持多输入设备(键盘鼠标、手柄、触屏)的游戏中,如何处理设备切换和输入冲突?
深入解析
设备切换处理:
自动切换方案
- PlayerInput 的 Auto-Switch 功能可自动响应设备切换
- 配置文件中为不同控制方案设置对应绑定
手动监听设备变化
playerInput.onControlsChanged += (input) => { // 设备切换时更新 UI 提示 };设备丢失处理
playerInput.onDeviceLost += (input) => { // 暂停游戏,提示重新连接 }; playerInput.onDeviceRegained += (input) => { // 恢复游戏 };
输入冲突处理:
Value vs Pass Through 选择
- Value:多设备时只响应主设备,避免冲突
- Pass Through:需要同时响应多设备时使用
控制方案隔离
- 在 .inputactions 中为不同设备创建独立控制方案
- 绑定时指定 Control Scheme
优先级处理
- 使用 Which Side Wins 参数处理同时按下
- Neither:取中间值;Positive/Negative:优先某方向
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