Unity + Oculus Quest 之六 XR Interactive Toolkit
Review在之前的文章中,我们设置过 Action Based XR Rig 同时还用它来 Grab 东西。
甚至还写过代码:
public class RightHandController : MonoBehaviour, XRIDefaultInputActions.IXRIRightHandActions
{
...
private XRIDefaultInputActions controls;
private void OnEnable()
{
if (controls == null )
{
controls = new XRIDefaultInputActions();
controls.XRILeftHand.SetCallbacks( this );
}
controls.XRILeftHand.Enable();
}
public void OnPrimaryButtonPress(InputAction.CallbackContext context)
{
if (context.performed)
{
Debug.Log(message: ": Primary Button Press!");
}
}
...
}
但如果想弄明白 XR Interactive Toolkit 并不是一件容易的事,看它的 doc 也会让人有点迷茫,同时代码里的 InputAction.CallbackContext 这又是怎么回事? 为什么设置 Action Based Rig 有那么多步骤?为什么需要在 Action Maps 中去创建 Action,再绑定按键? 为什么不可以类似 Oculus Integration 这样来知道 按键被按OVRInput.Get(OVRInput.Button.One) 然后触发事件?
打开 XR Interaction Toolkit manual,答案才缓缓的浮出。
要了解为什么参数类型是 InputAction.CallbackContext, 为什么要叫 Action Based Rig? Rig 有装备、操纵的意思,而 Rig 为什么有 Action Based 和 Device Based 之分?
这一切都要回到 Input System。 manual 写到:
This package has a dependency on Input System.
Input System
XR Interactive Toolkit 是依赖于 Input System,跳到 Input System 的 Manual:
The Input System package implements a system to use any kind of Input Device to control your Unity content. It's intended to be a more powerful, flexible, and configurable replacement for Unity's classic Input Manager (the UnityEngine.Input class).
好,又出现新的,嚯, Input Manager,看来要跟 Unity 的 Input 磕一下了。那就干脆回到 Unity 的 Manual 看一下 Input:
Unity supports input through two separate systems:
- The Input Manager is part of the core Unity platform and available by default.
- The Input System is a package that needs to be installed via the Package Manager before you can use it. It requires the .NET 4 runtime, and doesn’t work in projects that use the old .NET 3.5 runtime.那我就把问题再往前推, 如果不是 VR project, 甚至一个简单的 2D project,我怎么用 Unity 来获得用户的输入呢?
引入 Input System的必要
点开 Input Manager 的 Manual:
Input.GetKey("a");
啊,很简单啊,看起来很好用啊,为什么还要 Input System 呢?问题在于,当我们使用 Unity 开发的时候,我们可能想跨平台,比如我的 App,我想它可以
[*]Mac
[*]Windows
[*]iPad / Android
[*]Xbox
[*]Mac/Win + 手柄
那么这个时候来处理输入,如果我们需要针对每个 platform 来写处理数据,那真的有点烦,所以这个时候就出现了 Input System. 它可以帮我们统一不同的平台。
Input System 的哲学
所以 Input System 的逻辑是怎样的呢?它首先有这么几大块:
[*]Input Action Assets: 记录所有的 action 和 binding 的文件
[*]Actions : Actons 是定义输入的逻辑
[*]Bindings : 绑定描述动作和输入设备控件之间的连接
[*]PlayerInput:PlayerInput 是一个脚本,用于管理动作事件并将其链接到相应的代码逻辑。
它的工作逻辑是:
[*]Unity 从连接的设备收集信息并将相应的 event 事件(如按钮单击)发送到 Input System
[*]Input System 把 event 转化成 action,当然就是根据 Input Action Assets 来做转化
[*]最后把 actions 传到 PlayerInput 脚本
这样的好处是分层了,把物理的按键和触发的事件分开了,也就是所谓的 decoupling ? 那么它的好处就是更加强大,比如想触发同样的事件/代码,我可以把它绑在 WASD 上,也可以是 ↑↓←→上,甚至可以是自己手柄的上下左右上。
坏处就是:一开始上手有点复杂(特别对于 Unity 的初学者 ),并且它会强迫你去更多的思考关于设计模式之类的。
Input Manager 使用
那么我们来弄一个 Input System 使用的例子吧,作为对比,我们先用 Input Manager 来做事:
[*]新建 GameObject
[*]新建 C# Script
[*]把脚本绑在 GameObject 上
修改 update:
void Update()
{
if (Input.GetKey("space"))
{
Debug.Log("Space typed: jump");
}
if (Input.GetKey("j"))
{
Debug.Log("J pressed: jump");
}
}
run,可以看到是能得到输入的。
类似的事情,看看如何用 Input System 做到。
Input System 使用
[*]Window | Package Manager | Input System 安装
[*]Edit | Project Settings | Player | 确保 Active Input Handling 选的是 Input System Package (New)
这个时候 Console 可能会报错了,因为我们已经决定了用新的 input,先删除 Update 里的代码。 正式来尝试使用 Input System.
首先 Window | Analysis | Input Debugger,可以看到它会有 Devices,甚至你可以尝试插一个手柄,它也会识别,你可以点击设备,然后就可以看相应的设备,比如点击 keyboard,然后尝试按键,可以看到一些信息。
[*]Assets 右键 Input Action,命名称为 PlayerControls
[*]双击打开PlayerControls,新建 Action Maps,命名 Player,然后新建 Jump
[*]Jump 点击加号, 绑一下 Space 按键, 再加一个 绑定 j key
[*]甚至如果你想,可以绑手柄上的按键,保存
[*]在 PlayerControls 的 Inspector 中 Generate C# Class,它自动帮我们产生一个一个 C# 文件
修改之前的脚本:
using UnityEngine;
using UnityEngine.InputSystem;
public class UserInput : MonoBehaviour
{
public PlayerControls playerControls;
private void Awake()
{
playerControls = new PlayerControls();
}
private void OnEnable()
{
playerControls.Enable();
}
private void OnDisable()
{
playerControls.Disable();
}
void Start()
{
playerControls.Player.Jump.performed += OnJump;
}
private void OnJump(InputAction.CallbackContext context)
{
Debug.Log("Jump");
}
}
run, 点击 space 或者 j 建,都可以看到 Jump 被成功打印出来。
现在来分析代码:
[*]Awake 中的代码是建立了一个 PlayerControls 的实例,这可以 refer 到我们刚刚定义的 Input Action
[*]Enable/Disable常规代码
[*]Start 中的代码比较关键,我们通过 playerControls. 记得我们刚刚定义的 Action Maps 叫 Player,然后有一个 Action 叫 Jump,这里 playerControls.Player.Jump.performed += OnJump; 就是是把 OnJump 这个函数加到了它的 performed callback 中
[*]事件触发: 点击 space 或者 j 键的时候就调用了 OnJump
Input System 的 Quick start guide 也提供了比如别的方法来 Invoke Unity 事件。
我也有看到过不生成 PlayerControls.cs 然后直接用代码 InputActionReference gripInputAction;直接去链接 InputAction 的。 当然官方文档中也提供了别的设置 Input Actions 的方法。
这里可以提到的是绕了一大圈之后, XR Interactive Toolkit 设计 Action Based Rig 这件事以及其中涉及的代码部分希望不要再让人那么迷茫了。甚至你可以给自己一点挑战,那就是不创建 C# class,直接代码设置 InputActionReference gripInputAction;, inspector 中设置这个 gripInputAction,然后让 VR 手柄用另一种方式跟你说 Hello World。
甚至 XR Interaction Toolkit manual 我们也有勇气和实力打开读懂了。
参考:
[*]XR Interaction Toolkit Manual
[*]Input System Manual
[*]Input
[*]New Unity Input System: Getting Started
[*]Setting up the Input System
[*]Getting input indirectly through an Input Action
[*]Actions
页:
[1]