版本:Unity 6 (6000.0)
语言:英语
移动键盘
传统输入

Unity XR 输入

Unity 用户手册的这一部分提供了有关 Unity 支持的所有输入设备的信息,用于 虚拟现实虚拟现实 (VR) 使用头戴式设备和运动跟踪,将用户沉浸在一个逼真的图像和声音的人工 3D 世界中。 更多信息
词汇表
增强现实增强现实 (AR) 使用叠加在实时视频流上的计算机图形或视频来增强视图,并与真实和虚拟物体进行交互。 更多信息
词汇表
和 Windows 混合现实混合现实 (MR) 将其自己的虚拟环境与用户的真实世界环境相结合,并允许它们彼此交互。
词汇表
应用程序。本页面涵盖以下主题

XR 平台拥有丰富的输入功能,您可以在设计用户交互时利用这些功能。您的应用程序可以使用引用位置、旋转、触摸、按钮、操纵杆和手指传感器的特定数据。但是,对这些输入功能的访问在不同平台之间可能差异很大。例如,Vive 和 Oculus Rift 之间存在细微差异,但支持 VR 的桌面平台和 Daydream 等移动平台之间的差异要大得多。

Unity 提供了一个名为 InputFeatureUsage 的 C# 结构体,它定义了一组标准的物理设备控件(如按钮和触发器),用于访问任何平台上的用户输入。这些帮助您按名称识别输入类型。请参阅 XR.Input.CommonUsages,了解每个 InputFeatureUsage 的定义。

每个 InputFeatureUsage 对应于一个常见的输入操作或类型。例如,Unity 将名为 triggerInputFeatureUsage 定义为食指控制的单轴输入,无论您使用的是哪个 XR涵盖虚拟现实 (VR)、增强现实 (AR) 和混合现实 (MR) 应用程序的总称。支持这些形式的交互式应用程序的设备可以称为 XR 设备。 更多信息
词汇表
平台。您可以使用 InputFeatureUsage 通过其名称获取 trigger 状态,因此您无需为传统的 Unity 输入系统设置轴(或某些 XR 平台上的按钮)。

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 是否仍然代表活动控制器。

您可以通过以下方式访问输入设备

  • 特征
  • 角色
  • XR 节点

通过特征访问输入设备

设备特征描述了设备的功能或用途(例如,它是否戴在头上)。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 节点代表 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.deviceConnectedInputDevices.deviceDisconnected 在设备连接或断开连接时通知您的应用程序。它们还为您提供对新连接的输入设备的引用。

由于您可以跨多个帧保留这些引用,因此设备可能会断开连接或不再可用。要检查设备的输入是否仍然可用,请使用InputDevice.isValid脚本一段代码,允许您创建自己的组件,触发游戏事件,随着时间的推移修改组件属性,并以您喜欢的任何方式响应用户输入。 更多信息
请参阅 词汇表
访问输入设备的程序应在每帧开始时检查此项,然后再尝试使用该设备。

访问输入设备上的输入功能

您可以从特定的InputDevice读取输入功能,例如触发按钮的状态。例如,要读取右侧触发器的状态,请按照以下步骤操作:

  1. 使用InputDeviceRole.RightHandedXRNode.RightHand 获取右手设备的实例。
  2. 获得正确的设备后,使用InputDevice.TryGetFeatureValue 方法访问当前状态。

TryGetFeatureValue() 尝试访问功能的当前值,并返回:

  • true,如果它成功检索了指定的功能值
  • false,如果当前设备不支持指定的功能,或者设备无效(即控制器不再活动)

要获取特定的按钮、触摸输入或操纵杆轴值,请使用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()));
            }
        }
    }
}

primaryButton 示例

不同的控制器配置提供对不同功能的访问。例如,您可能在一个系统上有多个控制器,在不同系统上使用不同的控制器,或者在使用不同 SDK 的同一控制器上使用不同的按钮。这种多样性使得支持来自各种 XR 系统的输入变得更加复杂。Unity InputFeatureUsage API 有助于您获取与平台无关的输入。

以下示例访问名为primaryButtonInputFeatureUsage,无论哪个控制器或输入设备提供它。该示例包括一个类,该类在设备连接时扫描可用设备以查找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

XRInputSubsystem 和 InputDevice 关联

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 支持以下跟踪原点模式

  • Device:原点的位置位于主显示设备的第一个已知位置;通常是 HMD 或手机。
  • Floor:原点的位置位于地板上的一个已知位置。
  • Tracking Reference:原点的位置位于具有 TrackingReference 特性的 InputDevice 上。
  • Unknown:跟踪原点类型未知。这可能是由于系统故障或缺乏跟踪原点模式支持造成的。

您可以使用三个 API 来管理跟踪原点模式

通过传统输入系统进行 XR 输入

您仍然可以使用传统输入系统(包括InputXR.InputTracking)来检索 XR 输入功能。要执行此操作,请从本页的XR 输入映射表 中使用适用于平台设备功能的相应传统输入索引。在播放器设置设置,允许您为 Unity 生成的最终游戏设置各种特定于播放器的选项。 更多信息
请参阅 词汇表
输入 部分(菜单:编辑 > 项目设置一个广泛的设置集合,允许您配置物理、音频、网络、图形、输入以及项目中的许多其他方面的行为。 更多信息
请参阅 词汇表
> 输入),创建轴映射以添加从输入名称到平台设备功能的轴索引的相应映射。要检索按钮或轴值,请使用Input.GetAxisInput.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);
            }
    }
}
移动键盘
传统输入