Unity 新的输入系统,InputSystem |
InputSystem1.0.2
入门
词语
ActionAsset
包含一个或多个动作映射以及可选的一系列控制方案的资产。
ActionMaps
一个命名的动作集合。
您可以同时启用或禁用动作映射中的所有动作,因此根据动作相关的上下文(例如:“游戏玩法”)对动作映射中的动作进行分组非常有用。
Action
响应输入触发回调的命名操作。
逻辑输入,例如“Jump”或“Fire”。即,玩家可以通过一个或多个输入设备触发并运行一段游戏逻辑作为响应的输入动作
Property
属性
Binding
一个动作和一个或多个控件之间的连接,由一个 控制路径. 在运行时,绑定被解析为产生零个或多个控件,然后输入系统将这些控件连接到操作
properties
path
控制路径标识操作应从中接收输入的控件。
overridePath
控制路径覆盖path. 不像path,overridePath不是持久化的,因此您可以使用它来非破坏性地覆盖 Binding 上的路径。如果将其设置为 null 以外的值,则它生效并覆盖path。要获取当前有效的路径(即path或overridePath),您可以查询effectivePath 财产。
绑定应触发的操作的名称或 ID。请注意,这可以为 null 或为空(例如,对于 复合材料)。不区分大小写。
例子:"fire"
绑定所属的绑定组的分号分隔列表。可以为 null 或为空。绑定组可以是任何东西,但主要用于控制方案. 不区分大小写。
例子:"Keyboard&Mouse;Gamepad"
interactions
分号分隔的列表 互动应用于此绑定的输入。请注意,Unity 附加了应用于行动本身(如果有的话)到这个列表。不区分大小写。
例子:"slowTap;hold(duration=0.75)"
processors
分号分隔的列表 处理器应用于此绑定的输入。请注意,Unity 将应用到的处理器附加到行动本身(如果有的话)到这个列表。不区分大小写。
除了提供值的控件上的处理器之外,绑定上的处理器也适用。例如,如果您将stickDeadzone处理器放在绑定上,然后将其绑定到<Gamepad>/leftStick,则会应用两次死区:一次来自位于leftStick控件上的死区处理器,一次来自绑定。
例子:"invert;axisDeadzone(min=0.1,max=0.95)"
id
绑定的唯一 ID。例如,在用户设置中存储绑定覆盖时,您可以使用它来识别绑定。
name
绑定的可选名称。识别内部零件名称复合材料.
例子:"Positive"
isComposite
绑定是否作为 合成的.
isPartOfComposite
Binding是否属于合成的
Composite Bindings
描述
目前,系统自带四种复合类型:一维轴、二维向量、带有一个修饰符的按钮和带有两个修饰符的按钮。
有时,您可能希望多个 Controls 协同工作以模仿不同类型的 Control。最常见的例子是使用键盘上的 W、A、S 和 D 键形成一个 2D 矢量控件,相当于鼠标增量或游戏手柄杆。另一个例子是使用两个键形成一个相当于鼠标滚动轴的一维轴
类型
一维轴
描述
由两个按钮组成的复合体:一个沿其负方向拉动 1D 轴,另一个沿其正方向拉动。在AxisComposite类中实现。结果是一个float.
bind
negative
Type:Button
控制向负方向拉动,(朝向 minValue)。
positive
Type:Button
控制向正方向(朝向 maxValue)。
轴复合设置参数
双方都没有优先权。综合收益之间的中点minValue,并maxValue作为一个结果。在它们的默认设置下,这是 0。
这是此设置的默认值。
(1) Positive
积极的一面优先,综合回报maxValue。
(2) Negative
负方优先,综合回报minValue。
minValue
返回的值,如果 negative侧被启动。默认值为 -1
maxValue
返回的值,如果 positive侧被启动。默认值为 1。
二维矢量
描述
代表 4 向按钮设置的复合材料,如游戏手柄上的方向键。每个按钮代表一个基本方向。在Vector2Composite类中实现。结果是一个Vector2.此 Composite 最适用于表示上下左右控件,例如 WASD 键盘输入。
bind
up
Type:Button
right
代表(1,0)(+X) 的控件。
down
代表(0,-1)(-Y) 的控件。
left
Type:Button
代表(-1,0)(-X) 的控件。
参数
mode
Mode.Analog
Mode.DigitalNormalized
输入被视为按钮(如果低于 defaultButtonPressPoint如果等于或大于,则打开)。每个输入为 0 或 1,具体取决于按钮是否被按下。由上/下/左/右部分产生的向量被归一化。结果是菱形的 2D 输入范围。
Mode.Digital
最后,如果设置为Mode.Analog, 输入被视为模拟(即全浮点值),并且除了 down 和 left被反转,值将按原样传递。
(Button with one modifier)2个组合操作
描述
一个复合,要求用户在按住另一个“修饰符”按钮的同时按下触发操作的按钮(例如,代表键盘快捷键,如 Shift+1)。在ButtonWithOneModifier类中实现。这些按钮可以位于任何设备上,可以是切换按钮或全范围按钮,例如游戏手柄触发器。
结果是一个float.
modifier
Type:Button
button
(Button with two modifiers)3个组合操作
如上
可以使用代码编写自定义组合操作
Interaction
以在控件上识别的独特输入模式。只有当输入系统识别出模式时,交互才会触发操作。
例如,一个“保持”交互需要一个控件被启动,然后在它触发相关的动作之前保持一段时间。
Phase
描述
描述交互当前状态的枚举。
类型
Waiting
交互正在等待输入。
Started
交互已开始(即,它收到了一些预期的输入),但尚未完成。
Performed
交互完成。
Canceled
交互被中断并中止。例如,用户按下然后释放按钮所需的最短时间之前保持互动 去完成。
Processor
输入系统应用于输入值的操作。例如,“反转”处理器反转浮点值。
Control Scheme
允许您定义绑定到不同控制方案的映射,并在不同的控制方案之间切换您的操作映射,以便为您的操作启用不同的绑定子集。控制方案可以有关联的设备类型,以便游戏可以自动启用它们用户 使用该类型的设备时。
编辑
快捷方式
Ctrl-X、Ctrl-C、Ctrl-V
剪切、复制和粘贴。可用于动作、动作映射和绑定
Ctrl-D
复制。可用于动作、动作映射和绑定。
Del
删除。可用于动作、动作映射和绑定。
Alt-S
保存。
Alt-M
添加动作地图。
Alt-A
添加动作。
Alt-B
添加绑定.
创建 Input Action Assets
Assets > Create > Input Actions
编辑 Input Action Assets
双击打开.inputactions
生成C#代码
编辑 Input Action Maps
动作映射名称不能包含斜杠 ( /)
编辑 Input Actions
编辑 Properties
编辑 Binding
编辑Binding Path
编辑 Composite Bindings
复合绑定是由多个部分组成的绑定,它们一起形成一个控件。例如,2D 矢量复合使用四个按钮(左、右、上、下)来模拟 2D 摇杆输入
编辑 Control Schemes
Input Action Assets 可以有多个Control Schemes,它允许您为不同类型的设备启用或禁用不同组的绑定
编辑Interactions
Press
Hold
Tap
SlowTap
MultiTap
inputActionAsset
代码
// Create free-standing Actions.
var lookAction = new InputAction("look", binding: "<Gamepad>/leftStick");
var moveAction = new InputAction("move", binding: "<Gamepad>/rightStick");
lookAction.AddBinding("<Mouse>/delta");
moveAction.AddCompositeBinding("Dpad")
.With("Up", "<Keyboard>/w")
.With("Down", "<Keyboard>/s")
.With("Left", "<Keyboard>/a")
.With("Right", "<Keyboard>/d");
// Create an Action Map with Actions.
var map = new InputActionMap("Gameplay");
var lookAction = map.AddAction("look");
lookAction.AddBinding("<Gamepad>/leftStick");
// Create an Action Asset.
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var gameplayMap = new InputActionMap("gameplay");
asset.AddActionMap(gameplayMap);
var lookAction = gameplayMap.AddAction("look", "<Gamepad>/leftStick");
json
{
"name" : "MyDevice",
"extend" : "Gamepad", // Or some other thing
"controls" : [
{
"name" : "firstButton",
"layout" : "Button",
"offset" : 0,
"bit": 0,
"format" : "BIT",
},
{
"name" : "secondButton",
"layout" : "Button",
"offset" : 0,
"bit": 1,
"format" : "BIT",
},
{
"name" : "axis",
"layout" : "Axis",
"offset" : 4,
"format" : "FLT",
"parameters" : "clamp=true,clampMin=0,clampMax=1"
}
]
}
scriptableObject
使用
组件
PlayerInput
描述
该PlayerInput组件提供了一种方便的方式来处理一个或多个玩家的输入。它要求您在输入操作资产中设置所有操作,然后您可以将其分配给PlayerInput组件。PlayerInput然后可以自动处理激活动作映射并为您选择控制方案。
字段
Actions
一套 输入动作与播放器相关联。要接收输入,每个玩家必须有一组关联的操作。
Default Control Scheme
哪个 控制方案 (从什么定义 Actions) 默认启用。
Default Action Map
哪个 行动地图 在 Actions默认启用。如果设置为None,则播放器开始时不会启用任何操作。
Camera
与玩家关联的单个摄像机。这仅在使用时需要分屏 设置,否则没有任何影响。
Behavior
PlayerInput组件如何通知游戏代码玩家发生的事情。
PlayerInputManager
描述
该Player Input系统有助于设置本地多人游戏,其中多个玩家共享具有单个屏幕和多个控制器的单个设备。使用PlayerInputManager组件进行设置,PlayerInput当玩家加入和离开游戏时,它会自动管理实例的创建和生命周期。
字段
Notification Behavior
Join Behavior
确定启用加入时玩家可以加入的机制
Player Prefab
代表游戏中玩家的预制件。这PlayerInputManager每当有新玩家加入时,组件都会创建此预制件的实例。这个预制件必须有一个PlayerInput 其层次结构中的组件
Joining Enabled By Default
启用此功能后,新玩家可以通过由确定的机制加入 Join Behavior.
Limit Number of Players
如果您想限制可以加入游戏的玩家数量,请启用此选项
Max Player Count(仅在Limit number of Players启用时显示。)
允许加入游戏的最大玩家数量。
Enable Split-Screen
如果启用,每个玩家都会自动分配到可用屏幕区域的一部分。
API(详见https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/Migration.html)
对应旧系统的API
宏
#if ENABLE_INPUT_SYSTEM
// New input system backends are enabled.
#endif
#if ENABLE_LEGACY_INPUT_MANAGER
// Old input backends are enabled.
#endif
// NOTE: Both can be true at the same time as it is possible to select "Both"
// under "Active Input Handling".
acceleration
UnityEngine.Input.acceleration
使用Accelerometer.current.acceleration.ReadValue().
UnityEngine.Input.accelerationEventCount
见UnityEngine.Input.accelerationEvents。
UnityEngine.Input.accelerationEvents
加速事件不能与其他输入事件分开使用。以下代码跟踪Accelerometer.current设备上的所有输入事件。
private InputEventTrace trace;
void StartTrace()
{
trace = new InputEventTrace() {deviceId = Accelerometer.current.deviceId};
trace.Enable();
}
void Update()
{
foreach (var e in trace)
{
//...
}
trace.Clear();
}
Input.anyKey
对于键盘键,请使用:
Keyboard.current.anyKey.isPressed
对于鼠标按钮,请使用:
Mouse.current.leftButton.isPressed
Mouse.current.rightButton.isPressed
Mouse.current.middleButton.isPressed
UnityEngine.Input.anyKeyDown
利用 Keyboard.current.anyKey.wasUpdatedThisFrame
Input.compensateSensors
UnityEngine.Input.compensateSensors
使用InputSystem.settings.compensateForScreenOrientation.
UnityEngine.Input.compositionCursorPos
使用Keyboard.current.SetIMECursorPosition(myPosition).
UnityEngine.Input.compositionString
订阅Keyboard.onIMECompositionChange事件:
Input.gyro
UnityEngine.Input.gyro
UnityEngine.Gyroscope在新的输入系统中,该类被多个独立的传感器设备取代:
Gyroscope 来测量角速度。
GravitySensor 来测量重力方向。
AttitudeSensor 来测量设备的方向。
Accelerometer 测量施加到设备上的总加速度。
LinearAccelerationSensor 测量施加到设备上的加速度,以补偿重力。
UnityEngine.Input.gyro.attitude
使用AttitudeSensor.current.orientation.ReadValue().
UnityEngine.Input.gyro.enabled
UnityEngine.Input.gyro.gravity
使用GravitySensor.current.gravity.ReadValue().
UnityEngine.Input.gyro.rotationRate
使用Gyroscope.current.angularVelocity.ReadValue().
UnityEngine.Input.gyro.updateInterval
使用Sensor.samplingFrequency:
UnityEngine.Input.gyro.userAcceleration
使用LinearAccelerationSensor.current.acceleration.acceleration.ReadValue().
...
还没有的API
UnityEngine.Input.backButtonLeavesApp
还没有对应的API。
UnityEngine.Input.compass
还没有对应的API。
UnityEngine.Input.deviceOrientation
还没有对应的API。
UnityEngine.Input.gyro.rotationRateUnbiased
还没有对应的API。
...
进阶
控件(Control)
层次结构
控件可以形成层次结构。Control 层次结构的根始终是Device。
类型
所有控件都基于InputControl基类.大多数具体实现都基于InputControl<TValue>.
AxisControl
一维浮点轴
Gamepad.leftStick.x
ButtonControl
表示为浮点值的按钮。按钮是否可以具有 0 或 1 以外的值取决于底层表示。例如,游戏手柄触发器按钮可以具有 0 和 1 以外的值,但游戏手柄面部按钮通常不能。
Mouse.leftButton
KeyControl
一个专门的按钮,代表一个键 Keyboard. 键有一个关联keyCode并且,与其他类型的控件不同,根据当前活动的系统范围键盘布局更改其显示名称。
Keyboard.aKey
Vector2Control
一个二维浮点向量。
Pointer.position
Vector3Control
一个 3D 浮点向量。
Accelerometer.acceleration
QuaternionControl
3D 旋转。
AttitudeSensor.attitude
IntegerControl
一个整数值。
Touchscreen.primaryTouch.touchId
StickControl
2D 摇杆控件,如游戏手柄上的拇指摇杆或操纵杆的摇杆控件。
Gamepad.rightStick
DpadControl
一个 4 向按钮控件,如游戏手柄上的方向键或操纵杆上的帽子开关。
Gamepad.dpad
TouchControl
表示触摸的所有属性的控件触摸屏
Touchscreen.primaryTouch
用法
用法是一个字符串,表示控件的预期用途
件用法的一个示例是Submit,它标记一个控件,该控件通常用于确认 UI 中的选择。在游戏手柄上,这种用法常见于buttonSouth控件上。
您可以使用该InputControl.usages属性访问控件的用法
路径
示例:<Gamepad>/leftStick/x表示“游戏手柄左摇杆上的 X 控制”
输入系统可以使用文本路径查找控件。
输入操作上的绑定依赖此功能来识别它们从中读取输入的控件。
var gamepad = Gamepad.all[0];
var leftStickX = gamepad["leftStick/x"];
var submitButton = gamepad["{Submit}"];
var allSubmitButtons = InputSystem.FindControls("*/{Submit}");
每个组件都使用由多个字段组成的类似语法。
每个字段都是可选的,但必须至少存在一个字段。所有字段都不区分大小写。
<layoutName>{usageName}controlName#(displayName)
字段
<layoutName>
要求当前级别的控件基于给定的布局。控件的实际布局可能是相同的,也可能是基于给定布局的布局。
<Gamepad>/buttonSouth
控件和设备的工作方式不同。
当在设备(路径的第一个组件)上使用时,它要求设备具有给定的用法。看设备用途更多细节。
为了查找控件,使用字段当前仅限于紧跟在设备之后的路径组件(路径中的第二个组件)。它找到具有给定用法的设备上的控件。控件可以位于设备的控件层次结构中的任何位置。
设备:
<XRController>{LeftHand}/trigger
控制:
<Gamepad>/{Submit}
controlName
要求当前级别的控件具有给定的名称。采用两个“正确”名称(InputControl.name) 和别名 (InputControl.aliases) 考虑在内。
此字段也可以是通配符 ( *) 以匹配任何名称。
MyGamepad/buttonSouth
*/{PrimaryAction}(匹配PrimaryAction使用任何名称的设备)
#(displayName)
要求当前级别的控件具有给定的显示名称(即 InputControl.displayName)。显示名称可能包含空格和符号。
<Keyboard>/#(a) (根据当前的键盘布局匹配生成“a”字符的键,如果有的话)。
<Gamepad>/#(Cross)
状态
每个 Control 都连接到一个内存块,该内存块被认为是 Control 的“状态”。您可以通过InputControl.stateBlock属性从 Control 中查询此内存块的大小、格式和位置。
您可以通过其ReadValue方法访问控件的当前状态。
Gamepad.current.leftStick.x.ReadValue();
每种类型的 Control 都有其返回的特定类型的值,而不管它为其状态支持多少种不同类型的格式。您可以通过InputControl.valueType属性访问此值类型
记录状态历史
要记录状态随时间的变化,您可以使用InputStateHistory或InputStateHistory<TValue>。后者将控件限制为特定值类型的控件,从而简化了一些 API
驱动
当 Control 已从其默认状态移开从而影响 Control 的实际值时,它被视为已启动。您可以使用 查询当前是否启动了控件IsActuated。
// Check if leftStick is currently actuated.
if (Gamepad.current.leftStick.IsActuated())
Debug.Log("Left Stick is actuated");
嘈杂的控制
输入系统可以将控件标记为“嘈杂”。您可以使用该InputControl.noisy属性进行查询 。
嘈杂控件是那些无需任何实际或有意的用户交互即可更改值的控件。
一个很好的例子是手机中的重力传感器。即使手机完全静止,重力读数通常也会出现波动。另一个例子是来自 HMD 的方向读数。
注意:如果设备上的任何控件有噪音,则设备本身会被标记为有噪音。
合成控件
合成控制是一种控制不对应于实际的物理控制的设备上
(例如left,right,up,和down子上的控制StickControl)。
这些控件合成来自其他实际物理控件的输入并以不同的方式呈现
给定的 Control 是否是合成的由其InputControl.synthetic属性指示。
系统考虑用于交互式重新绑定的合成控件,但始终支持非合成控件。如果潜在匹配的合成控件和非合成控件都存在,则默认情况下非合成控件获胜。<Gamepad>/leftStick/left例如,这使得交互式绑定到成为可能,但也可以在<Gamepad>/leftStickPress不受摇杆上合成按钮干扰的情况下进行绑定。
处理器(Processors)
描述
输入处理器接受一个值并为它返回一个处理过的结果。
接收值和结果值必须是同一类型。
例如,您可以使用Clamp处理器将控件中的值钳制到某个范围。
使用处理器
bindings
当您为您的操作创建绑定时,您可以选择将处理器添加到绑定中。在系统将它们应用于 Action 值之前,它们会处理来自它们绑定到的控件的值。例如,Vector2在将这些值传递给驱动应用程序输入逻辑的 Action 之前,您可能希望沿 Y 轴反转来自控件的值
actions
动作处理器的工作方式与绑定处理器的工作方式相同,但它们影响绑定到动作的所有控件,而不仅仅是来自特定绑定的控件。
如果绑定和操作上都有处理器,则系统首先处理绑定中的处理器。
创建
您可以使用与绑定相同的方式在输入操作资产中的操作上添加和编辑处理器:选择要编辑的操作,然后在右侧窗格中添加一个或多个处理器
另一种代码创建:var action = new InputAction(processors: "invertVector2(invertX=false)");
controls
您可以直接在 上拥有任意数量的处理器InputControl,然后处理从控件读取的值。每当您调用ReadValue控件时,该控件上的所有处理器都会在值返回给您之前对其进行处理。您可以ReadUnprocessedValue在控件上使用绕过处理器。
如果在控件的布局中指定了处理器,则输入系统会在设备创建期间将处理器添加到控件中。在创建现有控件后,您无法将它们添加到现有控件中,因此您只能在创建自定义设备时将处理器添加到控件中。输入系统开箱即用支持的设备已经在其控件上添加了一些有用的处理器。例如,游戏手柄上的摇杆有一个摇杆死区处理器。
如果您使用输入系统从状态结构生成的布局using InputControlAttributes,则可以通过processors属性的属性指定要使用的处理器
public struct MyDeviceState : IInputStateTypeInfo
{
public FourCC format => return new FourCC('M', 'Y', 'D', 'V');
// Add an axis deadzone to the Control to ignore values
// smaller then 0.2, as our Control does not have a stable
// resting position.
[InputControl(layout = "Axis", processors = "AxisDeadzone(min=0.2)")]
public short axis;
}
如果您从 JSON 创建布局,您可以在您的控件上指定处理器
{
"name" : "MyDevice",
"extend" : "Gamepad", // Or some other thing
"controls" : [
{
"name" : "axis",
"layout" : "Axis",
"offset" : 4,
"format" : "FLT",
"processors" : "AxisDeadzone(min=0.2)"
}
]
}
预定义处理器
默认
自定义
设备
类型
Other
XR HMD
XR Controller
TrackedDevice
Touchscreen
Sensor
Pointer
Pen
Mouse
Keyboard
Joystick
Gamepad
API
事件
描述
输入系统是事件驱动的。所有输入都作为事件传递,您可以通过注入事件生成自定义输入。您还可以通过监听流经系统的事件来观察所有源输入。
注意:事件是输入系统的一项高级功能,主要是内部功能。如果您想支持自定义设备或更改现有设备的行为,则事件系统的知识非常有用。
输入事件是一种低级机制。通常,如果您只想接收应用程序的输入,则不需要处理事件。
事件存储在非托管内存缓冲区中,不会转换为 C# 堆对象。
输入系统提供了包装 API,但更多涉及的事件操作需要不安全的代码。
请注意,没有路由机制。
运行时直接将事件传递给输入系统,然后输入系统将它们直接合并到设备状态中。
输入事件由InputEvent结构表示。
每个事件都有一组通用属性
type
FourCC 指示事件类型的代码。
eventId
事件的唯一数字 ID。
time
事件生成的时间戳。
deviceId
事件所针对的设备的 ID。
sizeInBytes
事件的总大小(以字节为单位)。
Types of events
State events
StateEvent( 'STAT')
StateEvent包含特定于该设备的格式的设备整个状态的完整快照。该stateFormat字段标识事件中数据的类型。您可以使用state指针 和访问原始数据stateSizeInBytes。
DeltaStateEvent( 'DLTA')
ADeltaStateEvent类似于 a StateEvent,但仅包含设备状态的部分快照。输入系统通常为需要大量状态记录的设备发送此信息,以减少在只有某些控件更改其状态时需要更新的内存量。要访问原始数据,您可以使用deltaState指针 和deltaStateSizeInBytes。输入系统应将数据应用到设备状态中由 定义的偏移量stateOffset。
Device events
设备事件表示与整个设备相关的更改。如果您对这些事件感兴趣,通常订阅更高级别的InputSystem.onDeviceChange事件比InputEvents自己处理更方便。
DeviceRemoveEvent( 'DREM')
DeviceRemovedEvent表示设备已被移除或断开连接。查询被移除的设备,可以使用commondeviceId字段。此事件没有任何其他数据。
DeviceConfigurationEvent( 'DCFG')
DeviceConfigurationEvent表示设备的配置发生了变化。其含义是特定于设备的。例如,这可能表示键盘使用的布局已更改,或者在控制台上,游戏手柄已更改为其分配的玩家 ID。您可以从公共deviceId字段中查询更改的设备。此事件没有任何其他数据。
Text events
键盘设备发送这些事件来处理文本输入。如果您对这些事件感兴趣,通常订阅Keyboard 类上更高级别的回调比InputEvents自己处理更方便。
TextEvent( 'TEXT')
IMECompositionEvent( 'IMES')
Working with events
Monitoring events
如果您想自己对传入的事件进行任何监控或处理,请订阅InputSystem.onEvent回调。
Reading state events
状态事件包含设备的原始内存快照。因此,解释事件中的数据需要了解给定设备的各个状态的存储位置和方式
访问状态事件中包含的状态的最简单方法是依赖状态所针对的设备
您可以要求任何控件从给定事件而不是从其内部存储的状态中读取其值。
Creating events
任何人都可以针对任何现有设备创建和排队新的输入事件。排队输入事件是线程安全的,这意味着事件生成可以在后台线程中发生。
注意:Unity 为来自后台线程的事件分配有限的内存。如果后台线程产生太多事件,则从线程排队事件会阻塞线程,直到主线程刷新后台事件队列。
请注意,排队事件不会立即消耗该事件。事件处理发生在下一次更新(取决于InputSettings.updateMode,它通过 手动触发InputSystem.Update,或作为播放器循环的一部分自动触发)。
请注意,增量状态事件仅适用于字节对齐且内存中大小为 8 位倍数的控件。例如,您不能为存储为单个位的按钮控件发送增量状态事件。
Capturing events
注意:要下载包含可重用 MonoBehaviour 的示例项目InputRecorder,该项目可以从任意设备捕获和重放输入,请打开包管理器,选择输入系统包,然后选择示例项目“输入记录器”进行下载。
布局
描述
布局是输入系统了解输入设备和输入控件类型的核心机制。每个布局代表输入控件的特定组合。通过将设备的描述与布局相匹配,输入系统能够创建正确类型的设备并正确解释传入的输入数据。
注意:布局是输入系统的一项高级功能,主要是内部功能。如果您想支持自定义设备或更改现有设备的行为,则布局系统的知识非常有用。
布局描述了用于输入的内存格式,以及要构建的输入控件,以便从该内存读取数据或从该内存写入数据。
输入系统附带了大量常见控件类型和常见设备的布局。对于其他设备类型,系统会根据设备界面上报的设备描述自动生成布局。
您可以从输入调试器浏览当前理解的布局集。
布局有两个主要功能:
描述包含输入数据的特定内存布局。
为操作数据的控件分配名称、结构和含义。
布局格式
创建
由 C# 结构和类表示。
类型
在最基本的形式中,布局可以由派生自的 C# 类表示:
InputControl 用于控件布局。
InputDevice 用于设备布局。
// The InputControlLayout attribute is not strictly necessary here.
// However, you can use it to set additional properties (such as
// a custom display name for the layout).
[InputControlLayout]
public class MyDevice : InputDevice
{
public AxisControl axis { get; private set; }
public ButtonControl button { get; private set; }
protected override void FinishSetup(InputDeviceBuilder builder)
{
base.FinishSetup(builder);
axis = builder.GetControl<AxisControl>("axis");
button = builder.GetControl<ButtonControl>("button");
}
}
然后,您可以使用 注册布局InputSystem.RegisterLayout。这对控制和设备布局的作用相同
// Note: This should generally be done from InitializeOnLoad/
// RuntimeInitializeOnLoad code.
InputSystem.RegisterLayout<MyDevice>();
结构
当您实现对新输入设备的支持时,通常有一种现有的数据格式,输入系统在其中接收设备的输入。添加对数据格式的支持的最简单方法是使用带有InputControlAttribute.
public struct MyDeviceState : IInputStateTypeInfo
{
public FourCC format => new FourCC('M', 'D', 'E', 'V');
[InputControl(name = "button1", layout = "Button", bit = 0)]
[InputControl(name = "button2", layout = "Button", bit = 1)]
[InputControl(name = "dpad", layout = "Dpad", bit = 2, sizeInBits = 4)]
[InputControl(name = "dpad/up", bit = 2)]
[InputControl(name = "dpad/down", bit = 3)]
[InputControl(name = "dpad/left", bit = 4)]
[InputControl(name = "dpad/right", bit = 5)]
public int buttons;
[InputControl(layout = "Stick")]
public Vector2 stick;
[InputControl(layout = "Axis")] // Automatically converts from byte to float.
public byte trigger;
}
// The Device must be directed to the state struct we have created.
[InputControlLayout(stateType = typeof(MyDeviceState)]
public class MyDevice : InputDevice
{
}
JSON 格式。
您还可以从包含相同信息的 JSON 字符串创建布局。如果您希望能够与代码分开存储和传输布局信息,这将非常有用 - 例如,如果您希望能够动态添加对新设备的支持,而无需构建新的应用程序。您可以使用InputControlLayout.ToJson()和InputControlLayout.FromJson()将布局与格式相互转换。
与上面相同的布局在 JSON 格式中看起来像这样:
{
"name": "MyDevice",
"format": "MDEV",
"controls": [
{
"name": "button1",
"layout": "Button",
"offset": 0,
"bit": 0,
},
{
"name": "button2",
"layout": "Button",
"offset": 0,
"bit": 1,
},
{
"name": "dpad",
"layout": "Dpad",
"offset": 0,
"bit": 2,
"sizeInBits": 4,
},
{
"name": "dpad/up",
"offset": -1,
"bit": 2,
},
{
"name": "dpad/down",
"offset": -1,
"bit": 3,
},
{
"name": "dpad/left",
"offset": -1,
"bit": 4,
},
{
"name": "dpad/right",
"offset": -1,
"bit": 5,
},
{
"name": "stick",
"layout": "Stick",
"offset": 4,
"format": "VEC2",
},
{
"name": "trigger",
"layout": "Axis",
"offset": 12,
"format": "BYTE",
}
]
}
使用布局构建器在运行时即时构建。
最后,输入系统还可以在代码中动态构建布局。这对于为每个设备提供描述信息的设备接口(例如HID)很有用。
要在代码中动态构建布局,您可以使用InputControlLayout.BuilderAPI。
以下是与以编程方式构建的先前示例相同的布局:
var builder = new InputControlLayout.Builder()
.WithName("MyDevice")
.WithFormat("MDEV");
builder.AddControl("button1")
.WithLayout("Button")
.WithByteOffset(0)
.WithBitOffset(0);
builder.AddControl("button2")
.WithLayout("Button")
.WithByteOffset(0)
.WithBitOffset(1);
builder.AddControl("dpad")
.WithLayout("Dpad")
.WithByteOffset(0)
.WithBitOffset(2)
.WithSizeInBits(4);
builder.AddControl("dpad/up")
.WithByteOffset(-1)
.WithBitOffset(2);
builder.AddControl("dpad/down")
.WithByteOffset(-1)
.WithBitOffset(3);
builder.AddControl("dpad/left")
.WithByteOffset(-1)
.WithBitOffset(4);
builder.AddControl("dpad/right")
.WithByteOffset(-1)
.WithBitOffset(5);
builder.AddControl("stick")
.WithLayout("Stick")
.WithByteOffset(4)
.WithFormat("VEC2");
builder.AddControl("trigger")
.WithLayout("Axis")
.WithByteOffset(12)
.WithFormat("BYTE");
var layout = builder.Build();
布局继承
您可以从现有布局派生布局。此过程基于将派生布局中的信息合并到基本布局包含的信息之上。
对于定义为类型的布局,基本布局是基本类型(如果有)的布局。
对于 JSON 中定义的布局,您可以extends在根节点的属性中指定基本布局。
对于使用代码创建的布局InputControlLayout.Builder,您可以使用 指定基本布局InputControlLayout.Builder.Extend()。
控制项目
每个布局由零个或多个控制项组成。每一项要么描述一个新控件,要么修改现有控件的属性。后者还可以深入到层次结构并修改由另一项隐式添加为子项的 Control 的属性。
// Add a dpad Control.
[InputControl(layout = "Dpad")]
// And now modify the properties of the "up" Control that was added by the
// "Dpad" layout above.
[InputControl(name = "dpad/up", displayName = "DPADUP")]
public int buttons;
name 控件的名称。
默认情况下,这是字段/属性的名称InputControlAttribute 应用于。
displayName 控件的显示名称(用于 UI 字符串)。
shortDisplayName 控件的短显示名称(用于 UI 字符串)。
layout 用于控件的布局。
variants 控件的变体。
aliases 控件的别名。这些是控件可以引用的替代名称。
usages 用法 的控制。
offset 找到控件状态的字节偏移量。
bit 在其字节中找到控件状态的位偏移量。
sizeInBits 控件状态的总大小,以位为单位。
arraySize 如果将其设置为非零值,系统将创建此大小的控件数组。
parameters 要传递给控件的任何参数。系统会将这些应用于 Control 类型可能具有的任何字段,例如AxisControl.scaleFactor.
processors 处理器 申请控制。
noisy 是否考虑控制 嘈杂.
synthetic 是否考虑控制 合成的.
defaultState 状态记忆控件的默认初始值。
useStateFrom 为了 合成的 Controls,用于合成Control状态。
minValue 控件可以报告的最小值。用于评估控制幅度.
maxValue 控件可以报告的最大值。用于评估控制幅度.
布局覆盖
您可以使用布局覆盖以非破坏性方式更改现有布局的各个方面。您可以调用InputSystem.RegisterLayoutOverride将布局注册为其基本布局的覆盖。然后,系统将覆盖中存在的任何属性添加到基本布局或现有属性。
// Add an extra Control to the "Mouse" layout
const string json = @"
{
""name"" : ""Overrides"",
""extend"" : ""Mouse"",
""controls"" : [
{ ""name"" : ""extraControl"", ""layout"" : ""Button"" }
]
}
";
InputSystem.RegisterLayoutOverride(json);
Interactions
描述
交互表示特定的输入模式。例如,保留是一种交互,它要求控制至少保留最少的时间。
交互驱动对操作的响应。您可以将它们放在单个绑定或整个操作上,在这种情况下,它们适用于操作上的每个绑定。在运行时,当特定交互完成时,这会触发 Action。
阶段
交互具有一组不同的阶段,它可以响应接收输入而经历
Waiting 交互正在等待输入。
Started 交互已开始(即,它收到了一些预期的输入),但尚未完成。
Performed 交互完成。
Canceled 交互被中断并中止。例如,用户按下然后释放按钮所需的最短时间之前保持互动 去完成。
并非每个交互都会触发每个阶段,特定交互触发阶段的模式取决于交互类型。
下面的示例演示了这种带有火操作的设置,用户可以点击它立即开火,或按住以充电:
var fireAction = new InputAction("fire");
fireAction.AddBinding("<Gamepad>/buttonSouth")
// Tap fires, slow tap charges. Both act on release.
.WithInteractions("tap;slowTap");
fireAction.started +=
context =>
{
if (context.Interaction is SlowTapInteraction)
ShowChargingUI();
};
fireAction.performed +=
context =>
{
if (context.Interaction is SlowTapInteraction)
ChargedFire();
else
Fire();
};
fireAction.canceled +=
_ => HideChargingUI();