Nreal Light开发-lesson 4 NRSDK中的手势识别

请在前几讲的基础上进行

一、准备工作

1.在Assets>Scenes中新建场景,重命名为HandTrackingLearning,并打开。

2、打开Assets>NRSDK>Models>Hands>RightHand

3、将其另存为Paper.prefabRock.prefabScissors.prefab

4、打开Scissors,注意左侧的Hierarchy(层级)栏,下面解释一下各部分的含义(个人理解,并未找到官方说明,建议亲自实验):

  • wrist 手腕
  • middle_1 中指从下数第1个关节
  • middle_2 中指从下数第2个关节
  • middle_3 中指第3个关节
  • middle_end 中指指尖(作用不清楚)
    接下来的各部分只是对应的指头不同,但含义相同
  • pointer 食指
  • ring 无名指
  • thumb 大拇指
  • pinky 小拇指
    注意:pinky上方还有一级,名为:wristpadding,指的是小拇指下方、掌纹处的关节;同样,thumb_1实际上也不是通常意义上的大拇指第一节,而是大拇指的掌处关节。

5、将Scissors的手型调整为游戏“石头剪刀布”中“剪刀”的手型。
为了节约大家时间,大家可以通过本链接( 提取码:fs51)下载我调整好的手型,不过是在是有些难看,大家可以自行调整一下。

6、将Rock调整为“拳头”的手型。

二、正式开发

1、打开先前创建的HandTrackingLearning,删除场景中的Main Camera,新建NRCameraRigNRInputCanvas(注:前两个部件均位于Assets>NRSDK>Prefabs,拖入Hierarchy(层级)栏即可;新建Canvas的步骤为:在Hierarchy(层级)栏空白处右击,选择UI>Canvas(画布)。详见下面的若干图片。)

删除和创建完成后,Hierarchy(层级)栏应如下图所示:

2、点开Canvas,将其Render Mode(渲染模式)改为World Space(世界坐标),然后重置其坐标。

4、打开NRInput,将Input Source Type改为Hands

5、在游戏中,我们需要一个按钮来开始,所以在Canvas下面先创建一个空对象,命名为StartButton,再在空对象下创建一个按钮,方法如图:

创建好后如下图:

打开Text,修改文本和字体大小。(下图为推荐数据)

调整开始按钮的位置,使得能够在相机视角中看到它。(在我们完成整个项目后,认为将位置Z设为10,缩放设为0.05,0.05,1更为合适,所以请忽视下图的数据)

6、与上述同理,在Canvas下再创建4个空对象,分别命名为StartWinLoseTie,将其位置改为x=0,y=30,z=350。四个对象下分别创建Text,内容改为Ready...3,2,1,Go!You Win!You Lost!You’re Tied!,字体大小改为20,颜色改为白色(数据及颜色均为推荐数据和推荐颜色)。创建后如图所示:



7、分别打开StartWinLoseTie四个对象,在检查器一栏中取消勾选。效果如图所示。(注:在我们进行完全部开发后,我们认为将Start也取消勾选更为合适,但由于后面的教程图片已经完成,所以请您主动忽视跟此相关的问题)

编写脚本

1、在Assets下新建一个文件夹Scripts,并打开,新建C#脚本,重命名为MenuManager,用来控制这些提示语的出现。

打开该脚本,将以下代码编写到脚本中。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MenuManager : MonoBehaviour
{
    [SerializeField] GameObject[] menus;
    public static MenuManager Instance;

    private void Awake()
    {
        Instance = this;
    }
    public void OpenMenu(string menuName)
    {
        foreach(var item in menus)
        {
            item.SetActive(false);
        }
        foreach(var item in menus )
        {
            if(item.name==menuName)
            {
                item.SetActive(true);
                break;
            }
        }
    }
}

2、创建一个名为RPSHand的脚本,用来写判断类。编写以下代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NRKernal;

public class RPSHand : MonoBehaviour
{
    public string name;
    public virtual string Judge(string _name)
    {
        return null;
    }
}

3、创建一个名为RPSManager的脚本,用来编写我们需要的游戏逻辑。编写以下代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NRKernal;
using UnityEngine.UI;
using System.Globalization;

public class RPSManager : MonoBehaviour
{
    [SerializeField] GameObject[] hands;
    [SerializeField] Text gestureTxt;
    private bool isPlaying;
    private GameObject currentHand;
    public void OnClick()
    {
        if (isPlaying) return;
        isPlaying = true;
        foreach(var item in hands) 
        {
            item.SetActive(false);
        }
        MenuManager.Instance.OpenMenu("Start");
        StartCoroutine("DelayHand");
    }
    IEnumerator DelayHand() 
    {
        yield return new WaitForSeconds(3);
        currentHand = hands[Random.Range(0, hands.Length)];
        currentHand.SetActive(true);
        if(currentHand.GetComponent<RPSHand>().Judge(gestureTxt.text)=="win")
        {
            MenuManager.Instance.OpenMenu("Win");
        }
        else if(currentHand.GetComponent<RPSHand>().Judge(gestureTxt.text) == "lose")
        {
            MenuManager.Instance.OpenMenu("Lose");
        }
        else
        {
            MenuManager.Instance.OpenMenu("Tie");
        }
        isPlaying= false;
    }
}

4、接下来是三种手势的判断逻辑,同样是在Scripts的文件夹下,分别创建三个脚本,RockHandPaperHandScissorHand。三个脚本分别如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RockHand : RPSHand
{
    public override string Judge(string _name)
    {
        if (_name == "R:Paper")
        {
            return "win";
        }
        else if (_name == "R:Rock")
        {
            return "tie";
        }
        else return "lose";
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PaperHand : RPSHand
{
    public override string Judge(string _name)
    {
        if (_name == "R:Paper")
        {
            return "tie";
        }
        else if (_name == "R:Rock")
        {
            return "lose";
        }
        else return "win";
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScissorHand : RPSHand
{
    public override string Judge(string _name)
    {
        if (_name == "R:Paper")
        {
            return "lose";
        }
        else if (_name == "R:Rock")
        {
            return "win";
        }
        else return "tie";
    }
}

四、脚本使用

1、在Hierarchy(层级)栏中点开Canvas,将脚本MenuManager拖入,点击Menus,点击4次加号,将Hierarchy(层级)栏中StartWinLoseTie依次拖入Menus的四个元素。点击Add Component(添加组件),搜索组件Canvas Raycast Target并添加。
在这里插入图片描述
2、新建一个空对象并命名为GameManager,将先前创建的三个手势模型拖入GameManager下作为子对象,然后调整三个子对象的模型位置直至出现在合适的视角范围内。(注:我们建议将三个手掌的位置全部设为(x=0,y=0,z=0),这是一个更好的位置。由于这是我们在完成整个项目开发之后发现的,所以后面的图片全部都存在与此相关的问题,请您忽视)

3、分别打开上述三个模型子对象,分别添加名为Animator的组件,再添加对应的控制脚本文件,最后取消勾选最上方的选项。

4、将RPSManager关联到GameManager这个对象上,并将三个手势模型添加到Hands下面。

5、在资产中搜索NRHand,将NRHand_LNRHand_R分别拖入NRInput中作为其中RightLeft的子对象。

五、手势识别

1、在资产中,搜索HandTracking,将名为HandTracking的场景拖入层级栏中。将HandTracking场景的NRInput>Right(或Left)>NRHand_R(NRHand_L)>GestureSimpleTip_R(GestureSimpleTip_L)复制粘贴HandTrackingLearning场景中NRInput>Right(或Left)>NRHand_R(NRHand_L)子对象

2、在层级栏中移除整个HandTracking场景。

3、在Assets>Scripts中新建脚本GestureRPSTip,内容如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NRKernal;
using NRKernal.NRExamples;
using System.Runtime;

public class GestureRPSTip : GestureSimpleTip
{
    public class GestureRPSName
    {
        public const string Gesture_Point = "Point";
        public const string Gesture_Rock = "Rock";
        public const string Gesture_Scissor = "Scissors";
        public const string Gesture_Paper = "Paper";
    }
    public override void UpdateGestureTip()
    {
        var handState = NRInput.Hands.GetHandState(handEnum);
        if (handState == null)
            return;
        switch (handState.currentGesture)
        {
            case HandGesture.Point:
                gestureTxt.text = string.Empty;
                break;
            case HandGesture.Grab:
                gestureTxt.text = GetHandEnumLabel() + GestureRPSName.Gesture_Rock;
                break;
            case HandGesture.Victory:
                gestureTxt.text = GetHandEnumLabel() + GestureRPSName.Gesture_Scissor;
                break;
            case HandGesture.OpenHand:
                gestureTxt.text = GetHandEnumLabel() + GestureRPSName.Gesture_Paper;
                break;
            default:
                gestureTxt.text = string.Empty;
                break;
        }
        if (handState.isTracked)
        {
            Pose palmPose;
            if (handState.jointsPoseDict.TryGetValue(HandJointID.Palm, out palmPose))
            {
                UpdateAnchorTransform(palmPose.position);
            }
            tipAnchor.gameObject.SetActive(!string.IsNullOrEmpty(gestureTxt.text));
        }
        else
        {
            tipAnchor.gameObject.SetActive(false);
        }
    }
}

4、在资产中寻找GestureSimpleTip脚本并打开。

将代码第33private改为virtual public,8195private改为virtual public

5、打开两个GestureSimpleTip对象,将原有的Gesture Simple Tip组件移除,再将新创建好的GestureRPSTip文件关联上,调整Hand Fnum(左手选Left Hand,右手选Right Hand),再将层级栏中的NRInput>Right(Left)>NRHand_R(NRHand_L)>GestureSimpleTip_R(GestureSimpleTip_L)>TipAnchorNRInput>Right(Left)>NRHand_R(NRHand_L)>GestureSimpleTip_R(GestureSimpleTip_L)>TipAnchor>Canvas>PanelBg>GestureTxt两个对象拖入Tip AnchorGesture Txt

6、在Assets下新建文件夹Controller,进入后新建三个动画控制器,命名为PaperRockScissor


7、将三个动画控制器拖入相应模型的Animator控制器中。

8、打开Canvas>StartButton>Button,找到鼠标点击,点击+,将GameManager拖入,最后选择RPSManager.OnClick

六、导出文件

像以往一样将文件导出,在Nreal Light中运行即可。

七、后记

由于我们教程的制作也是在学习他人教程的同时进行的,在本教程的制作过程中,我们参考的教程出现多处操作没有进行说明,导致我们最后的项目一度处于停滞不前的状态。在经过多日的研究、查阅资料后,我们才总算解决所有问题,利用markdown写下了这篇9100余字的教程。由于过程中我们也有部分步骤由于没有经验而出错,所以图片上会存在问题。如有任何需要帮助的地方,欢迎留言。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值