【用Unity开发Oculus Quest用的APP】用Hand tracking功能抓住物体

#◆ 写在开头
上一回、写了关于[Oculus Quest 中Hand tracking功能的使用](https://qiita.com/Kujirai_sakananosuke/items/b98923ac72730c126aa5e)的内容。
所以这次的内容上，关于如何使用Hand tracking实现抓住物体的方法。

关于手的动作、手指状态的取得等，这里就不详细写了。
具体方法等，请自行查询。

如果制作中用什么问题或不明点，
有需要的话可以参考下面两条内容。
[用Unity开发Oculus Quest用的APP 导入APP、进行运行状态确认](https://qiita.com/Kujirai_sakananosuke/items/7f0dacd447b5fa256796)
[抓住物体吧（物体的设置与制作)](https://qiita.com/Kujirai_sakananosuke/items/62ca694ff20cff962a96)
[Hand tracking功能的使用](https://qiita.com/Kujirai_sakananosuke/items/b98923ac72730c126aa5)

~~关于这次的内容，抓住物体的实行方法
完全属于自己想当然的做法，参考性比较低……请谅解。~~
#◆ 开发环境
macOS Mojave 版本 10.14.6
Unity 2018.4.12f1
Android SDK
为了能够使用Hand tracking的功能，
Oculus的版本必须得是（Ver12）或以上。（需要更新的请即时更新）

#◆ 制作顺序
- 准备阶段的说明
- 实现【抓住】功能
- 手指动作的判断
- 理论关系和代码
- 动作确认

## 准备阶段的说明
<img width="600" alt="スクリーンショット 2020-02-17 18.43.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541380/63ee5242-6f1b-ace1-73fe-644f07ef0936.png">
首先准备必要的Object，
在这里我们意见实装了Hand tracking功能、准备好了自带物理的几个测试用方块。
方块的尺寸颜色等请自由设置（方便看清就行）。

为了更直观的显示代码部分是否有正确实行等、
这里制作了观测用Canvas，当然这个只是起到了方便看的作用并不是必须要制作的东西，
只是单纯的有了会更直观而已。
当然桌子也不是必须品。
##实现【抓住】功能
以前在[抓住物体吧（物体的设置与制作)](https://qiita.com/Kujirai_sakananosuke/items/62ca694ff20cff962a96)中，只要成功的在画面中表示手，便可以直接实行抓取，
但Hand tracking好像没有办法直接抓取的样子。
<img width="283" alt="02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541380/af9be08c-5898-5329-3c3a-29c0bf3dad83.png">
所以，
这次要做的事情就是，
当手触碰到物体且作出了抓住物体的动作时，把被触碰物体放入手的Object中，
也就是说，成为手的子级。

当然，除了成为子级以外，
当发生抓住判定时，被抓物体跟随手一起移动也是实现的方法之一。
##手指动作的判断
```ovrHand.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ovrHand : MonoBehaviour
{
    private OVRHand _ovrHand;
    // 抓住状态的取得
    public bool _catching;

    // Start is called before the first frame update
    void Start()
    {
        _catching = false;
        _ovrHand = this.gameObject.GetComponent<OVRHand>();
    }
    // Update is called once per frame
    void Update()
    {
        if (_ovrHand.GetFingerIsPinching(OVRHand.HandFinger.Index))
        {
            _catching = true;
        }
        else
        {
            _catching = false;
        }
    }
}
```
将上述内容直接拖入「OVRHandPrefab」中。
关于手动作的取得、变数「_ catching」的制作与「_ catching」的说明，之后再说。

GetFingerIsPinching可以判断手指尖是否接触
OVRHand.HandFinger可以判断某手指是否与大拇指接触，这次接触手指的设置是食指。
当食指尖与大拇指触碰时，「_catching」变为true。

~~将「_catching」的状态在Canvas上表示
（和上面讲到的一样Canvas并不是必需品，下面的内容里讲省略全部关于Canvas的内容）~~
##理论关系和代码
```cubeTouch.cs
    private ovrHand _ovrHand;
    private bool _touchIN;      //触碰
    private bool _catch;        //抓住动作
    private bool _hold;         //是否能抓住

    // Start is called before the first frame update
    void Start()
    {
        _ovrHand = FindObjectOfType<ovrHand>();
    }
void Update()
    {
        // 取得手的动作 判断是否抓住
        _catch = _ovrHand._catching;
        touchCube();
        CatchCube();
    }
void touchCube()
    {
        if (_touchIN == true && _catch == false)
        {
            _hold = true;
        }
    }

    void CatchCube()
    {
        if (_hold == true && _catch == true)
        {
            _touchIN = false;
        }
        else if (_touchIN = false && _touchIN == false)
        {
            _hold = false;
        }
    }
```
首先，需要分析关于抓住物体这一动作。

当手是抓住动作状态的时候触碰物体，是不能抓住物体的。
而在手触碰物体后，实行抓住动作便可抓住物体。

将上述内容用代码表示。

```cubeTouch.cs
void OnCollisionStay(Collision other)
    {
        if (other.gameObject.name == "Hand_Index3_CapsuleRigidBody" ||
            other.gameObject.name == "Hand_Index2_CapsuleRigidBody" ||
            other.gameObject.name == "Hand_Thumb3_CapsuleRigidBody" ||
            other.gameObject.name == "Hand_Thumb2_CapsuleRigidBody" 
            )
        {
            _touchIN = true;
            // 固定位置且消除物理す
            gameObject.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
            gameObject.GetComponent<Rigidbody>().isKinematic = true;
        }
    }
```
参考上述内容，
想要实行抓住动作，首先需要从触碰物体开始。

关于手指各个部位的名字请参考最上面的连接。
发生触碰时，「 _touchIN」的状态变为true。

```cubeTouch.cs
void CatchCube()
    {
        if (_hold == true && _catch == true)
        {
            // 变为右手子级
            gameObject.transform.SetParent(_rightHandAnchor.gameObject.transform);
            // 消除重力与触碰判定
            gameObject.GetComponent<Rigidbody>().useGravity = false;
            gameObject.GetComponent<BoxCollider>().isTrigger = true;
            _touchIN = false;
        }
        else if (_touchIN = false && _touchIN == false)
        {
            // 解除父级关系
            gameObject.transform.parent = null;
            // 恢复重力 触碰
            gameObject.GetComponent<Rigidbody>().useGravity = true;
            gameObject.GetComponent<BoxCollider>().isTrigger = false;
            // 恢复位置移动与物理
            gameObject.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
            gameObject.GetComponent<Rigidbody>().isKinematic = false;
            _hold = false;
        }
    }
```
这次准备的方块中因为加入了物理效果，所以当发生抓住判定时，需要将其去掉。
（没放入物理的情况下就没有写的必要了）
请根据自己的状况判断后，将必要的内容写入「CatchCube()」中。
（代码的意思，在上面有写请参考）

最后，把「cubeTouch.cs」拖入到方块中，完成必要的设置就算完成了。
##动作确认
![com.oculus.vrshell-20200218-204038_1_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541380/0a494099-aa38-cc0e-c0f1-85ff3540a39b.gif)

最后导入Oculus，确认是否能够正常运行吧。