Unity 用户手册的这一部分提供了有关 Unity 支持的所有输入设备的信息,用于 虚拟现实虚拟现实 (VR) 使用头戴式设备和运动跟踪,将用户沉浸在一个逼真的图像和声音的人工 3D 世界中。 更多信息
见 词汇表、增强现实增强现实 (AR) 使用叠加在实时视频流上的计算机图形或视频来增强视图,并与真实和虚拟物体进行交互。 更多信息
见 词汇表 和 Windows 混合现实混合现实 (MR) 将其自己的虚拟环境与用户的真实世界环境相结合,并允许它们彼此交互。
见 词汇表 应用程序。本页面涵盖以下主题
XR 平台拥有丰富的输入功能,您可以在设计用户交互时利用这些功能。您的应用程序可以使用引用位置、旋转、触摸、按钮、操纵杆和手指传感器的特定数据。但是,对这些输入功能的访问在不同平台之间可能差异很大。例如,Vive 和 Oculus Rift 之间存在细微差异,但支持 VR 的桌面平台和 Daydream 等移动平台之间的差异要大得多。
Unity 提供了一个名为 InputFeatureUsage 的 C# 结构体,它定义了一组标准的物理设备控件(如按钮和触发器),用于访问任何平台上的用户输入。这些帮助您按名称识别输入类型。请参阅 XR.Input.CommonUsages,了解每个 InputFeatureUsage
的定义。
每个 InputFeatureUsage
对应于一个常见的输入操作或类型。例如,Unity 将名为 trigger
的 InputFeatureUsage
定义为食指控制的单轴输入,无论您使用的是哪个 XR涵盖虚拟现实 (VR)、增强现实 (AR) 和混合现实 (MR) 应用程序的总称。支持这些形式的交互式应用程序的设备可以称为 XR 设备。 更多信息
见 词汇表 平台。您可以使用 InputFeatureUsage
通过其名称获取 trigger
状态,因此您无需为传统的 Unity 输入系统设置轴(或某些 XR 平台上的按钮)。
下表列出了标准控制器 InputFeatureUsage
名称及其如何映射到流行 XR 系统的控制器
InputFeatureUsage | 功能类型 | 传统输入索引(左控制器/右控制器) | WMR | Oculus | GearVR | Daydream | OpenVR(完整) | Vive | 通过 OpenVR 的 Oculus | 通过 OpenVR 的 WMR |
---|---|---|---|---|---|---|---|---|---|---|
primary2DAxis | 2D 轴 | [(1,2)/(4,5)] | 触控板 | 操纵杆 | 触控板 | 触控板 | 触控板/操纵杆 | 触控板 | 操纵杆 | 操纵杆 |
trigger | 轴 | [9/10] | 扳机 | 扳机 | 扳机 | 扳机 | 扳机 | 扳机 | 扳机 | 扳机 |
grip | 轴 | [11/12] | 抓握 | 抓握 | 保险杠 | 抓握 | 抓握 | 抓握 | 抓握 | |
secondary2DAxis | 2D 轴 | [(17,18)/(19,20)] | 操纵杆 | 触控板 | ||||||
secondary2DAxisClick | 按钮 | [18/19] | 操纵杆 - 点击 | |||||||
primaryButton | 按钮 | [2/0] | [X/A] - 按下 | 应用 | 主要 | 主要(夹心按钮)(1) | 主要(Y/B) | 菜单 | ||
primaryTouch | 按钮 | [12/10] | [X/A] - 触摸 | |||||||
secondaryButton | 按钮 | [3/1] | [Y/B] - 按下 | 备用 | 备用(X/A) | |||||
secondaryTouch | 按钮 | [13/11] | [Y/B] - 触摸 | |||||||
gripButton | 按钮 | [4/5] | 抓握 - 按下 | 抓握 - 按下 | 抓握 - 按下 | 抓握 - 按下 | 抓握 - 按下 | 抓握 - 按下 | ||
triggerButton | 按钮 | [14/15] | 触发 - 按下 | 触发 - 按下 | 触发 - 按下 | 触发 - 按下 | 触发 - 按下 | 触发 - 按下 | 触发 - 触摸 | 触发 - 按下 |
menuButton | 按钮 | [6/7] | 菜单 | 开始(仅限左控制器) | ||||||
primary2DAxisClick | 按钮 | [8/9] | 触控板 - 点击 | 拇指杆 - 按下 | 触控板 - 按下 | 触控板 - 按下 | 触控板/操纵杆 - 按下 | 触控板 - 按下 | 操纵杆 - 按下 | 触控板 - 按下 |
primary2DAxisTouch | 按钮 | [16/17] | 触控板 - 触摸 | 拇指杆 - 触摸 | 触控板 - 触摸 | 触控板 - 触摸 | 触控板/操纵杆 - 触摸 | 触控板 - 触摸 | 操纵杆 - 触摸 | 触控板 - 触摸 |
batteryLevel | 轴 | 电池电量 | ||||||||
userPresence | 按钮 | 用户存在 | 用户存在 |
(1)夹心按钮是指 Vive 菜单按钮。为了更好地处理跨平台应用程序,此按钮映射到 primaryButton 而不是 menuButton。
请参阅 XR.Input.CommonUsages,了解每个 InputFeatureUsage
的定义。
一个 InputDevice 代表任何物理设备,例如控制器、移动电话或头戴式设备。它可以包含有关设备跟踪、按钮、操纵杆和其他输入控件的信息。有关 InputDevice
API 的更多信息,请参阅有关 InputDevice 的文档。
使用 XR.InputDevices 类访问当前连接到 XR 系统的输入设备。要获取所有连接设备的列表,请使用 InputDevices.GetDevices
var inputDevices = new List<UnityEngine.XR.InputDevice>();
UnityEngine.XR.InputDevices.GetDevices(inputDevices);
foreach (var device in inputDevices)
{
Debug.Log(string.Format("Device found with name '{0}' and role '{1}'", device.name, device.role.ToString()));
}
输入设备在 XR 系统将其断开连接之前跨帧保持有效。使用 InputDevice.IsValid 属性确定 InputDevice
是否仍然代表活动控制器。
您可以通过以下方式访问输入设备
设备特征描述了设备的功能或用途(例如,它是否戴在头上)。InputDeviceCharacteristics 是一系列标志,您可以将其添加到您的代码中以搜索符合特定规范的设备。您可以根据以下特征过滤设备
设备 | 特征 |
---|---|
HeadMounted | 该设备连接到用户的头部。它具有设备跟踪和中心眼球跟踪。此标志最常用于识别头戴式显示器 (HMD)。 |
Camera创建一个场景中特定视点的图像的组件。输出要么绘制到屏幕上,要么作为纹理捕获。 更多信息 见 词汇表 |
该设备具有相机跟踪。 |
HeldInHand | 用户将设备握在手中。 |
HandTracking | 该设备代表一只经过物理跟踪的手。它具有设备跟踪,并且可能包含手部和骨骼数据。 |
EyeTracking | 该设备可以执行眼球跟踪,并具有 EyesData 功能。 |
TrackedDevice | 该设备可以在 3D 空间中进行跟踪。它具有设备跟踪。 |
Controller | 该设备具有按钮和轴的输入数据,可用作控制器。 |
TrackingReference | 该设备代表一个静态跟踪参考对象。它具有设备跟踪,但该跟踪数据不应改变。 |
Left | 将此特征与 HeldInHand 或 HandTracking 特征结合使用以识别该设备与左手相关联。 |
Right | 将此特征与 HeldInHand 或 HandTracking 特征结合使用以识别该设备与右手相关联。 |
Simulated6DOF | 该设备报告 6DOF 数据,但只有 3DOF 传感器。Unity 模拟位置数据。 |
底层 XR SDK 报告这些特征。您可以使用 InputDevice.Characteristics 查询它们。一个设备可以而且通常应该具有多个特征,您可以使用位标志对其进行过滤和访问。
InputDevices.GetDevicesWithCharacteristics 提供了一种搜索具有给定特征集的所有设备的方法。例如,您可以使用以下代码搜索系统中可用的 Left、HeldInHand、Controller InputDevices
var leftHandedControllers = new List<UnityEngine.XR.InputDevice>();
var desiredCharacteristics = UnityEngine.XR.InputDeviceCharacteristics.HeldInHand | UnityEngine.XR.InputDeviceCharacteristics.Left | UnityEngine.XR.InputDeviceCharacteristics.Controller;
UnityEngine.XR.InputDevices.GetDevicesWithCharacteristics(desiredCharacteristics, leftHandedControllers);
foreach (var device in leftHandedControllers)
{
Debug.Log(string.Format("Device name '{0}' has characteristics '{1}'", device.name, device.characteristics.ToString()));
}
此函数找到的设备至少包含指定的特征,但可能包含其他特征。例如,要查找左手控制器,您可以仅查找 InputDeviceCharacteristic.Left,而不是 InputDeviceCharacteristic.Controller。
设备角色描述了输入设备的一般功能。使用 InputDeviceRole 枚举指定设备角色。定义的角色是
角色 | 描述 |
---|---|
GameController | 一个控制台式 游戏控制器用于控制游戏中物体和角色的设备。 见 词汇表。 |
Generic | 代表核心 XR 设备的设备,例如头戴式显示器或移动设备。 |
HardwareTracker | 一个跟踪设备。 |
LeftHanded | 与用户左手相关的设备。 |
RightHanded | 与用户右手相关的设备。 |
TrackingReference | 跟踪其他设备的设备,例如 Oculus 跟踪相机。 |
底层 XR SDK 报告这些角色,但不同的提供商可能以不同的方式组织其设备角色。此外,用户可以交换手,因此角色分配可能与用户握持输入设备的手不匹配。例如,用户必须将 Daydream 控制器设置为右手或左手,但可以选择用相反的手握持控制器。
GetDevicesWithRole 提供具有特定 InputDeviceRole
的所有设备的列表。例如,您可以使用 InputDeviceRole.GameController
获取任何连接的 GameController
设备
var gameControllers = new List<UnityEngine.XR.InputDevice>();
UnityEngine.XR.InputDevices.GetDevicesWithRole(UnityEngine.XR.InputDeviceRole.GameController, gameControllers);
foreach (var device in gameControllers)
{
Debug.Log(string.Format("Device name '{0}' has role '{1}'", device.name, device.role.ToString()));
}
XR 节点代表 XR 系统中的物理参考点(例如,用户头部位置、左右手或跟踪参考,如 Oculus 相机)。
XRNode 枚举定义了以下节点
XR 节点 | 描述 |
---|---|
CenterEye | 用户双眼瞳孔之间中点。 |
GameController | 一个控制台式游戏控制器。您的应用程序可以有多个游戏控制器设备。 |
HardwareTracker | 一个硬件跟踪设备,通常连接到用户或物理物品。可以存在多个硬件跟踪器节点。 |
Head | 用户的头部中心点,由 XR 系统计算得出。 |
LeftEye | 用户的左眼。 |
LeftHand | 用户的左手。 |
RightEye | 用户的右眼。 |
RightHand | 用户的右手。 |
TrackingReference | 一个跟踪参考点,例如 Oculus 相机。可以存在多个跟踪参考节点。 |
使用 InputDevices.GetDevicesAtXRNode 获取与特定 XRNode
关联的设备列表。以下示例演示如何获取左手控制器
var leftHandDevices = new List<UnityEngine.XR.InputDevice>();
UnityEngine.XR.InputDevices.GetDevicesAtXRNode(UnityEngine.XR.XRNode.LeftHand, leftHandDevices);
if(leftHandDevices.Count == 1)
{
UnityEngine.XR.InputDevice device = leftHandDevices[0];
Debug.Log(string.Format("Device name '{0}' with role '{1}'", device.name, device.role.ToString()));
}
else if(leftHandDevices.Count > 1)
{
Debug.Log("Found more than one left hand!");
}
输入设备在帧之间保持一致,但可以在任何时间连接或断开连接。为了避免反复检查设备是否已连接到平台,请使用 InputDevices.deviceConnected 和 InputDevices.deviceDisconnected 在设备连接或断开连接时通知您的应用程序。它们还为您提供对新连接的输入设备的引用。
由于您可以跨多个帧保留这些引用,因此设备可能会断开连接或不再可用。要检查设备的输入是否仍然可用,请使用InputDevice.isValid。 脚本一段代码,允许您创建自己的组件,触发游戏事件,随着时间的推移修改组件属性,并以您喜欢的任何方式响应用户输入。 更多信息
请参阅 词汇表 访问输入设备的程序应在每帧开始时检查此项,然后再尝试使用该设备。
您可以从特定的InputDevice读取输入功能,例如触发按钮的状态。例如,要读取右侧触发器的状态,请按照以下步骤操作:
TryGetFeatureValue()
尝试访问功能的当前值,并返回:
要获取特定的按钮、触摸输入或操纵杆轴值,请使用CommonUsages 类。CommonUsages
包含 XR 输入映射表 中的每个InputFeatureUsage
,以及跟踪功能,如位置和旋转。以下代码示例使用CommonUsages.triggerButton 来检测用户是否当前正在按下特定InputDevice
实例上的触发按钮
bool triggerValue;
if (device.TryGetFeatureValue(UnityEngine.XR.CommonUsages.triggerButton, out triggerValue) && triggerValue)
{
Debug.Log("Trigger button is pressed.");
}
您还可以使用InputDevice.TryGetFeatureUsages 方法获取设备提供的每个InputFeatureUsage
的列表。此函数返回InputFeatureUsage 项的列表,这些项具有一个类型和一个描述功能的名称属性。以下示例枚举了给定输入设备提供的全部布尔值功能
var inputFeatures = new List<UnityEngine.XR.InputFeatureUsage>();
if (device.TryGetFeatureUsages(inputFeatures))
{
foreach (var feature in inputFeatures)
{
if (feature.type == typeof(bool))
{
bool featureValue;
if (device.TryGetFeatureValue(feature.As<bool>(), out featureValue))
{
Debug.Log(string.Format("Bool feature {0}'s value is {1}", feature.name, featureValue.ToString()));
}
}
}
}
不同的控制器配置提供对不同功能的访问。例如,您可能在一个系统上有多个控制器,在不同系统上使用不同的控制器,或者在使用不同 SDK 的同一控制器上使用不同的按钮。这种多样性使得支持来自各种 XR 系统的输入变得更加复杂。Unity InputFeatureUsage
API 有助于您获取与平台无关的输入。
以下示例访问名为primaryButton
的InputFeatureUsage
,无论哪个控制器或输入设备提供它。该示例包括一个类,该类在设备连接时扫描可用设备以查找primaryButton
。该类监视任何连接设备上的功能值,如果值发生变化,该类将调度一个UnityEvent。
要使用此类,请将其作为组件添加到 游戏对象Unity 场景中的基本对象,可以表示角色、道具、场景、相机、航路点等等。游戏对象的功能由附加到它的组件定义。 更多信息
请参阅 词汇表 在场景场景包含游戏中的环境和菜单。将每个唯一的场景文件视为一个唯一的关卡。在每个场景中,您放置您的环境、障碍物和装饰,本质上是逐块设计和构建您的游戏。 更多信息
请参阅 词汇表 中。例如
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.XR;
[System.Serializable]
public class PrimaryButtonEvent : UnityEvent<bool> { }
public class PrimaryButtonWatcher : MonoBehaviour
{
public PrimaryButtonEvent primaryButtonPress;
private bool lastButtonState = false;
private List<InputDevice> devicesWithPrimaryButton;
private void Awake()
{
if (primaryButtonPress == null)
{
primaryButtonPress = new PrimaryButtonEvent();
}
devicesWithPrimaryButton = new List<InputDevice>();
}
void OnEnable()
{
List<InputDevice> allDevices = new List<InputDevice>();
InputDevices.GetDevices(allDevices);
foreach(InputDevice device in allDevices)
InputDevices_deviceConnected(device);
InputDevices.deviceConnected += InputDevices_deviceConnected;
InputDevices.deviceDisconnected += InputDevices_deviceDisconnected;
}
private void OnDisable()
{
InputDevices.deviceConnected -= InputDevices_deviceConnected;
InputDevices.deviceDisconnected -= InputDevices_deviceDisconnected;
devicesWithPrimaryButton.Clear();
}
private void InputDevices_deviceConnected(InputDevice device)
{
bool discardedValue;
if (device.TryGetFeatureValue(CommonUsages.primaryButton, out discardedValue))
{
devicesWithPrimaryButton.Add(device); // Add any devices that have a primary button.
}
}
private void InputDevices_deviceDisconnected(InputDevice device)
{
if (devicesWithPrimaryButton.Contains(device))
devicesWithPrimaryButton.Remove(device);
}
void Update()
{
bool tempState = false;
foreach (var device in devicesWithPrimaryButton)
{
bool primaryButtonState = false;
tempState = device.TryGetFeatureValue(CommonUsages.primaryButton, out primaryButtonState) // did get a value
&& primaryButtonState // the value we got
|| tempState; // cumulative result from other controllers
}
if (tempState != lastButtonState) // Button state changed since last frame
{
primaryButtonPress.Invoke(tempState);
lastButtonState = tempState;
}
}
}
以下PrimaryReactor
类使用PrimaryButtonWatcher
来检测您何时按下主按钮,并响应按下操作,旋转其父游戏对象。要使用此类,请将其添加到可见的游戏对象(例如立方体)中,并将PrimaryButtonWatcher
引用拖动到 Watcher 属性中。
using System.Collections;
using UnityEngine;
public class PrimaryReactor : MonoBehaviour
{
public PrimaryButtonWatcher watcher;
public bool IsPressed = false; // used to display button state in the Unity Inspector window
public Vector3 rotationAngle = new Vector3(45, 45, 45);
public float rotationDuration = 0.25f; // seconds
private Quaternion offRotation;
private Quaternion onRotation;
private Coroutine rotator;
void Start()
{
watcher.primaryButtonPress.AddListener(onPrimaryButtonEvent);
offRotation = this.transform.rotation;
onRotation = Quaternion.Euler(rotationAngle) * offRotation;
}
public void onPrimaryButtonEvent(bool pressed)
{
IsPressed = pressed;
if (rotator != null)
StopCoroutine(rotator);
if (pressed)
rotator = StartCoroutine(AnimateRotation(this.transform.rotation, onRotation));
else
rotator = StartCoroutine(AnimateRotation(this.transform.rotation, offRotation));
}
private IEnumerator AnimateRotation(Quaternion fromRotation, Quaternion toRotation)
{
float t = 0;
while (t < rotationDuration)
{
transform.rotation = Quaternion.Lerp(fromRotation, toRotation, t / rotationDuration);
t += Time.deltaTime;
yield return null;
}
}
}
InputDevices 支持手部跟踪设备。手部跟踪设备始终
手部跟踪数据包含一个 Hand 对象和一系列最多 21 个骨骼输入功能。每个Bone 都有一个位置和方向,以及对层次结构中其父级和任何子级骨骼的引用。Hand 对象可以获取根骨骼,或者获取每个手指的骨骼列表。
当Hand.TryGetRootBone 获取根骨骼时,它会检索一个对象,该对象代表一个位于手腕正上方的骨骼。您还可以获取代表每个手指的骨骼列表。调用Hand.TryGetFingerBones 会返回一个列表(从指关节到指尖),该列表代表该手指的骨骼。
Input devices 支持眼部跟踪设备以及手部跟踪设备。眼部跟踪包含左右眼位置、用户正在观看的 3D 空间位置以及每只眼睛的眨眼次数。其数据类型为Eyes。要从设备中检索它,请使用CommonUsages.eyesData。
Unity 提供了两个输入系统:传统输入系统和 2019.2 中引入的XR 插件架构。在新的设置中,每个 InputDevice 都与XRInputSubsystem 相关联。这些子系统对象控制不与任何特定输入设备相关的全局输入行为(例如,管理跟踪原点或重新居中跟踪设备)。
每个 InputDevice 都包含对关联子系统的引用。如果设备来自集成平台,则此引用为 null。您还可以使用SubsystemManager.GetInstances<XRInputSubsystem> 获取所有活动的 XRInputSubsystem 对象,并且每个 XRInputSubsystem 都可以使用XRInputSubsystem.TryGetInputDevices 获取其设备。
您可以使用输入子系统来重新居中设备,方法是使用UnityEngine.XR.XRInputSubsystem。重新居中将 HMD 的当前位置设置为所有设备的新原点。它对于无法重新居中的设备或平台不支持重新居中的设备返回 false。
要检索跟踪边界,请使用TryGetBoundaryPoints。它包含一系列顺时针排序的 3D 点,其中 y 值位于地板级别,它们标出用户指定的“安全区域”,以放置内容和交互。您可以使用XRInputSubsystem.boundaryChanged 侦听此边界发生的变化。
XRInputSubsystem 还负责跟踪原点模式,该模式提供关于跟踪世界的原点位置的上下文。Unity 支持以下跟踪原点模式
您可以使用三个 API 来管理跟踪原点模式
您仍然可以使用传统输入系统(包括Input 和XR.InputTracking)来检索 XR 输入功能。要执行此操作,请从本页的XR 输入映射表 中使用适用于平台设备功能的相应传统输入索引。在播放器设置设置,允许您为 Unity 生成的最终游戏设置各种特定于播放器的选项。 更多信息
请参阅 词汇表 的 输入 部分(菜单:编辑 > 项目设置一个广泛的设置集合,允许您配置物理、音频、网络、图形、输入以及项目中的许多其他方面的行为。 更多信息
请参阅 词汇表 > 输入),创建轴映射以添加从输入名称到平台设备功能的轴索引的相应映射。要检索按钮或轴值,请使用Input.GetAxis 或Input.GetButton 并传入现在映射的轴或按钮名称。
有关如何使用按钮和操纵杆轴的更多信息,请参阅有关InputManager 的文档。
您可以向InputDevice 发送触觉事件。触觉以脉冲的形式出现,具有幅度和持续时间。
并非所有平台都支持所有类型的触觉,但您可以查询设备以了解触觉功能。以下示例获取右手输入设备,检查设备是否具有触觉功能,然后在有触觉功能的情况下回放脉冲
List<UnityEngine.XR.InputDevice> devices = new List<UnityEngine.XR.InputDevice>();
UnityEngine.XR.InputDevices.GetDevicesWithRole(UnityEngine.XR.InputDeviceRole.RightHanded, devices);
foreach (var device in devices)
{
UnityEngine.XR.HapticCapabilities capabilities;
if (device.TryGetHapticCapabilities(out capabilities))
{
if (capabilities.supportsImpulse)
{
uint channel = 0;
float amplitude = 0.5f;
float duration = 1.0f;
device.SendHapticImpulse(channel, amplitude, duration);
}
}
}