Unity3D中使用Leap Motion进行手势控制

Leap Motion作为一款手势识别设备,相比于Kniect,优点在于精确度。

在我的毕业设计《场景漫游器》的开发中,Leap Motion的手势控制作为重要的一个环节。以此,谈谈开发中使用Leap Motion进行手势识别的实现方式以及需要注意的地方。

一、对Leap Motion的能力进行评估

在设定手势之前,我们必须知道Leap Motion能做到哪种程度,以免在设定方案之后发现很难实现。这个评估依靠实际对设备的使用体验,主要从三个方面:

1.Leap Motion提供的可视化的手势识别界面

2.SDK文档说明

3.Leap商店中的APP

基本可以的得出:

1.Leap Motion的识别对于水平方向或者以水平方向为基础手势能够较好的识别。

2.对于握拳或者垂直的行为识别会出现误差,这种误差和具体的手势行为有关。

3.不应该过分依赖高精确度,Leap Motion能检测到毫米级别是没错的,但是有时候会把你伸直的手指识别成弯曲的,所以要做好最坏的打算。

二、实际的需要

移动、旋转、点击按钮、缩放和旋转物体、关闭程序、暂停,基本的功能需求是这样。

有一些原则:

1.相同环境下的手势应该接近和方便的转换。旋转和移动的之间的转换应该设计的很自然。

2.手势避免冲突,手势过于相似不是什么好事。比如三个伸直的手指和四个伸直的手指不应该被设计成两个手势。当然这不是绝对的,如果你进行一个缓慢的动作并且动作是面向Leap Motion的摄像头,这时候应该相信它,至少要针对这个手势做一个单独的测试。

三、考虑基本的数据结构和算法的轮廓

Leap Motion的SDK在第一部分的时候已经浏览过,最起码能知道Leap Motion可以包含的信息,从SDK看来这是非常丰富的,既然设计自己的手势,那么最好不要依赖于SKD开发包的炫酷的手势。很可能,这些手势只是官方用来演示或者炫耀的。自己设计手势的基本数据结构也有另外的好处,比如更换了体感设备,但是功能是相似的,这时候只需要更改获取数据的方式就可以了(从一个SDK更换到另一个SDK),而不要修改算法。

算法的轮廓与基本数据有很大的关系。所以数据结构一定要尽量的精简并且允许修改(可能某个算法占据了决定性因素,但是开始没考虑到)。

public class HandAndFingersPoint : MonoBehaviour
{
	const int BUFFER_MAX=5;
	Controller m_LeapCtrl;

    <span style="white-space:pre">	</span>public E_HandInAboveView m_AboveView = E_HandInAboveView.None;

	//手指-数据 ,[0]表示左手,[1]表示右手
	private Dictionary<Finger.FingerType,FingerData>[] m_FingerDatas = new Dictionary<Finger.FingerType, FingerData>[2];
	//buffer,[0]表示左手,[1]表示右手,[,n](n属于0,3,表示第n次缓存)
	private Dictionary<Finger.FingerType,FingerData>[,] m_FingerDatasBuffer=new Dictionary<Finger.FingerType, FingerData>[2,BUFFER_MAX];
	private int m_CurBufIndex=0;
	//palm 0:左手 和1:右手
	private PointData[] m_PalmDatas = new PointData[2];

	private readonly PointData m_DefaultPointData = new PointData(Vector.Zero, Vector.Zero);
        private readonly FingerData m_DefaultFingerData = new FingerData(Vector.Zero,Vector.Zero,Vector.Zero);

HandAndFingersPoint类中剩下的部分是对数据的填充、清除、刷新等方法。E_HandInAboveView记录哪只手先进入Leap Motion的视野,用于设定优先级。

另外两个基本的数据结构PointData和FingerData:

//一个手指的数据包含一个指尖点数据和手指根骨的位置数据
public struct FingerData
{
    public PointData m_Point;//指尖的位置和指向
    public Vector m_Position;//手指根骨的位置,对于拇指来说是Proximal phalanges近端指骨的位置

    public FingerData(PointData pointData, Vector pos)
    {
        m_Point = pointData;
        m_Position = pos;
    }

    public FingerData(Vector pointPos, Vector pointDir, Vector pos)
    {
        m_Point.m_Position = pointPos;
        m_Point.m_Direction = pointDir;
        m_Position = pos;
    }

    public void Set(FingerData fd)
    {
	m_Point = fd.m_Point;
	m_Position = fd.m_Position;
    }
}
//一个点的数据,包括方向和位置
public struct PointData
{
    public Vector m_Position;//位置
    public Vector m_Direction;//方向

	public PointData(Vector pos,Vector dir)
	{
		m_Position = pos;
		m_Direction = dir;
	}

	public void Set(PointData pd)
	{
		m_Position = pd.m_Position;
		m_Direction = pd.m_Direction;
	}

	public void Set(Vector pos,Vector dir)
	{
		m_Position = pos;
		m_Direction = dir;
	}
}

//先被看到的手
public enum E_HandInAboveView
{
    None,
    Left,
    Right
}

基本数据定义好之后,最好确认数据的填充是没问题的,实际通过Frame frame = Leap.Controller.Frame();来获取最新的数据。这时候并不急着写完和基本数据相关的方法,现在最终要的是手势算法的合理性。要判断是否合理,最好先写一个算法。

最简单的是伸掌手势,在控制中水平的伸掌用于漫游,垂直的伸掌用于暂停。我发现手掌依赖于手指,而手指包括两个状态——伸直和弯曲。另外,其他的手势,也都是手指的伸直或者弯曲,外加方向的判定累积出各种效果。理所当然的,应该单独写出手指的弯曲和伸直判定算法:

/// <summary>
/// 该方法提供对于单个手指匹配的算法,如伸直,弯曲
/// 以后可能的改变:对于不同的场景可能要求有所不同,这里的阈值也许会随之改变
/// </summary>
public class FingerMatch
{
	//弯曲状态的角度阈值
	static readonly float FingerBendState_Radian = Mathf.PI*4f / 18 ;//40度
	//伸直状态的角度阈值
	static readonly float FingerStrightState_Radian = Mathf.PI/12;//15度

	/// <summary>
	/// 手指伸直的状态,当根骨-指尖的方向和指向的偏差小于阀值时,判定手指为伸直状态。
	/// 注意无效的方向为零向量,先判定是零向量
	/// </summary>
	/// <param name="adjustBorder">对阈值做的微调</param>
	/// <returns></returns>
	public static bool StrightState(FingerData fingerData, float adjustBorder=0f)
	{
		bool isStright =false;
		Vector disalDir = fingerData.m_Point.m_Direction;
		//如果指尖方向为0向量,表示无效的数据
		if (!disalDir.Equals(Vector.Zero))
		{
			Vector fingerDir = fingerData.m_Point.m_Position - fingerData.m_Position;//指尖位置减去指根位置,由指根指向指尖的向量
			float radian = fingerDir.AngleTo(disalDir);

			if (radian < FingerStrightState_Radian + adjustBorder)
			{
				isStright = true;
			}
		}
		return isStright;
	}

	/// <summary>
	/// 判断一根手指是否处于弯曲状态
	/// </summary>
	/// <param name="fingerData">需要判定的手指数据</param>
	/// <param name="bandBorder">弯曲的阈值</param>
	/// <returns></returns>
	public static bool BendState(FingerData fingerData, float adjustBorder=0f)//,out float eulerAugle)
	{
		bool isBend = false;

		//eulerAugle = -1f;
		Vector disalDir = fingerData.m_Point.m_Direction;
		if( !disalDir.Equals(Vector.Zero) )
		{
			Vector fingerDir = fingerData.m_Point.m_Position - fingerData.m_Position;//指尖位置减去指根位置,指跟到指尖的向量

			float radian = fingerDir.AngleTo(disalDir);
			//eulerAugle = radian*180/Mathf.PI;
			//夹角超过定义的阈值时,认定为弯曲状态
			if (radian > FingerBendState_Radian + adjustBorder)
			{
				isBend = true;
			}
		}

		return isBend;
	}

}

上面包含了一个重要的概念——阈值。它是描述到底何种程度算是伸直,何种程度算是弯曲。阈值的确定是需要实际测试来决定的。写到这里也是时候进行一次简单的测试了,毕竟算法的轮廓已经确定。我甚至没写出手掌伸直的判定算法,就确定是可行的。

基本数据结构相关的操作——HandAndFingersPoint类:源代码GitHub链接

该类使用基本数据,在Unity Editor中运行会展示了一个手掌的轮廓,蓝色表示手指的方向,红色表示手指骨根到掌心和指尖的连线,黄色表示掌心到指尖的连线:

四、手势实现中简要的概括

其他代码都可以在我的GitHub:Leap Motion In Unity3D仓库中获取,在手势的实现中,也包含了一些小的技巧,比如对于动作的匹配要防止手指的颤抖引起的误差,采用离散的数据取样——每隔一定时间做一次取样。

使用和观察这些脚本的方式:可以把这些脚本放在一个GameObject中,通过Leap Motion会看到脚本的属性在匹配成功时会发生变化。另外,脚本中包含了事件的注册功能,换句话说,外部可以向任意的手势注册一个事件,以便手势完成匹配或者到达某种匹配状态时做一些额外的处理。这些脚本现在并不能直接完成我们的需求,如暂停。我们需要在这些手势状态或者动作上做进一步的限定,如根据掌心的方向设定垂直向前的手掌为暂停,水平的手掌为平移之类的。

版权声明:随意评论。

时间: 2024-10-15 02:49:50

Unity3D中使用Leap Motion进行手势控制的相关文章

如何在Unity中开发Leap Motion桌面版(Non-VR)APP

最近因需要,翻出几年前的Leapmotion感测器,准备用Unity3D做个互动APP,于是连上官网下载SDK.等下载下来一安装调试,瞬间傻眼,居然要求VR设备.我们Lab倒是不缺VR,有几套VIVE,不过不能保证甲方也有啊,所以得解决这件事. 首先,说明一下新版Leapmotion有2个版本的SDK:1.Orion(VR版)2.V2 Tracking(老版) 所以,得用V2版的,需要安装2个东西:1.V2 Tracking SDK(当前版本 2.3.1)2.Unity Core Assets(

【VR】Leap Motion 官网文档 Unity插件概述

前言: Leap Motion的官网文档已经有不少的热心网友参与了翻译,但没有覆盖官网文档的全部. 为迎合Unity VR的热潮与大家的学习需要,本博客将推出针对Unity方向的官方文档翻译系列. 本篇主要对Leap Motion的Unity插件及坐标系系统进行简述. 英文原文网址:https://developer.leapmotion.com/documentation/unity/unity/Unity_Overview.html 译文首发&持续更新:http://blog.csdn.ne

【VR】Leap Motion 官网文档 Unity资源与插件

前言: Leap Motion的官网文档已经有不少的热心网友参与了翻译,但没有覆盖官网文档的全部. 为迎合Unity VR的热潮与大家的学习需要,本博客将推出针对Unity方向的官方文档翻译系列. 本篇首先对Leap Motion所提供的Unity资源与插件进行概述. 英文原文网址:https://developer.leapmotion.com/documentation/unity/index.html 译文首发&持续更新:http://blog.csdn.net/duzixi Leap M

基于unity3d和leap motion的拼图游戏

最近用unity3d引擎做了一个拼图游戏,会分几次写完,以此作为总结.本文基本查找了网上能查到的所有资料作为参考.也算是大家节省了时间. 目前只完成了拼图部分,leap motion手势控制部分会在后续完成,不过说实话不太看好LM. 项目资源来自 栋笃神探 http://blog.csdn.net/cube454517408/article/details/7907247,不过玩法不同,玩法与小夭 http://game.ceeger.com/forum/read.php?tid=2852相同.

【手势交互】3. Leap Motion

美国 http://www.leapmotion.com/ Leap Motion是一种3D动作控制系统,其创始人在读书期间发现传统的鼠标对于3D软件的控制极为不方便,因此迸发出要设计一套全新的动作控制系统来自由的控制电脑软件.2012年5月,Leap Motion推出的3D动作控制系统完全突破了以前3D控制的概念,为未来的3D交互打开了无限的想象空间,可以在电脑里进行3D手绘.三维模型设计.游戏控制等.这个被称作Leap的技术能让人通过手指直接控制电脑,包括图片缩放.移动.旋转.指令操作.精准

在Unity3D的Legacy动画系统中应用Root Motion

最近仔细比较了Unity3D目前版本中的两套动画系统:Legacy和Mecanim.Mecanim系统功能较之Legacy要强大很多,但是使用AnimatorController着实不方便(尽管使用AnimatorOverrideController可以避免重复编辑状态机),是因为游戏逻辑层面往往要用一个状态机或者类似的机制来控制角色的状态,而角色层面的状态逻辑和动画层面是无法一一对应的,两套复杂的状态机要配合起来...想想就觉得蛋疼啊!难怪很多朋友现在还在使用Legacy动画系统.Legacy

Unity3D中的第三人称镜头的脚本控制

原地址:http://blog.csdn.net/mobanchengshuang/article/details/27591271 好久没有敲Blog了,谢谢大家的留言.关注.私信等支持,但是我好像已经没有办法让自己继续写以前的博客系列了,因为我发现网上关于unity3D的内容太少了,所以我无法自拔地想写U3D相关的文章!!! 第三人称视角 第三人称视角是什么?很简单,CS就是一种第一人称视角游戏,玩家没有办法看到自己的角色形象,只能观察除开自己之外的游戏内容.第三人称视角那么就明显是能够看到

Leap Motion 之Unity 开发指南(一. 基本概念与制作手预制件)

LeapMotion 之Unity开发指南(一.基本概念和手的预制件) [序言] 最近用Leap Motion + 国产的3Glass眼镜构建了房地产.数字矿山的一些应用,感觉这是很经济的一个VR架构方案,比用Oculus和HTC的硬件要省钱得多,并且效果也不错. 在行业VR应用中,其实重在培训教育.生产安全的仿真演练上,更多地通过一些UI互动.基础性的手势(比如开关设备.拾起灭火装备等)来进行交互,这些并不一定需要非常昂贵的激光定位设备.复杂的硬件套件才可实现,而手势识别在未来也是一个趋势.

【VR】Leap Motion 官网文档 手型资源

前言: Leap Motion的官网文档已经有不少的热心网友参与了翻译,但没有覆盖官网文档的全部. 为迎合Unity VR的热潮与大家的学习需要,推出的针对Unity方向的官方文档翻译系列. 第三篇 <手型资源> 介绍了Leap Motion为Unity提供的资源包种的手型预设体资源. Hand Assets  手型资源 The Leap Motion core assets for Unity include a number of pre-made hand prefabs assets.