参考资料

https://docs.unity3d.com/cn/2022.2/Manual/class-InputManager.html

学习路线

  1. 了解Unity在PC端如何获取鼠标键盘的输入事件,了解Input

  2. 了解Unity如何获取XR的输入事件,了解InputDevices

  3. 了解XR Interaction Toolkit,了解Unity的输入的高阶用法

名词解释

HMD:头戴,Head Mounted Display

Unity的两套独立的输入系统

InputManager:旧版的输入系统

InputSystem:新版的输入系统

输入系统不影响API的使用。

Unity 支持来自多种输入设备的输入,包括:

  • 键盘和鼠标

  • 游戏杆

  • 控制器

  • 触摸屏

  • 加速度计或陀螺仪等移动设备的运动感应功能

  • VR 和 AR 控制器

float horizontalInput = Input.GetAxis ("Horizontal");
float moveSpeed = 10;
//定义对象移动的速度。

float horizontalInput = Input.GetAxis("Horizontal");
//获取水平输入轴的数值。

float verticalInput = Input.GetAxis("Vertical");
//获取垂直输入轴的数值。

transform.Translate(new Vector3(horizontalInput, verticalInput, 0) * moveSpeed * Time.deltaTime);
//将对象移动到 XYZ 坐标,分别定义为 horizontalInput、0 以及 verticalInput。

移动端输入:触碰点

    void Update()
    {
        foreach(Touch touch in Input.touches)
        {
            if (touch.phase == TouchPhase.Began)
            {
                // 从当前触摸坐标构造一条射线
                Ray ray = Camera.main.ScreenPointToRay(touch.position);
                if (Physics.Raycast(ray))
                {
                    // 如果命中,则创建一个粒子
                    Instantiate(particle, transform.position, transform.rotation);
                }
            }
        }
    }

加速度计

using UnityEngine;

public class Accelerometer : MonoBehaviour
{
    float speed = 10.0f;

    void Update()
    {
        Vector3 dir = Vector3.zero;
        // 我们假设设备与地面平行,
        // 主屏幕按钮位于右侧

        // 将设备的加速度轴重新映射到游戏坐标:
        // 1) 设备的 XY 平面映射到 XZ 平面
        // 2) 绕 Y 轴旋转 90 度

        dir.x = -Input.acceleration.y;
        dir.z = Input.acceleration.x;

        // 将加速度矢量限制为单位球体
        if (dir.sqrMagnitude > 1)
            dir.Normalize();

        // 使其每秒移动 10 米而不是每帧 10 米...
        dir *= Time.deltaTime;

        // 移动对象
        transform.Translate(dir * speed);
    }
}

XR输入系统

Unity的输入系统一般只通过Input这个类就能够实现。

但是对于XR,目前推荐使用InputDevices这个类作为入口类。

Unity的Input、XR.InputTracking这两个类也能够获取XR输入,但是不鼓励使用,尽量使用InputDevices,这样语义更明确、语法更简洁。Input的缺点在于没法以向量的形式获取数据。

以Input为例,要想获取primaryAxis,就需要Input.GetAxis(1),Input.GetAxis(2),这样才能获取左手的摇杆位置;Input.GetAxis(4),Input.GetAxis(5)才能够获取右手的摇杆位置。

primary2DAxis [(1,2)/(4,5)]

InputDevices

InputDevices

静态

InputDevices的静态函数只有一个目标:获取InputDevice。它提供了几种检索InputDevice的方式

  • 通过XRNode获取,这也是最常用的方式

  • 通过Role获取,对应枚举InputDeviceRole

  • 通过Characteristics获取,对应枚举InputDeviceCharacteristics

  • GetDeviceAtXRNode:根据XRNode获取InputDevice

  • GetDevicesAtRole:根据Role获取设备

  • GetDevicesWithCharacteristics:根据设别的特性获取设备。

成员

  • DeviceRole:设备的角色,InputDeviceRole

  • TryGet+(InputFeatureType中的数据类型):从设备读取数据

事件:

  • 设备连接

  • 设备断连

  • 设备配置发生变化

获取全部的可用设备

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()));
}

XRNode

  • 左眼

  • 右眼

  • 中心眼

  • 左手

  • 右手

  • 游戏控制器

  • 跟踪参照物

  • 硬件跟踪器

XRNode跟InputDeviceRole基本上是同一类东西。

InputDeviceCharacteristics

一个设备可能包含多种特性,使用一个int值表示一个设备的特性,例如HeldInHand|Left表示左右持有的设备。就像使用rwx表示一个文件的特性一样。

  • HeadMounted

  • Camera

  • HeldInHand

  • HandTracking

  • EyeTracking

  • TrackedDevice

  • Controller

  • TrackingReference

  • Left

  • Right

InputDeviceRole

输入设备的角色,包括:

  • 左手持有

  • 右手持有

  • 游戏控制器

  • 跟踪参照物

  • 硬件跟踪器

public enum InputDeviceRole : uint
{
  /// <summary>
  ///   <para>This device does not have a known role.</para>
  /// </summary>
  Unknown,
  /// <summary>
  ///   <para>This device is typically a HMD or Camera.</para>
  /// </summary>
  Generic,
  /// <summary>
  ///   <para>This device is a controller that represents the left hand.</para>
  /// </summary>
  LeftHanded,
  /// <summary>
  ///   <para>This device is a controller that represents the right hand.</para>
  /// </summary>
  RightHanded,
  /// <summary>
  ///   <para>This device is a game controller.</para>
  /// </summary>
  GameController,
  /// <summary>
  ///   <para>This device is a tracking reference used to track other devices in 3D.</para>
  /// </summary>
  TrackingReference,
  /// <summary>
  ///   <para>This device is a hardware tracker.</para>
  /// </summary>
  HardwareTracker,
  /// <summary>
  ///   <para>This device is a legacy controller.</para>
  /// </summary>
  LegacyController,
}

触觉

Haptic是触觉的意思,有些游戏手柄支持触觉。

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);
            }
    }
}

XR Input Tracking:Unity旧版XR输入接口

InputTracking

Unity.Xr.InputTracking是Unity旧版的输入入口类。

设备事件

输入设备的跟踪有四个事件

  • trackingAcquired:获取设备跟踪

  • trackingLost:设备丢失

  • nodeAdded:设备添加

  • nodeRemoved:设备移除

读取数据

  • 获取XRNode的位置

  • 获取XRNode的Rotation

  • 获取XRNode的DeviceId

从InputTracking中读取数据已经不鼓励使用了,请使用InputDevices。例如获取设备地址。

[NativeConditional("ENABLE_VR", "Vector3f::zero")]
[Obsolete("This API is obsolete, and should no longer be used. Please use InputDevice.TryGetFeatureValue with the CommonUsages.devicePosition usage instead.")]
public static Vector3 GetLocalPosition(XRNode node)
{
  Vector3 ret;
  InputTracking.GetLocalPosition_Injected(node, out ret);
  return ret;
}

XRNodeState

XNode:结点的类型

AwailableTrackingData:可用的数据列表

位置

角度

速度

角速度

加速度

角加速度

tracked:是否正在跟踪

AwailableTrackingData

枚举,描述一个XRNodeState可用的数据列表可以组合这个枚举,例如位置|旋转,表示这个XRNodeState可用的数据是位置和旋转。

  • 位置

  • 旋转

  • 速度

  • 角速度

  • 加速度

  • 角加速度

InputFeatureUsage

InputFeatureUsage

Unity引擎里面的类,定义了一个输入类型,可以描述所有的输入。它包含的字段:

  • name:输入的名称

  • UsageType:输入的类型

InputFeatureType

internal enum InputFeatureType : uint
{
  Custom = 0,//Custom类型,使用byte数组表示
  Binary = 1,//bool
  DiscreteStates = 2,//uint,枚举状态
  Axis1D = 3,//一维浮点
  Axis2D = 4,//二维浮点:例如鼠标
  Axis3D = 5,//三维浮点
  Rotation = 6,//角度,Quaternion,四元数
  Hand = 7,//Hand类型
  Bone = 8,//Bone类型
  Eyes = 9,//眼睛
  kUnityXRInputFeatureTypeInvalid = 4294967295, // 0xFFFFFFFF,未定义的操作类型
}

InputFeature中涉及到的结构体

Hand、Bone、Eyes

Bone

  • deviceId:设备ID

  • featureIndex:featureID

  • Position

  • Rotation

  • ParentBone:父骨骼

  • ChildBones:List

Eyes

眼睛有左眼和右眼:

  • LeftEyes

  • RightEyes

以下传感器是左眼和右眼都有的。

  • EyeRotation

  • EyePosition

  • FixationPoint:修正点的位置

  • OpenAmount:眼睛的睁开量

Hand类型

Hand是一个Bone的封装,一个Hand包含一个RootBone和多个FingerBones

  • 设备Id

  • FeatureIndex

  • RootBone:Bone

  • FingerBones:List返回手指骨骼列表

Unity支持的输入的类型:InputFeatureType。

  • bool

  • uint

  • float

CommonUsages

UnityEngine.XR中定义了一些InputFeatureUsage,与Pico XR SDK的InputFeatureUsage类似。

CommonUsage中定义的是比较通用的InputFeatureUsage,而PXR_Usage中存放的是Pico特有的InputFeatureUsage,这些featureUsage大多是与眼睛有关系。

/// <summary>
///   <para>Informs to the developer whether the device is currently being tracked.</para>
/// </summary>
public static InputFeatureUsage<bool> isTracked = new InputFeatureUsage<bool>("IsTracked");
/// <summary>
///   <para>The primary face button being pressed on a device, or sole button if only one is available.</para>
/// </summary>
public static InputFeatureUsage<bool> primaryButton = new InputFeatureUsage<bool>("PrimaryButton");
/// <summary>
///   <para>The primary face button being touched on a device.</para>
/// </summary>
public static InputFeatureUsage<bool> primaryTouch = new InputFeatureUsage<bool>("PrimaryTouch");
/// <summary>
///   <para>The secondary face button being pressed on a device.</para>
/// </summary>
public static InputFeatureUsage<bool> secondaryButton = new InputFeatureUsage<bool>("SecondaryButton");
/// <summary>
///   <para>The secondary face button being touched on a device.</para>
/// </summary>
public static InputFeatureUsage<bool> secondaryTouch = new InputFeatureUsage<bool>("SecondaryTouch");
/// <summary>
///   <para>A binary measure of whether the device is being gripped.</para>
/// </summary>
public static InputFeatureUsage<bool> gripButton = new InputFeatureUsage<bool>("GripButton");
/// <summary>
///   <para>A binary measure of whether the index finger is activating the trigger.</para>
/// </summary>
public static InputFeatureUsage<bool> triggerButton = new InputFeatureUsage<bool>("TriggerButton");
/// <summary>
///   <para>Represents a menu button, used to pause, go back, or otherwise exit gameplay.</para>
/// </summary>
public static InputFeatureUsage<bool> menuButton = new InputFeatureUsage<bool>("MenuButton");
/// <summary>
///   <para>Represents the primary 2D axis being clicked or otherwise depressed.</para>
/// </summary>
public static InputFeatureUsage<bool> primary2DAxisClick = new InputFeatureUsage<bool>("Primary2DAxisClick");
/// <summary>
///   <para>Represents the primary 2D axis being touched.</para>
/// </summary>
public static InputFeatureUsage<bool> primary2DAxisTouch = new InputFeatureUsage<bool>("Primary2DAxisTouch");
/// <summary>
///   <para>Represents the secondary 2D axis being clicked or otherwise depressed.</para>
/// </summary>
public static InputFeatureUsage<bool> secondary2DAxisClick = new InputFeatureUsage<bool>("Secondary2DAxisClick");
/// <summary>
///   <para>Represents the secondary 2D axis being touched.</para>
/// </summary>
public static InputFeatureUsage<bool> secondary2DAxisTouch = new InputFeatureUsage<bool>("Secondary2DAxisTouch");
/// <summary>
///   <para>Indicates whether the user is present and interacting with the device.</para>
/// </summary>
public static InputFeatureUsage<bool> userPresence = new InputFeatureUsage<bool>("UserPresence");
/// <summary>
///   <para>Represents the values being tracked for this device.</para>
/// </summary>
public static InputFeatureUsage<InputTrackingState> trackingState = new InputFeatureUsage<InputTrackingState>("TrackingState");
/// <summary>
///   <para>Value representing the current battery life of this device.</para>
/// </summary>
public static InputFeatureUsage<float> batteryLevel = new InputFeatureUsage<float>("BatteryLevel");
/// <summary>
///   <para>A trigger-like control, pressed with the index finger.</para>
/// </summary>
public static InputFeatureUsage<float> trigger = new InputFeatureUsage<float>("Trigger");
/// <summary>
///   <para>Represents the users grip on the controller.</para>
/// </summary>
public static InputFeatureUsage<float> grip = new InputFeatureUsage<float>("Grip");
/// <summary>
///   <para>The primary touchpad or joystick on a device.</para>
/// </summary>
public static InputFeatureUsage<Vector2> primary2DAxis = new InputFeatureUsage<Vector2>("Primary2DAxis");
/// <summary>
///   <para>A secondary touchpad or joystick on a device.</para>
/// </summary>
public static InputFeatureUsage<Vector2> secondary2DAxis = new InputFeatureUsage<Vector2>("Secondary2DAxis");
/// <summary>
///   <para>The position of the device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> devicePosition = new InputFeatureUsage<Vector3>("DevicePosition");
/// <summary>
///   <para>The position of the left eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> leftEyePosition = new InputFeatureUsage<Vector3>("LeftEyePosition");
/// <summary>
///   <para>The position of the right eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> rightEyePosition = new InputFeatureUsage<Vector3>("RightEyePosition");
/// <summary>
///   <para>The position of the center eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> centerEyePosition = new InputFeatureUsage<Vector3>("CenterEyePosition");
/// <summary>
///   <para>The position of the color camera on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> colorCameraPosition = new InputFeatureUsage<Vector3>("CameraPosition");
/// <summary>
///   <para>The velocity of the device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> deviceVelocity = new InputFeatureUsage<Vector3>("DeviceVelocity");
/// <summary>
///   <para>The angular velocity of this device, formatted as euler angles.</para>
/// </summary>
public static InputFeatureUsage<Vector3> deviceAngularVelocity = new InputFeatureUsage<Vector3>("DeviceAngularVelocity");
/// <summary>
///   <para>The velocity of the left eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> leftEyeVelocity = new InputFeatureUsage<Vector3>("LeftEyeVelocity");
/// <summary>
///   <para>The angular velocity of the left eye on this device, formatted as euler angles.</para>
/// </summary>
public static InputFeatureUsage<Vector3> leftEyeAngularVelocity = new InputFeatureUsage<Vector3>("LeftEyeAngularVelocity");
/// <summary>
///   <para>The velocity of the right eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> rightEyeVelocity = new InputFeatureUsage<Vector3>("RightEyeVelocity");
/// <summary>
///   <para>The angular velocity of the right eye on this device, formatted as euler angles.</para>
/// </summary>
public static InputFeatureUsage<Vector3> rightEyeAngularVelocity = new InputFeatureUsage<Vector3>("RightEyeAngularVelocity");
/// <summary>
///   <para>The velocity of the center eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> centerEyeVelocity = new InputFeatureUsage<Vector3>("CenterEyeVelocity");
/// <summary>
///   <para>The angular velocity of the center eye on this device, formatted as euler angles.</para>
/// </summary>
public static InputFeatureUsage<Vector3> centerEyeAngularVelocity = new InputFeatureUsage<Vector3>("CenterEyeAngularVelocity");
/// <summary>
///   <para>The velocity of the color camera on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> colorCameraVelocity = new InputFeatureUsage<Vector3>("CameraVelocity");
/// <summary>
///   <para>The angular velocity of the color camera on this device, formatted as euler angles.</para>
/// </summary>
public static InputFeatureUsage<Vector3> colorCameraAngularVelocity = new InputFeatureUsage<Vector3>("CameraAngularVelocity");
/// <summary>
///   <para>The acceleration of the device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> deviceAcceleration = new InputFeatureUsage<Vector3>("DeviceAcceleration");
/// <summary>
///   <para>The angular acceleration of this device, formatted as euler angles.</para>
/// </summary>
public static InputFeatureUsage<Vector3> deviceAngularAcceleration = new InputFeatureUsage<Vector3>("DeviceAngularAcceleration");
/// <summary>
///   <para>The acceleration of the left eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> leftEyeAcceleration = new InputFeatureUsage<Vector3>("LeftEyeAcceleration");
/// <summary>
///   <para>The angular acceleration of the left eye on this device, formatted as euler angles.</para>
/// </summary>
public static InputFeatureUsage<Vector3> leftEyeAngularAcceleration = new InputFeatureUsage<Vector3>("LeftEyeAngularAcceleration");
/// <summary>
///   <para>The acceleration of the right eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> rightEyeAcceleration = new InputFeatureUsage<Vector3>("RightEyeAcceleration");
/// <summary>
///   <para>The angular acceleration of the right eye on this device, formatted as euler angles.</para>
/// </summary>
public static InputFeatureUsage<Vector3> rightEyeAngularAcceleration = new InputFeatureUsage<Vector3>("RightEyeAngularAcceleration");
/// <summary>
///   <para>The acceleration of the center eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> centerEyeAcceleration = new InputFeatureUsage<Vector3>("CenterEyeAcceleration");
/// <summary>
///   <para>The angular acceleration of the center eye on this device, formatted as euler angles.</para>
/// </summary>
public static InputFeatureUsage<Vector3> centerEyeAngularAcceleration = new InputFeatureUsage<Vector3>("CenterEyeAngularAcceleration");
/// <summary>
///   <para>The acceleration of the color camera on this device.</para>
/// </summary>
public static InputFeatureUsage<Vector3> colorCameraAcceleration = new InputFeatureUsage<Vector3>("CameraAcceleration");
/// <summary>
///   <para>The angular acceleration of the color camera on this device, formatted as euler angles.</para>
/// </summary>
public static InputFeatureUsage<Vector3> colorCameraAngularAcceleration = new InputFeatureUsage<Vector3>("CameraAngularAcceleration");
/// <summary>
///   <para>The rotation of this device.</para>
/// </summary>
public static InputFeatureUsage<Quaternion> deviceRotation = new InputFeatureUsage<Quaternion>("DeviceRotation");
/// <summary>
///   <para>The rotation of the left eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Quaternion> leftEyeRotation = new InputFeatureUsage<Quaternion>("LeftEyeRotation");
/// <summary>
///   <para>The rotation of the right eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Quaternion> rightEyeRotation = new InputFeatureUsage<Quaternion>("RightEyeRotation");
/// <summary>
///   <para>The rotation of the center eye on this device.</para>
/// </summary>
public static InputFeatureUsage<Quaternion> centerEyeRotation = new InputFeatureUsage<Quaternion>("CenterEyeRotation");
/// <summary>
///   <para>The rotation of the color camera on this device.</para>
/// </summary>
public static InputFeatureUsage<Quaternion> colorCameraRotation = new InputFeatureUsage<Quaternion>("CameraRotation");
/// <summary>
///   <para>Value representing the hand data for this device.</para>
/// </summary>
public static InputFeatureUsage<Hand> handData = new InputFeatureUsage<Hand>("HandData");
/// <summary>
///   <para>An Eyes struct containing eye tracking data collected from the device.</para>
/// </summary>
public static InputFeatureUsage<Eyes> eyesData = new InputFeatureUsage<Eyes>("EyesData");
/// <summary>
///   <para>A non-handed 2D axis.</para>
/// </summary>
[Obsolete("CommonUsages.dPad is not used by any XR platform and will be removed.")]
public static InputFeatureUsage<Vector2> dPad = new InputFeatureUsage<Vector2>("DPad");
/// <summary>
///   <para>Represents the grip pressure or angle of the index finger.</para>
/// </summary>
[Obsolete("CommonUsages.indexFinger is not used by any XR platform and will be removed.")]
public static InputFeatureUsage<float> indexFinger = new InputFeatureUsage<float>("IndexFinger");
/// <summary>
///   <para>Represents the grip pressure or angle of the middle finger.</para>
/// </summary>
[Obsolete("CommonUsages.MiddleFinger is not used by any XR platform and will be removed.")]
public static InputFeatureUsage<float> middleFinger = new InputFeatureUsage<float>("MiddleFinger");
/// <summary>
///   <para>Represents the grip pressure or angle of the ring finger.</para>
/// </summary>
[Obsolete("CommonUsages.RingFinger is not used by any XR platform and will be removed.")]
public static InputFeatureUsage<float> ringFinger = new InputFeatureUsage<float>("RingFinger");
/// <summary>
///   <para>Represents the grip pressure or angle of the pinky finger.</para>
/// </summary>
[Obsolete("CommonUsages.PinkyFinger is not used by any XR platform and will be removed.")]
public static InputFeatureUsage<float> pinkyFinger = new InputFeatureUsage<float>("PinkyFinger");
/// <summary>
///   <para>Represents a thumbrest or light thumb touch.</para>
/// </summary>
[Obsolete("CommonUsages.thumbrest is Oculus only, and is being moved to their package. Please use OculusUsages.thumbrest. These will still function until removed.")]
public static InputFeatureUsage<bool> thumbrest = new InputFeatureUsage<bool>("Thumbrest");
/// <summary>
///   <para>Represents a touch of the trigger or index finger.</para>
/// </summary>
[Obsolete("CommonUsages.indexTouch is Oculus only, and is being moved to their package.  Please use OculusUsages.indexTouch. These will still function until removed.")]
public static InputFeatureUsage<float> indexTouch = new InputFeatureUsage<float>("IndexTouch");
/// <summary>
///   <para>Represents the thumb pressing any input or feature.</para>
/// </summary>
[Obsolete("CommonUsages.thumbTouch is Oculus only, and is being moved to their package.  Please use OculusUsages.thumbTouch. These will still function until removed.")]
public static InputFeatureUsage<float> thumbTouch = new InputFeatureUsage<float>("ThumbTouch");