Hololens+Unity手势交互脚本开发

Unity手部关节示意图:
手部关节述

脚本实现的功能:

  • 定位左右手位置;
  • 跟踪手部某个关节的空间位置变换;
  • 判断手部姿态;
  • 物体能够跟随手部移动。

具体实现过程请看以下代码:

using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Utilities;
using MRTKExtensions.Gesture;
using UnityEngine;

namespace MRTKExtensions.Gesture
{
    public static class GestureUtils
    {
        private const float PinchThreshold = 0.7f;
        private const float GrabThreshold = 0.4f;

        public static bool IsPinching(Handedness trackedHand)
        {
            return HandPoseUtils.CalculateIndexPinch(trackedHand) > PinchThreshold;
        }

        public static bool IsGrabbing(Handedness trackedHand)
        {
            return !IsPinching(trackedHand) &&
                   HandPoseUtils.MiddleFingerCurl(trackedHand) > GrabThreshold &&
                   HandPoseUtils.RingFingerCurl(trackedHand) > GrabThreshold &&
                   HandPoseUtils.PinkyFingerCurl(trackedHand) > GrabThreshold &&
                   HandPoseUtils.ThumbFingerCurl(trackedHand) > GrabThreshold;
        }
    }
}

namespace MyHandPoseControl
{
    public class HandPose: MonoBehaviour
    {
        //SerializeField 非常方便的功能可以帮助用户将下面的 成员变量 在Inspector中显示并修改
        [SerializeField]
        //TrackedHandJoint跟踪哪个手部关节,这里是食指中间关节 
        private TrackedHandJoint trackedHandJoint = TrackedHandJoint.IndexMiddleJoint;

        [SerializeField]
        private float grabDistance = 0.1f;

        [SerializeField]
        //左右手手势识别
        private Handedness trackedHand = Handedness.Both;

        [SerializeField]
        //可以拇指食指捏
        private bool trackPinch = true;

        [SerializeField]
        //可以握拳抓
        private bool trackGrab = true;

        private IMixedRealityHandJointService handJointService;

        //?的用法:用于判断对象是否为空,如果对象为空,则无论该对象调用什么皆不会抛出异常,直接返回null
        //??的用法: 可用于判断一个变量在为null时返回一个指定的值。例如,a??1 等价于 a==null?1:a
        //Link: https://blog.csdn.net/qq_40323256/article/details/86771753
        private IMixedRealityHandJointService HandJointService =>
            handJointService ??
            (handJointService = CoreServices.GetInputSystemDataProvider<IMixedRealityHandJointService>());

        private MixedRealityPose? previousLeftHandPose;
        private MixedRealityPose? previousRightHandPose;

        private void Update()
        {
            var leftHandPose = GetHandPose(Handedness.Left, previousLeftHandPose != null);
            var rightHandPose = GetHandPose(Handedness.Right, previousRightHandPose != null);
            {
                var jointTransform = HandJointService.RequestJointTransform(trackedHandJoint, trackedHand);
                if (rightHandPose != null && previousRightHandPose != null)//有右手
                {
                    //在有右手的情况下,又同时存在左手
                    if (leftHandPose != null && previousLeftHandPose != null)
                    {
                        // fight! pick the closest one
                        var isRightCloser = Vector3.Distance(rightHandPose.Value.Position, jointTransform.position) <
                                            Vector3.Distance(leftHandPose.Value.Position, jointTransform.position);

                        ProcessPoseChange(
                            isRightCloser ? previousRightHandPose : previousLeftHandPose,
                            isRightCloser ? rightHandPose : leftHandPose);
                    }
                    else//只有右手
                    {
                        ProcessPoseChange(previousRightHandPose, rightHandPose);
                    }
                }
                else if (leftHandPose != null && previousLeftHandPose != null)//有左手(因为上面讨论了,所以在这里没有考虑同时有右手的情况)
                {
                    ProcessPoseChange(previousLeftHandPose, leftHandPose);
                }
            }
            previousLeftHandPose = leftHandPose;
            previousRightHandPose = rightHandPose;
        }

        private MixedRealityPose? GetHandPose(Handedness hand, bool hasBeenGrabbed)
        {
            if ((trackedHand & hand) == hand)//确保当前手是在预设范围内
            {
                //如果检测到手且((当前手势是捏且允许捏)||(当前手势是握拳且允许握拳))
                if (HandJointService.IsHandTracked(hand) &&
                    ((GestureUtils.IsPinching(hand) && trackPinch) ||
                     (GestureUtils.IsGrabbing(hand) && trackGrab)))
                {
                    //关节和手掌的空间变换
                    //var类似于C++的auto类型
                    var jointTransform = HandJointService.RequestJointTransform(trackedHandJoint, hand);
                    var palmTransForm = HandJointService.RequestJointTransform(TrackedHandJoint.Palm, hand);

                    if (hasBeenGrabbed ||
                       Vector3.Distance(gameObject.transform.position, jointTransform.position) <= grabDistance)//手已经抓住物体或手离物体的距离足够近
                    {
                        return new MixedRealityPose(jointTransform.position, palmTransForm.rotation);
                    }
                }
            }

            return null;
        }

        private void ProcessPoseChange(MixedRealityPose? previousPose, MixedRealityPose? currentPose)
        {
            var delta = currentPose.Value.Position - previousPose.Value.Position;
            var deltaRotation = Quaternion.FromToRotation(previousPose.Value.Forward, currentPose.Value.Forward);
            gameObject.transform.position += delta;
            gameObject.transform.rotation *= deltaRotation;
        }
    }
}

结果

绿色模型——可以捏,但不可以握拳控制;
黄色模型——不可以捏,但可以握拳控制;
蓝色模型——既可捏又可握拳控制。
请添加图片描述

Reference

https://localjoost.github.io/Basic-hand-gesture-recognition-with-MRTK-on-HoloLens-2/

  • 2
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值