Unity新版InputSystem全解析(2)
目录
Player Input Manager
Ugui中使用
Input Debug
InputSystem中专用于任意键按下的方案
通过Json数据加载配置文件
改键练习
Player Input Manager
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: 如果该值大于零,则 PlayerInputManager 始终将屏幕分割为固定数量的矩形,而不考虑实际的玩家数量。
Screen Rectangle: 可用于分配播放器拆分屏幕的规范化屏幕矩形
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;public class lesson3_8 : MonoBehaviour
{private PlayerInputManager playerInputManager;// Start is called before the first frame updatevoid Start(){//得到PlayerInputManagerplayerInputManager = PlayerInputManager.instance;//玩家加入时playerInputManager.onPlayerJoined += (playerInput) =>{print("创建一个玩家");};//玩家离开时playerInputManager.onPlayerLeft += (playerInput) =>{print("离开一个玩家");};}// Update is called once per framevoid Update(){}
}
Ugui中使用
InputSystem对UI的支持
-
新输入系统 InputSystem 不支持 IMGUI(GUI)注意:编辑器代码不受影响 如果当前激活的是 InputSystem,那么 OnGUI 中的输入判断相关内容不会被触发 你必须要选择 Both 或者只激活老输入系统 InputManager 才能让 OnGUI 中内容有用
-
新输入系统支持 UGUI,但是需要使用新输入系统输入模块(Input System UI Input Module)

Move Repeat Delay 生成初始 MoveHandler 之间的初始延迟(秒)。OnMove 导航事件,并在移动操作保持激活状态时生成重复的导航事件。
Move Repeat Rate 移动动作保持激活时,生成重复导航事件之间的间隔(秒)。请注意,这是由帧速率限制的;每帧不会有多个移动重复事件,因此,如果帧速率低于重复率,则有效重复率将低于此设置。
XR Tracking Origin
Deselect On Background Click 默认情况下,当指针被点击并且没有击中任何游戏对象时,当前选择被清除。然而,这可能会妨碍键盘和游戏板导航,因为它们需要关闭当前选定的对象。要防止自动取消选择,请将此属性设置为 false。
Single Mouse Or Pen But Multi Touch And Track
对于来源于触摸或跟踪输入的所有输入,其行为类似于单个统一指针,但于分类为触摸输入,其行为于所有指针。例如,如果在鼠标和笔上接收到并发输入,则将两者的输入馈送到同一 UI 指针实例中。其中一个的位置输入将覆盖另一个的位置。请注意,当从触摸或跟踪设备接收到输入时,鼠标和笔的单个统一指针将被删除,包括在鼠标 / 笔光标当前悬停在对象上时发送的 PointerExit 事件。
Single Unified Pointer
所有指针输入都是统一的,因此只有一个指针。这包括触摸和跟踪输入。例如,这意味着,无论有多少设备将输入输入输入点,只有帧中最后一个这样的输入才会生效并成为当前 UI 指针的位置。
All Pointers As Is
所有输入根本不统一任何指针输入。任何设备,包括提供输入指针类型动作的触摸和跟踪设备,都将是其自己的指针(或触摸输入的多个指针)。注意:这可能意味着 UI 中将有任意数量的指针,并且可能同时指向多个对象。
Pointer Behavior 如何处理将输入送入 UI 的多个指针
Actions Asset 包含控制 UI 的所有操作的输入操作资产。您可以使用以下属性选择资产中的哪些操作对应于哪些 UI 输入。默认情况下,这将引用名为 DefaultInputActions 的内置资产,该资产包含用于驱动 UI 的常见默认操作。如果要设置自己的操作,请创建自定义输入操作资源并在此处分配。在 Inspector 中将资源引用指定给此字段时,编辑器会尝试根据常用命名约定自动将操作映射到 UI 输入。
Point 提供 2D 屏幕位置的动作。用作指向 UI 元素的光标,以实现鼠标样式的 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 位置的动作。结合跟踪设备方向,这允许通过指向空间中的 UI 可选择项进行 XR 样式的 UI 交互。设置为传递操作类型和向量 3 值类型。
Tracked Orientation 传递表示一个或多个空间跟踪设备(如 XR hand 控制器)旋转的四元数的操作。结合跟踪设备位置,这允许通过指向空间中的 UI 可选择项进行 XR 样式的 UI 交互。
多人游戏使用多套 UI
如果同一设备上的多人游戏,每个人想要使用自己的一套独立 UI
需要将 EventSystem 中的 EventSystem 组件替换为 Multiplayer Event System 组件
与 EventSystem 组件不同,可以在场景中同时激活多个 MultiplayerEventSystem。
这样,您可以有多个玩家,每个玩家都有自己的 InputSystemUIInputModule 和 MultiplayerEventSystem 组件
每个玩家都可以有自己的一组操作来驱动自己的 UI 实例。
如果您正在使用 PlayerInput 组件,还可以设置 PlayerInput 以自动配置玩家的 InputSystemUIInputModule 以使用玩家的操作
MultiplayerEventSystem 组件的属性与事件系统中的属性相同
此外,MultiplayerEventSystem 组件还添加了一个 playerRoot 属性,您可以将其设置为一个游戏对象
该游戏对象包含此事件系统应在其层次结构中处理的所有 UI 可选择项
On-Screen组件相关
On-Screen组件可以模拟UI和用户操作的交互
1.On-Screen Button:按钮交互
2.On-Screen Stick:摇杆交互
Input Debug
InputDebug 顾名思义是输入调试器的意思
我们可以通过输入调试窗口检测输入相关信息
当我们的输入不按预期工作时,可以通过它来排查问题
打开方式 Window -> Analysis -> Input Debugger


Add Devices Not Listed in Supported Devices添加未在支持的设备中列出的设备
Enable Event Diagnostics启用事件诊断
Simulate Touch Input From Mouse or Pen模拟鼠标或笔的触摸输入
Remote Devices 远程设备
Devices 系统中当前所有输入设备列表
Unsupported 不支持、无法识别的设备列表
Layouts 设备布局列表所有已注册的设备控制列表
Abstract Devices 抽象设备
Specific Devices 特定设备
Settings 设置相关默认的一些特殊输入设置
Metrics 指标相关 输入系统资源使用情况的统计信息
运行时界面

运行时双击设备

InputSystem中专用于任意键按下的方案
InputSystem.onAnyButtonPress.CallOnce((control)=>{printf(control.name);printf(control.path);});
通过Json数据加载配置文件
//通过Json手动加载输入配置文件playerInput = this.gameObject.GetComponent<PlayerInput>();InputActionAsset inputActions = InputActionAsset.FromJson(Resources.Load<TextAsset>("PlayerInfoTest").text);playerInput.actions = inputActions;playerInput.onActionTriggered += (obj) =>{if (obj.phase == InputActionPhase.Performed){switch (obj.action.name){case "Move":print("Move");break;case "Fire":print("Fire");break;case "Jump":print("Jump");break;}}};
改键练习
改键的原理就是修改inputactions文件,输入配置文件在本地存储的形式其实就是Json文件,如下
{"version": 1,"name": "My project (5)","maps": [{"name": "Player","id": "3700f80b-dd62-4f02-86ae-749471be4a42","actions": [{"name": "Move","type": "Value","id": "e284c4c0-9482-462f-b202-f84c040b8cdc","expectedControlType": "Vector2","processors": "","interactions": "","initialStateCheck": true},{"name": "Fire","type": "Button","id": "de2f7bcf-1f01-4020-94a4-c046c5216e3d","expectedControlType": "","processors": "","interactions": "","initialStateCheck": false}],"bindings": [{"name": "WASD","id": "00ca640b-d935-4593-8157-c05846ea39b3","path": "Dpad","interactions": "","processors": "","groups": "","action": "Move","isComposite": true,"isPartOfComposite": false},{"name": "up","id": "e2062cb9-1b15-46a2-838c-2f8d72a0bdd9","path": "<Keyboard>/w","interactions": "","processors": "","groups": ";Keyboard&Mouse","action": "Move","isComposite": false,"isPartOfComposite": true},{"name": "down","id": "320bffee-a40b-4347-ac70-c210eb8bc73a","path": "<Keyboard>/s","interactions": "","processors": "","groups": ";Keyboard&Mouse","action": "Move","isComposite": false,"isPartOfComposite": true},{"name": "left","id": "d2581a9b-1d11-4566-b27d-b92aff5fabbc","path": "<Keyboard>/a","interactions": "","processors": "","groups": ";Keyboard&Mouse","action": "Move","isComposite": false,"isPartOfComposite": true},{"name": "right","id": "fcfe95b8-67b9-4526-84b5-5d0bc98d6400","path": "<Keyboard>/d","interactions": "","processors": "","groups": ";Keyboard&Mouse","action": "Move","isComposite": false,"isPartOfComposite": true},{"name": "","id": "05f6913d-c316-48b2-a6bb-e225f14c7960","path": "<Mouse>/leftButton","interactions": "","processors": "","groups": ";Keyboard&Mouse","action": "Fire","isComposite": false,"isPartOfComposite": false}]}],"controlSchemes": []
}
改键的目的就是更改对应的“path”
string中的一个函数Replace可以将一个字符串替换为另一个新的字符串,那以开火为例我们可以将配置文件更改如下
"name": "","id": "05f6913d-c316-48b2-a6bb-e225f14c7960","path": "<fire>","interactions": "","processors": "","groups": ";Keyboard&Mouse","action": "Fire","isComposite": false,"isPartOfComposite": false
定义一个控制各个功能的对象用来存储当前按键的path
在玩家初始化的时候将json文件读取出来,用Replace将我们的默认按键和<fire>进行替换
然后通过InputActionAsset的FromJson方法将json文件格式化成InputActionAsset对象,赋值给当前对象的PlayerInput对象
改键的时候只需要修改控制各个功能的对象的path值,然后再调用InputActionAsset的FromJson方法重新格式化并赋值就可以实现改键
DataMgr 单例模式获取唯一的PlayerInfo对象和Json文件,为外部提供了获取输入配置文件的方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;public class DataMgr
{private static DataMgr instance = new DataMgr();public static DataMgr Instance => instance;private PlayerInfo playerInfo;public PlayerInfo InputInfo => playerInfo;private string jsonStr;private DataMgr() {playerInfo = new PlayerInfo();jsonStr = Resources.Load<TextAsset>("PlayerInfoTest").text;}public InputActionAsset GetActionAsset(){ string str = jsonStr.Replace("<up>",playerInfo.up);str = str.Replace("<down>", playerInfo.down);str = str.Replace("<left>", playerInfo.left);str = str.Replace("<right>", playerInfo.right);str = str.Replace("<fire>", playerInfo.fire);return InputActionAsset.FromJson(str);}
}
ChangeInputPanel UGUI负责修改按键和显示当前按键,每次修改调用InputSystemTest提供给外部的更新方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UI;public enum BTN_TYPE
{ UP,DOWN, LEFT, RIGHT,FIRE,
}
public class ChangeInputPanel : MonoBehaviour
{public Text txtUp;apublic Text txtDown;public Text txtLeft;public Text txtRight;public Text txtFire;public Button btnUp;public Button btnDown;public Button btnLeft;public Button btnRight;public Button btnFire;private BTN_TYPE nowType;public InputSystemTest inputSystem;void Start(){UpdateBtnInfo();btnUp.onClick.AddListener(() => {ChanegBtn(BTN_TYPE.UP);});btnDown.onClick.AddListener(() => {ChanegBtn(BTN_TYPE.DOWN);});btnLeft.onClick.AddListener(() => {ChanegBtn(BTN_TYPE.LEFT);});btnRight.onClick.AddListener(() => {ChanegBtn(BTN_TYPE.RIGHT);});btnFire.onClick.AddListener(() => {ChanegBtn(BTN_TYPE.FIRE);});}private void ChanegBtn(BTN_TYPE type){nowType = type;//得到一次任意键输入InputSystem.onAnyButtonPress.CallOnce(ChangeBtnReally);}private void ChangeBtnReally(InputControl control){string[] strs = control.path.Split('/');string temp = "<" + strs[1] + ">/" + strs[2];switch (nowType){case BTN_TYPE.UP:DataMgr.Instance.InputInfo.up = temp;break;case BTN_TYPE.DOWN:DataMgr.Instance.InputInfo.down = temp;break;case BTN_TYPE.LEFT:DataMgr.Instance.InputInfo.left = temp;break;case BTN_TYPE.RIGHT:DataMgr.Instance.InputInfo.right = temp;break;case BTN_TYPE.FIRE:DataMgr.Instance.InputInfo.fire = temp;break;default:break;}UpdateBtnInfo();inputSystem.ChangeInput();}private void UpdateBtnInfo(){txtUp.text = DataMgr.Instance.InputInfo.up;txtDown.text = DataMgr.Instance.InputInfo.down;txtLeft.text = DataMgr.Instance.InputInfo.left;txtRight.text = DataMgr.Instance.InputInfo.right;txtFire.text = DataMgr.Instance.InputInfo.fire;}// Update is called once per framevoid Update(){}
}
InputSystemTest 初始化对象和给外部提供更新输入配置文件的方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;public class InputSystemTest : MonoBehaviour
{private PlayerInput playerInput;private Vector3 dir;private Rigidbody rb;// Start is called before the first frame updatevoid Start(){playerInput = GetComponent<PlayerInput>();rb = GetComponent<Rigidbody>();playerInput.actions = DataMgr.Instance.GetActionAsset();playerInput.actions.Enable();playerInput.onActionTriggered += (obj) =>{if (obj.phase == InputActionPhase.Performed){switch (obj.action.name){case "Fire":RaycastHit hit;if (Physics.Raycast(Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()), out hit)){Vector3 point = hit.point;point.y = gameObject.transform.position.y;Vector3 dir = point - gameObject.transform.position;Instantiate(Resources.Load<GameObject>("Bullet"), gameObject.transform.position, Quaternion.LookRotation(dir));}break;}}};}public void ChangeInput(){playerInput.actions = DataMgr.Instance.GetActionAsset();playerInput.actions.Enable();}void Update(){dir = Vector3.zero;dir = playerInput.currentActionMap["Move"].ReadValue<Vector2>();dir.z = dir.y;dir.y = 0;rb.AddForce(dir * 3);}
}
PlayerInfo 用来存储当前各个功能的按键
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerInfo
{public string up = "<Keyboard>/w";public string down = "<Keyboard>/s";public string left = "<Keyboard>/a";public string right = "<Keyboard>/d";public string fire = "<Mouse>/leftButton";}
效果

