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


◆ 写在开头

上一回、写了关于Oculus Quest 中Hand tracking功能的使用的内容。
所以这次的内容上,关于如何使用Hand tracking实现抓住物体的方法。

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

如果制作中用什么问题或不明点,
有需要的话可以参考下面两条内容。
用Unity开发Oculus Quest用的APP 导入APP、进行运行状态确认
抓住物体吧(物体的设置与制作)
Hand tracking功能的使用

关于这次的内容,抓住物体的实行方法
完全属于自己想当然的做法,参考性比较低……请谅解。

◆ 开发环境

macOS Mojave 版本 10.14.6
Unity 2018.4.12f1
Android SDK
为了能够使用Hand tracking的功能,
Oculus的版本必须得是(Ver12)或以上。(需要更新的请即时更新)

◆ 制作顺序

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

准备阶段的说明


首先准备必要的Object,
在这里我们意见实装了Hand tracking功能、准备好了自带物理的几个测试用方块。
方块的尺寸颜色等请自由设置(方便看清就行)。

为了更直观的显示代码部分是否有正确实行等、
这里制作了观测用Canvas,当然这个只是起到了方便看的作用并不是必须要制作的东西,
只是单纯的有了会更直观而已。
当然桌子也不是必须品。

实现【抓住】功能

以前在抓住物体吧(物体的设置与制作)中,只要成功的在画面中表示手,便可以直接实行抓取,
但Hand tracking好像没有办法直接抓取的样子。

所以,
这次要做的事情就是,
当手触碰到物体且作出了抓住物体的动作时,把被触碰物体放入手的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」拖入到方块中,完成必要的设置就算完成了。

动作确认

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