Valveコントローラからの入力を一括で管理する

#概要

ValveIndexのコントローラから指の情報が取れたり、SteamVRがver.2でUIの大幅な変更(だいぶ前ですが)があったりなど、自分がVRを始めたころから色々変わってきたなと感じる今日この頃です。
コントローラから入力を取りたいときに調べるのも面倒なので、自分なりにまとめてみました。

#環境 

* Unity 2019.2.8f1
* SteamVR Unity Plugin v2.2.0
* SteamVR
* VALVE INDEX

(プラグインや初期設定などは今回は省略します)

#コントローラからの入力を取得する

##入力を一括で管理するスクリプトの作成

```c#:ValveController.cs
using UnityEngine;
using Valve.VR;
using System;

public class ValveController : MonoBehaviour
{
    //取得する手（右手か左手か任意の手）
    public SteamVR_Input_Sources handType;

    #region ボタンに変化があった時のイベント
    public event Action TriggerPress;
    public event Action TriggerPressDown;
    public event Action TriggerPressUp;

    public event Action GrabPinchDown;
    public event Action GrabPinchUp;

    public event Action TeleportPressDown;
    public event Action TeleportPressUp;

    public event Action ButtonAPress;
    public event Action ButtonAPressDown;
    public event Action ButtonAPressUp;

    public event Action ButtonBPress;
    public event Action ButtonBPressDown;
    public event Action ButtonBPressUp;

    public event Action JoyStickChanged;

    public event Action SnapTurnRightPress;
    public event Action SnapTurnLeftPress;
    #endregion

    #region ボタンUI

    public SteamVR_Action_Single Trigger;

    public SteamVR_Action_Boolean Trigger_Touch;

    public SteamVR_Action_Boolean Teleport;

    public SteamVR_Action_Boolean GrabPinch;

    public SteamVR_Action_Boolean GrabGrip;

    public SteamVR_Action_Pose Pose;

    public SteamVR_Action_Skeleton SkeletonHand;

    public SteamVR_Action_Single Squeeze;

    public SteamVR_Action_Boolean HeadsetOnHead;

    public SteamVR_Action_Boolean SnapTurnLeft;
    public SteamVR_Action_Boolean SnapTurnRight;

    public SteamVR_Action_Boolean Button_A;

    public SteamVR_Action_Boolean Button_B;

    public SteamVR_Action_Vector2 JoyStick;

    #endregion

    public bool DebugMessage;
    private ButtonState _ButtonAState;
    private ButtonState _ButtonBState;
    private ButtonState _TriggerState;

    private enum ButtonState
    {
        Normal,
        Pushing,
    }

    private void Start()
    {
        _ButtonAState = ButtonState.Normal;
        _ButtonBState = ButtonState.Normal;
        _TriggerState = ButtonState.Normal;
    }

    private void Update()
    {
        ControllerFunction();
    }
    private void ControllerFunction()
    {
        if (Trigger_Touch.GetStateDown(handType))
        {
            TriggerPressDown?.Invoke();
            _TriggerState = ButtonState.Pushing;
            DebugLog("トリガーを押した");
        }

        if (Trigger_Touch.GetStateUp(handType))
        {
            TriggerPressUp?.Invoke();
            _TriggerState = ButtonState.Normal;
            DebugLog("トリガーを離した");
        }

        if (JoyStick.GetChanged(handType))
        {
            DebugLog("Joystickが変わった");
            JoyStickChanged?.Invoke();
        }
        if (GrabPinch.GetStateDown(handType))
        {
            GrabPinchUp?.Invoke();
            DebugLog("親指 Down?");
        }

        if (GrabPinch.GetStateUp(handType))
        {
            GrabPinchUp?.Invoke();
            DebugLog("親指 Up?");
        }

        if (Teleport.GetStateDown(handType))
        {
            TeleportPressDown?.Invoke();
            DebugLog("親指を押した");
        }

        if (Teleport.GetStateUp(handType))
        {
            TeleportPressUp?.Invoke();
            DebugLog("親指を離した");
        }

        if (SnapTurnRight.GetStateDown(handType))
        {
            DebugLog("TurnRight-東");
            SnapTurnRightPress?.Invoke();
        }
        if (SnapTurnLeft.GetStateDown(handType))
        {
            DebugLog("TurnLeft-西");
            SnapTurnLeftPress?.Invoke();
        }
        if (Button_A.GetStateDown(handType))
        {
            ButtonAPressDown?.Invoke();
            _ButtonAState = ButtonState.Pushing;
            DebugLog("ボタンAを押した");
        }

        if (Button_A.GetStateUp(handType))
        {
            ButtonAPressUp?.Invoke();
            if (_ButtonAState == ButtonState.Pushing)
            {
                ButtonAPress?.Invoke();
                _ButtonAState = ButtonState.Normal;
                DebugLog("ボタンAを押して離した");
            }
            DebugLog("ボタンAを離した");
        }

        if (Button_B.GetStateDown(handType))
        {
            ButtonBPressDown?.Invoke();
            _ButtonBState = ButtonState.Pushing;
            DebugLog("ボタンBを押した");
        }

        if (Button_B.GetStateUp(handType))
        {
            ButtonBPressUp?.Invoke();
            if (_ButtonBState == ButtonState.Pushing)
            {
                ButtonBPress?.Invoke();
                _ButtonBState = ButtonState.Normal;
                DebugLog("ボタンBを押して離した");
            }
            DebugLog("ボタンBを離した");
        }
    }

    private void DebugLog(string msg)
    {
        if (DebugMessage)
        {
            Debug.Log(msg);
        }
    }

    public float GetTriggerValue()
    {
        return Trigger.GetAxis(handType);
    }

    public float GetSqueezeValue()
    {
        return Squeeze.GetAxis(handType);
    }

    public Vector2 GetJoyStickValue()
    {
        return JoyStick.GetAxis(handType);
    }


    public int[] GetFingerValues()
    {
        return new int[5]
        {
            (int)(SkeletonHand.thumbCurl * 100),
            (int)(SkeletonHand.indexCurl * 100),
            (int)(SkeletonHand.middleCurl * 100),
            (int)(SkeletonHand.ringCurl * 100),
            (int)(SkeletonHand.pinkyCurl * 100)
        };
    }

    public bool GetTriggerPushing()
    {
        if (_TriggerState == ButtonState.Pushing) return true;
        return false;
    }

    public bool GetButtonAPushing()
    {
        if (_ButtonAState == ButtonState.Pushing) return true;
        return false;
    }

    public bool GetButtonBPushing()
    {
        if (_ButtonBState == ButtonState.Pushing) return true;
        return false;
    }
}

```
##解説
(解説というか軽くポイントだけ)

* イベント入力で他のスクリプトから参照できるようにします
* 使用したいボタンはValve.VRのアクションから取得します
* ボタンの入力を2状態だけでなく、押し続けている状態も取れるようにしてあります
* 左右両方に同じスクリプトを使用して区別できるように、handTypeを用意しました
* デバッグログは一応オンオフで切り替えられるようにしました

#Actionの追加
プラグインの仕様変更でActionの定義を手動で追加しなければいけなくなりました。
先ほどスクリプトから設定したボタンUIのアクションを追加していきます。

入力の取得にはSteamVR Inputを使って行っていきます。
細かい操作について写真を撮って説明する元気がなかったので、先人の知識をお借りして参考サイトのリンクを最後に載せておきます。

簡単に説明すると、
1. UnityでSteamVR InputからActionsの＋ボタンで追加し、ValveContoller.csで定義した名前を割り当てる(ボタンUIの部分を参照)
2. Typeを割り当てあられた変数の型と同様にする
例: SteamVR_Action_SingleならVector1、SteamVR_Action_Booleanならboolean
注意：あらかじめ用意されているものもあります。といっても、こちらで追加したものは、Trigger_Touch、Button_A、Button_Bくらいです。
3. それぞれ追加したら、Save and generateのボタンを押します
4. SteamVR InputからOpen binding UIのボタンを押して、コントローラのバインド画面を開いてください
5. 取得したい部分を追加していきます。バインドの編集を忘れずに保存してください
トリガーのクリック → trigger_touch
ジョイスティックの位置 → joystick
Aボタンのクリック → button_a
Bボタンのクリック → button_b
6. CameraRigからControllerのオブジェクト左右それぞれにValveControllerのコンポーネントを追加
7. エディタ上の選択から名前と一致するものを追加してく(HandTypeは左右それぞれで分けてください)

Actionの追加は以上です。全然簡単に説明出来なかった...。
めんどくさい作業だけど、一回やってしまえばValveControllerから参照して全部使えるので便利です。

# 使い方

基本的な使い方はだいたいこんな感じです。

```c#:
using Valve.VR;
using UnityEngine.UI;
using System;

public class InputTextManager : MonoBehaviour
{
    public ValveController _controller;

    void Start()
    {
        // デリゲートでアクションを追加
        _controller.TriggerPressDown += InputTriggerDown;
        _controller.ButtonAPressDown += InputButtonA;
    }

    private void Update()
    {
        // トリガーを押し続けているかどうか
        var isTrigger = _controller.GetTriggerPushing();

        // トリガーの引き具合
        var triggerValue = _controller.GetTriggerValue();

        // ボタンAを押し続けているかどうか
        var isButtonA = _controller.GetButtonAPushing();
    }

    // トリガーを引いた時に呼ばれる
    private void InputTriggerDown()
    {
        // トリガーを引いた時に行いたい処理
    }

    // ボタンAを押した押したときに呼ばれる
    private void InputButtonA()
    {
        // ボタンAを押したときに行いたい処理
    }
}
```
# おわりに

ボタンの押した離したの処理を一括で行いたかったので。
結構便利だと思うので使っていただければ幸いです。

(2020/06/10)
諸事情により急遽投稿しため、推敲もしていない雑文で申し訳ないです。
今後、書き直していきます。

#参考サイト
以下のサイトなどを参考にさせていただきました。ありがとうございました。
[SteamVR Unity Plugin v2.2.0でのインプット](https://qiita.com/sakano/items/d87a9b11c23a9bbe166f)