SteamVR 手柄交互

为了在 Unity 中与 SteamVR 交互,可下载并引入 SteamVR 插件。安装并加载完成后,可在 Window -> SteamVR Input 中设置输入系统。在输入系统中,VR 设备的输入需要和动作绑定,一个动作集合可以包含多个动作。以示例项目中的 platformer 动作集合为例:

platformer 动作集合包含两个输入动作:Move 动作的类型为 Vector 2,表示输入动作将会返回二维向量,比如说操控杆的位置或者手指在触控板上的位置;而 Jump 动作的类型为 Boolean,表示输入的动作只有两种状态,比如说某个按键是否被点击。

为了演示更多动作,此处再加入一个 Boolean 类型的动作 Disappear

设置完成后,点击 Opening binding UI,在设置页面中将动作与输入设备绑定。该设定是与具体的 VR 设备相关的,对于 HTC Vive 来说,手柄的握持键、菜单按钮、骨骼、扳机键、触控板和系统按钮皆可接收输入行为。在 platformer 动作集合中,进行如下设置:

  • 握持键的点击事件与 Disappear 动作关联,该输入动作属于布尔类型,表示握持键是否被按下。

  • 触控板的点击事件与 Jump 动作关联,该输入动作属于布尔类型,表示触控板是否被点击。

  • 触控板的位置信息与 Move 动作关联,该输入动作属于二维向量类型,表示触控板被触摸的位置。

完成以上的设置后,可将 platformer 动作集合用于具体的场景中。

以交互系统示例中的远程遥控场景为例:现在有一个玩偶预制 JoeJeff,具有移动、跳跃和消失等行为,玩家可通过遥控器 JoeJeffController 来操控该玩偶。

首先需要为遥控器预制绑定 Interactable 脚本(由插件提供),用于探测手柄对该物体的抓取事件。然后,在与遥控器预制绑定的 JoeJeffController 脚本中获取 Interactable,用于获取抓取的相关数据:

private Interactable interactable;

输入系统中设置的三个动作可通过 SteamVR_Input.GetAction 函数获取:

public SteamVR_Action_Vector2 moveAction = SteamVR_Input.GetAction<SteamVR_Action_Vector2>("platformer", "Move");
public SteamVR_Action_Boolean jumpAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("platformer", "Jump");
public SteamVR_Action_Boolean disappearAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("platformer", "Disappear");

这三个动作可以用三个变量表示:

private Vector3 movement;
private bool jump;
private bool disappear;

为了获取输入源的类型(左手柄或右右手柄),可定义一个 SteamVR_Input_Sources 类型(其实就是一个枚举类型)的变量 hand:

private SteamVR_Input_Sources hand;

在遥控器中引用玩偶变量:

public JoeJeff character;

通过 interactable.attachedToHand 变量即可确定遥控器是否被玩家(手柄)抓住。如果是的话,在通过 interactable.attachedToHand.handType 判断抓取该物体的是左手柄还是右手柄。之后,通过 moveAction[hand].axis 可获得玩家的手指在触控板上的位置,作为玩偶移动方向的依据;通过 jumpAction[hand].stateDown 可判断玩家是否按下了触控板,作为玩偶是否跳跃的依据;通过 disappearAction[hand].stateDown 可判断玩家是否按下了握持键,作为让玩偶消失或出现的依据。

private void Update() {
    if (interactable.attachedToHand) {
        hand = interactable.attachedToHand.handType;
        Vector2 m = moveAction[hand].axis;
        movement = new Vector3(m.x, 0, m.y);

        jump = jumpAction[hand].stateDown;
        glow = Mathf.Lerp(glow, jumpAction[hand].state ? 1.5f : 1.0f, Time.deltaTime * 20);

        if (disappearAction[hand].stateDown) {
            disappear = !disappear;
        }
    }
    else {
        movement = Vector2.zero;
        jump = false;
        glow = 0;
    }

    Joystick.localPosition = movement * joyMove;
    float rot = transform.eulerAngles.y;
    movement = Quaternion.AngleAxis(rot, Vector3.up) * movement;
    jumpHighlight.sharedMaterial.SetColor("_EmissionColor", Color.white * glow);

    if (disappear) {
        character.Disappear();
    }
    else {
        character.Appear();
        character.Move(movement * 2, jump);
    }
}

实际效果

  • 按下握持键,玩偶消失。

  • 触摸触控板,玩偶移动。

  • 点击触控板,玩偶跳跃。

Updated: