OculusQuest のハンドトラッキングで指の関節情報などを取得する方法


OVRSkelton を利用すると、片手で24箇所の位置（以下の図の球の箇所）が取得できます。
![lefthand.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/974424ca-1c28-acf4-cc79-13fd0b881851.jpeg)

# 前提
Unity 2019.3.0f3
Oculus Integration 12.0

# OVRSkelton.cs について
Oculus Integration でハンドトラッキングする時に追加する、OVRHandTrackingPrefab に最初から設定されているプログラムです。このプログラムが持っている Bones（OVRBoneというクラスのリスト）と、各位置を指し示す BoneId を利用することで、指の関節などの情報を取得します。

この方の記事がわかりやすいです。（この記事を書く前に知りたかった・・・）
https://qiita.com/divideby_zero/items/4949fadb2c60f810b3aa

## 部位取得の概要
| 部位     | 取得箇所数 | 指定方法の概要                 |
|---------|------------|----------------------------|
| 親指     | 5          | 付け根側から 0, 1, 2, 3, tip |
| 人差し指  | 4          | 付け根側から 1, 2, 3, tip    |
| 中指     | 4          | 付け根側から 1, 2, 3, tip    |
| 薬指     | 4          | 付け根側から 1, 2, 3, tip    |
| 小指     | 5          | 付け根側から 0, 1, 2, 3, tip |
| 手首     | 2          | WristRoot, ForearmStub     |

## 部位を指定するBoneIdの詳細
（上の図を見た方が解りやすい）

| BoneId           | 部位の説明                                          |
|------------------|-----------------------------------------------------|
| Hand_WristRoot   | 手の付け根                                          |
| Hand_ForearmStub | 腕でも手に近いところ                                |
| Hand_Thumb0      | 親指の第3関節から手首側に小さな骨があり、その付け根 |
| Hand_Thumb1      | 親指の第3関節                                       |
| Hand_Thumb2      | 親指の第2関節                                       |
| Hand_Thumb3      | 親指の第1関節                                       |
| Hand_ThumbTip    | 親指の先端                                          |
| Hand_Index1      | 人差し指の第3関節                                   |
| Hand_Index2      | 人差し指の第2関節                                   |
| Hand_Index3      | 人差し指の第1関節                                   |
| Hand_IndexTip    | 人差し指の先端                                      |
| Hand_Middle1     | 中指の第3関節                                       |
| Hand_Middle2     | 中指の第2関節                                       |
| Hand_Middle3     | 中指の第1関節                                       |
| Hand_MiddleTip   | 中指の先端                                          |
| Hand_Ring1       | 薬指の第3関節                                       |
| Hand_Ring2       | 薬指の第2関節                                       |
| Hand_Ring3       | 薬指の第1関節                                       |
| Hand_RingTip     | 薬指の先端                                          |
| Hand_Pinky0      | 小指の第3関節から手首側に伸びている骨の付け根       |
| Hand_Pinky1      | 小指の第3関節                                       |
| Hand_Pinky2      | 小指の第2関節                                       |
| Hand_Pinky3      | 小指の第1関節                                       |
| Hand_PinkyTip    | 小指の先端                                          |

# 外部から値を取得するプログラム
## サンプル

```C#
using UnityEngine;

public class Hand : MonoBehaviour
{
    [SerializeField] private OVRSkeleton skeleton;

    [SerializeField] private Transform thumb0;
    [SerializeField] private Transform thumb1;
    [SerializeField] private Transform thumb2;
    [SerializeField] private Transform thumb3;
    [SerializeField] private Transform thumbTip;
    [SerializeField] private Transform index1;
    [SerializeField] private Transform index2;
    [SerializeField] private Transform index3;
    [SerializeField] private Transform indexTip;
    [SerializeField] private Transform middle1;
    [SerializeField] private Transform middle2;
    [SerializeField] private Transform middle3;
    [SerializeField] private Transform middleTip;
    [SerializeField] private Transform ring1;
    [SerializeField] private Transform ring2;
    [SerializeField] private Transform ring3;
    [SerializeField] private Transform ringTip;
    [SerializeField] private Transform pinky0;
    [SerializeField] private Transform pinky1;
    [SerializeField] private Transform pinky2;
    [SerializeField] private Transform pinky3;
    [SerializeField] private Transform pinkyTip;

    [SerializeField] private Transform start;
    [SerializeField] private Transform wristRoot;
    [SerializeField] private Transform forearmStub;
    [SerializeField] private Transform maxSkinnable;
    [SerializeField] private Transform end;
    
    void Update()
    {
        thumb0.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Thumb0].Transform.position;
        thumb1.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Thumb1].Transform.position;
        thumb2.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Thumb2].Transform.position;
        thumb3.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Thumb3].Transform.position;
        thumbTip.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_ThumbTip].Transform.position;

        index1.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Index1].Transform.position;
        index2.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Index2].Transform.position;
        index3.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Index3].Transform.position;
        indexTip.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_IndexTip].Transform.position;

        middle1.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Middle1].Transform.position;
        middle2.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Middle2].Transform.position;
        middle3.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Middle3].Transform.position;
        middleTip.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_MiddleTip].Transform.position;

        ring1.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Ring1].Transform.position;
        ring2.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Ring2].Transform.position;
        ring3.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Ring3].Transform.position;
        ringTip.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_RingTip].Transform.position;

        pinky0.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Pinky0].Transform.position;
        pinky1.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Pinky1].Transform.position;
        pinky2.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Pinky2].Transform.position;
        pinky3.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Pinky3].Transform.position;
        pinkyTip.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_PinkyTip].Transform.position;

        wristRoot.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_WristRoot].Transform.position;
        forearmStub.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_ForearmStub].Transform.position;

        start.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_Start].Transform.position;
        // maxSkinnable.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_MaxSkinnable].Transform.position;
        end.position = skeleton.Bones[(int) OVRSkeleton.BoneId.Hand_End].Transform.position;
    }
}
```

## 補足
* Hand_Start、Hand_MaxSkinnable、Hand_End はBoneIdとしては使用されておらず、プログラムを読む限り、Enum の中で値を仕切るために使用されているように見える。
    * このプログラムを実行しても、Hand_Start、Hand_Endの球は表示されることがない。
    * Hand_MaxSkinnable をコメントアウトしているのは、親指の先端と同じ値が設定されているため。コメントを外すと親指の先端箇所に Hand_MaxSkinnable の球が表示される。
* OVRSkelton の BoneId の enum の中に、 `// add new bones here` とコメントがあることから、これからまだ何かしらの値を追加していく想定見て取れる。それを見越しての、Hand_Start などなのだと思われる。