├── Common
├── VectorCompare.cs
├── VectorCompare.cs.meta
├── VectorTrans.cs
└── VectorTrans.cs.meta
├── GesturesDeal
├── BaseDataFilter.meta
└── BaseDataFilter
│ ├── Data.meta
│ ├── Data
│ ├── BaseData.cs
│ └── BaseData.cs.meta
│ ├── HandAndFingersPoint.cs
│ ├── HandAndFingersPoint.cs.meta
│ ├── LeapDriver.cs
│ └── LeapDriver.cs.meta
├── GustersPatch
├── Motion.meta
├── Motion
│ ├── FingerClick.cs
│ ├── FingerClick.cs.meta
│ ├── FingerPinch.cs
│ ├── FingerPinch.cs.meta
│ ├── FingerUnPinch.cs
│ ├── FingerUnPinch.cs.meta
│ ├── HandFaceToF.cs
│ ├── HandFaceToF.cs.meta
│ ├── HandOpenToFist.cs
│ ├── HandOpenToFist.cs.meta
│ ├── SingleHandOpenTrack.cs
│ └── SingleHandOpenTrack.cs.meta
├── State.meta
└── State
│ ├── FingerMatch.cs
│ ├── FingerMatch.cs.meta
│ ├── HandBend.cs
│ ├── HandBend.cs.meta
│ ├── HandFingerPoint.cs
│ ├── HandFingerPoint.cs.meta
│ ├── HandOpen.cs
│ ├── HandOpen.cs.meta
│ ├── HandPinchGroup.cs
│ └── HandPinchGroup.cs.meta
└── README.md
/Common/VectorCompare.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 | public class VectorCompare
5 | {
6 | static readonly float VerToXAxisDirThreshold = Mathf.PI * 7 / 18;//垂直x轴的方向阈值[70,180-70]
7 | static readonly float VerToYAxisDirThreshold = Mathf.PI * 7 / 18;//垂直y轴方向的阈值[70,180-70]
8 | static readonly float VerToZAxisDirThreshold = Mathf.PI * 7 / 18;//垂直y轴方向的阈值[70,180-70]
9 |
10 |
11 | ///
12 | /// 一个方向是否垂直+x轴
13 | ///
14 | ///
15 | /// 阈值
16 | /// 垂直true
17 | public static bool VerDirToXAxis(Vector dir,float thresholdAdjust = 0f)
18 | {
19 | bool isVer=false;
20 | float radian = dir.AngleTo(Vector.XAxis);
21 |
22 | if( radian > VerToXAxisDirThreshold+thresholdAdjust &&
23 | radian < (Mathf.PI-(VerToXAxisDirThreshold+thresholdAdjust)))
24 | {
25 | isVer = true;
26 | }
27 | return isVer;
28 | }
29 |
30 |
31 | public static bool VerDirToYAxis(Vector dir, float thresholdAdjust = 0f)
32 | {
33 | bool isVer = false;
34 |
35 | float radian = dir.AngleTo(Vector.YAxis);
36 |
37 | if( radian > VerToYAxisDirThreshold+thresholdAdjust &&
38 | radian < ( Mathf.PI -(VerToYAxisDirThreshold+ thresholdAdjust) ))
39 | {
40 | isVer = true;
41 | }
42 | return isVer;
43 | }
44 | public static bool VerDirToZAxis(Vector dir, float thresholdAdjust = 0f)
45 | {
46 | bool isVer = false;
47 |
48 | float radian = dir.AngleTo(Vector.YAxis);
49 |
50 | if( radian > VerToZAxisDirThreshold+thresholdAdjust &&
51 | radian < ( Mathf.PI -(VerToYAxisDirThreshold+ thresholdAdjust) ))
52 | {
53 | isVer = true;
54 | }
55 | return isVer;
56 | }
57 |
58 | //public static float RadianTo
59 | }
60 |
--------------------------------------------------------------------------------
/Common/VectorCompare.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b0d3e1de4f299464abafcfe434ca61fe
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/Common/VectorTrans.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 | public class VectorTrans
5 | {
6 | public static Vector3 ToUnityVector3(Vector leapVector)
7 | {
8 | Vector3 unityVector3 = new Vector3( leapVector.x,leapVector.y,-leapVector.z);
9 | return unityVector3;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Common/VectorTrans.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9cb140438d66dc440aac77cbbd578da6
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GesturesDeal/BaseDataFilter.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 656d772805deee448b0eef55c6ca380d
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/GesturesDeal/BaseDataFilter/Data.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 270bbca07a7dbd146a8d17d3293ade67
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/GesturesDeal/BaseDataFilter/Data/BaseData.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 |
5 | //一个手指的数据包含一个指尖点数据和手指根骨的位置数据
6 | public struct FingerData
7 | {
8 | public PointData m_Point;//指尖的位置和指向
9 | public Vector m_Position;//手指根骨的位置,对于拇指来说是Proximal phalanges近端指骨的位置
10 |
11 | public FingerData(PointData pointData, Vector pos)
12 | {
13 | m_Point = pointData;
14 | m_Position = pos;
15 | }
16 |
17 | public FingerData(Vector pointPos, Vector pointDir, Vector pos)
18 | {
19 | m_Point.m_Position = pointPos;
20 | m_Point.m_Direction = pointDir;
21 | m_Position = pos;
22 | }
23 |
24 | public void Set(FingerData fd)
25 | {
26 | m_Point = fd.m_Point;
27 | m_Position = fd.m_Position;
28 | }
29 | }
30 | //一个点的数据,包括方向和位置
31 | public struct PointData
32 | {
33 | public Vector m_Position;//位置
34 | public Vector m_Direction;//方向
35 |
36 | public PointData(Vector pos,Vector dir)
37 | {
38 | m_Position = pos;
39 | m_Direction = dir;
40 | }
41 |
42 | public void Set(PointData pd)
43 | {
44 | m_Position = pd.m_Position;
45 | m_Direction = pd.m_Direction;
46 | }
47 |
48 | public void Set(Vector pos,Vector dir)
49 | {
50 | m_Position = pos;
51 | m_Direction = dir;
52 | }
53 | }
54 |
55 | //先被看到的手
56 | public enum E_HandInAboveView
57 | {
58 | None,
59 | Left,
60 | Right
61 | }
--------------------------------------------------------------------------------
/GesturesDeal/BaseDataFilter/Data/BaseData.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 003a47f586dabc24ab47a29c554c644b
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GesturesDeal/BaseDataFilter/HandAndFingersPoint.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 | using System.Collections.Generic;
5 | using System;
6 | ///
7 | /// 此类为数据的模型,收集Leap中需要的数据,并转换成自己需要的格式
8 | /// 整理手掌和指尖的数据信息
9 | /// [可能的数据结构的优化]:这里用零向量表示无效数据,这个操作在ClearNotExistHandData方法中设定,
10 | /// 但是,在FingerData和使用PointData中增加额外的字段IsVaild来表示会更直接一些。
11 | ///
12 | public class HandAndFingersPoint : MonoBehaviour
13 | {
14 | const int BUFFER_MAX=5;
15 | Controller m_LeapCtrl;
16 |
17 | public E_HandInAboveView m_AboveView = E_HandInAboveView.None;
18 |
19 | //手指-数据 ,[0]表示左手,[1]表示右手
20 | private Dictionary[] m_FingerDatas = new Dictionary[2];
21 | //buffer,[0]表示左手,[1]表示右手,[,n](n属于0,3,表示第n次缓存)
22 | private Dictionary[,] m_FingerDatasBuffer=new Dictionary[2,BUFFER_MAX];
23 | private int m_CurBufIndex=0;
24 | //palm 0:左手 和1:右手
25 | private PointData[] m_PalmDatas = new PointData[2];
26 |
27 | private readonly PointData m_DefaultPointData = new PointData(Vector.Zero, Vector.Zero);
28 | private readonly FingerData m_DefaultFingerData = new FingerData(Vector.Zero,Vector.Zero,Vector.Zero);
29 |
30 | //中间量:不再使用。不能作为一个中间存储变量,它会被改变。
31 | //Dictionary m_FingerDataMiddle = new Dictionary ();
32 |
33 | public Dictionary[] FingerDatas
34 | {
35 | get
36 | {
37 | return m_FingerDatas;
38 | }
39 | }
40 |
41 | public PointData[] PalmDatas
42 | {
43 | get
44 | {
45 | return m_PalmDatas;
46 | }
47 | }
48 |
49 | void AddDefaultPalmsData()
50 | {
51 | m_PalmDatas [0]=m_DefaultPointData;
52 | m_PalmDatas [1]=m_DefaultPointData;
53 | }
54 |
55 | ///
56 | /// 设定手指的
57 | ///
58 | void AddDefaultFingersData()
59 | {
60 | DicAddDefaultData(m_FingerDatas [0]) ;
61 | DicAddDefaultData(m_FingerDatas [1]) ;
62 | for(int i=0;i<2;i++)
63 | {
64 | for(int j=0;j
72 | /// 初始化Dictionary的值。
73 | ///
74 | /// Dic.
75 | void DicAddDefaultData(Dictionary dic)
76 | {
77 | dic.Add (Finger.FingerType.TYPE_INDEX,m_DefaultFingerData);
78 | dic.Add (Finger.FingerType.TYPE_MIDDLE,m_DefaultFingerData);
79 | dic.Add (Finger.FingerType.TYPE_PINKY,m_DefaultFingerData);
80 | dic.Add (Finger.FingerType.TYPE_RING,m_DefaultFingerData);
81 | dic.Add (Finger.FingerType.TYPE_THUMB,m_DefaultFingerData);
82 | }
83 |
84 | ///
85 | /// 设定Dictionary的值为默认值。
86 | ///
87 | /// Dic.
88 | void DicUseDefaultData(Dictionary dic)
89 | {
90 | dic[Finger.FingerType.TYPE_INDEX]=m_DefaultFingerData;
91 | dic[Finger.FingerType.TYPE_MIDDLE]=m_DefaultFingerData;
92 | dic[Finger.FingerType.TYPE_PINKY]=m_DefaultFingerData;
93 | dic[Finger.FingerType.TYPE_RING]=m_DefaultFingerData;
94 | dic[Finger.FingerType.TYPE_THUMB]=m_DefaultFingerData;
95 | }
96 |
97 | void Awake()
98 | {
99 | ////分配空间
100 | m_FingerDatas[0] = new Dictionary();
101 | m_FingerDatas[1] = new Dictionary();
102 |
103 | for (int i = 0; i < 2; i++)
104 | {
105 | for (int j = 0; j < BUFFER_MAX; j++)
106 | {
107 | m_FingerDatasBuffer[i, j] = new Dictionary();
108 | }
109 | }
110 |
111 | //制作默认数据
112 | //DicAddDefaultData (m_FingerDataMiddle);
113 |
114 | }
115 |
116 | void Start()
117 | {
118 | m_LeapCtrl = LeapDriver.GetLeapCtrl ();
119 | }
120 |
121 | void Update()
122 | {
123 | //清除(确切说是填充)不存在手掌信息数据。因为手没有被检测到,但这时数据需要更新。所以有这样一个填充判断和控制。
124 | ClearNotExistHandData ();
125 |
126 | //掌心信息
127 | SavePalmsFrame ();
128 |
129 | //手指信息
130 | SaveFingersFrame();
131 |
132 |
133 | //记录先进入视野的手,记录到m_AboveView中
134 | SaveAboveViewHand();
135 | }
136 |
137 | void FixedUpdate()
138 | {
139 | ShowBaxis ();
140 | //显示
141 | ShowAllFingersData ();
142 | // ShowIndexFinger();
143 | //测试数据的可用性
144 | //TestFingerData ();
145 | }
146 |
147 | ///
148 | /// 记录第一次进入检测的手是左手还是右手.
149 | /// 1.枚举为None时
150 | /// 1.1检测到一只手,指定为该手
151 | /// 1.2检测到两只手,指定[0]下标
152 | /// 1.3检测到0只手,指定None
153 | /// 2.枚举为Right/Legt时
154 | /// 2.1检测到1只手,指定为该手
155 | /// 2.2检测到2之手,不变
156 | /// 2.3检测到0只收,设定为None
157 | ///
158 | void SaveAboveViewHand()
159 | {
160 | Frame frame = m_LeapCtrl.Frame();
161 | HandList hands = frame.Hands;
162 | int count = hands.Count;
163 |
164 | if(m_AboveView== E_HandInAboveView.None)
165 | {
166 | if(count ==1||count==2)
167 | {
168 | bool isRight = hands[0].IsRight;
169 | if(isRight)
170 | {
171 | m_AboveView = E_HandInAboveView.Right;
172 | }
173 | else
174 | {
175 | m_AboveView = E_HandInAboveView.Left;
176 | }
177 | }
178 | }
179 | else
180 | {
181 | if(count==1)
182 | {
183 | bool isRight = hands[0].IsRight;
184 | if(isRight)
185 | {
186 | m_AboveView = E_HandInAboveView.Right;
187 | }
188 | else
189 | {
190 | m_AboveView = E_HandInAboveView.Left;
191 | }
192 | }
193 | else if(count==0)
194 | {
195 | m_AboveView = E_HandInAboveView.None;
196 | }
197 | }
198 | }
199 | ///
200 | /// 保存五个手指的信息。
201 | ///
202 | void SaveFingersFrame()
203 | {
204 | Frame frame = m_LeapCtrl.Frame ();
205 |
206 | HandList hands = frame.Hands;
207 |
208 | foreach ( var hand in hands )
209 | {
210 | SaveFingerDataWithHandIndex( hand );
211 | }
212 |
213 | m_CurBufIndex=(m_CurBufIndex+1)%(BUFFER_MAX-1);
214 | }
215 |
216 | ///
217 | /// 保存手的信息到指定的缓存中,也保存在当前数据中
218 | ///
219 | /// Hand index.
220 | /// Hand.
221 | /// Current buffer index.
222 | void SaveFingerDataWithHandIndex(Hand hand)
223 | {
224 | //做空判断好恶心,既然要求传入一个Hand,为什么你要传入一个null呢。
225 | //这里不做null的判断也不会有问题
226 | //if (hand != null)
227 | //{
228 | bool isLeft = hand.IsLeft;
229 | int handIndex = isLeft ? 0 : 1;
230 |
231 | foreach (Finger finger in hand.Fingers)
232 | {
233 | Finger.FingerType fingerType = finger.Type();
234 |
235 | Vector fingerDir = finger.Direction;
236 |
237 | // Bone bone = finger.Bone(Bone.BoneType.TYPE_DISTAL);
238 | // Vector distalPos = bone.Center;
239 | Vector distalPos = finger.TipPosition;
240 | //记录根骨位置
241 | Vector metacarpalPos = finger.Bone(Bone.BoneType.TYPE_METACARPAL).Center;
242 |
243 | //如果是拇指,用近端骨指的位置代替
244 | if (finger.Type()==Finger.FingerType.TYPE_THUMB)
245 | {
246 | metacarpalPos = finger.Bone(Bone.BoneType.TYPE_PROXIMAL).Center;
247 | }
248 |
249 | //将数据保存到m_FingerDatas中,以及buffer中。
250 | FingerData fingerData = new FingerData(distalPos,fingerDir, metacarpalPos);
251 |
252 | //Vector3 fingerPos = VectorTrans.ToUnityVector3(fingerData.m_Position);
253 | //s Vector3 fingerDistalPos = VectorTrans.ToUnityVector3(fingerData.m_Point.m_Position);
254 | SaveFingerData(handIndex, fingerType, fingerData);
255 | }
256 | //}
257 | }
258 |
259 | ///
260 | /// 保存指定的手指到指定的位置中
261 | ///
262 | /// 索引号 0表示左手
263 | ///
264 | /// 手指信息
265 | void SaveFingerData(int handIndex,
266 | Finger.FingerType fingerType,
267 | FingerData fingerData)
268 | {
269 | //将data保存或者覆盖到m_FingerDatas中
270 | if(m_FingerDatas[handIndex].ContainsKey(fingerType))
271 | {
272 | m_FingerDatas[handIndex][fingerType] = fingerData;
273 | }
274 | else
275 | {
276 | m_FingerDatas[handIndex].Add(fingerType, fingerData);
277 | }
278 |
279 | //保存或者覆盖到buffer中
280 | if(m_FingerDatasBuffer[handIndex,m_CurBufIndex].ContainsKey(fingerType))
281 | {
282 | m_FingerDatasBuffer[handIndex, m_CurBufIndex][fingerType] = fingerData;
283 | }
284 | else
285 | {
286 | m_FingerDatasBuffer[handIndex, m_CurBufIndex].Add(fingerType, fingerData);
287 | }
288 | }
289 |
290 | ///
291 | /// 获取掌心的位置和法向量,记录到m_PalmDatas中
292 | ///
293 | void SavePalmsFrame()
294 | {
295 | Frame frame = m_LeapCtrl.Frame ();
296 |
297 | //将左手的信息记录到0中,将右手的信息记录到1中
298 | foreach (var hand in frame.Hands)
299 | {
300 | if (hand.IsLeft)
301 | {
302 | m_PalmDatas[0].m_Position = hand.PalmPosition;
303 | m_PalmDatas[0].m_Direction = hand.PalmNormal;
304 | }
305 | else if(hand.IsRight)
306 | {
307 | m_PalmDatas[1].m_Position = hand.PalmPosition;
308 | m_PalmDatas[1].m_Direction = hand.PalmNormal;
309 | }
310 | }
311 | }
312 |
313 | ///
314 | /// 让不存在手对应的记录,设置为DefaultData
315 | /// 会在Update中调用,这个方法只是功能性单纯的封装。并没有自己的层次结构
316 | ///
317 | void ClearNotExistHandData()
318 | {
319 | Frame frame = m_LeapCtrl.Frame ();
320 | HandList hands = frame.Hands;
321 | int count = hands.Count;
322 |
323 | //筛选出不存在的手掌,并将这个手掌的信息设定为空
324 | //一个手掌,或零个手掌
325 | if (count == 0)
326 | {
327 | //将[0],[1]索引的数据都清空
328 | for(int i=0;i<2;i++)
329 | {
330 | ClearTheHandData(i);
331 | }
332 | }
333 | //1个手掌
334 | else if(count==1)
335 | {
336 | //查找哪个手掌是没有被检测到
337 | int emptyHandIndex = hands[0].IsLeft?//存在的是左手
338 | 1://空的是右手,返回1
339 | 0;//空的是左手,返回0
340 | ClearTheHandData(emptyHandIndex);
341 | }
342 | }
343 | ///
344 | /// 清除一个手掌的信息
345 | /// 包括:1.手掌信息
346 | /// 2.当前五指信息
347 | /// 3.缓存五指信息
348 | ///
349 | /// 参数:0表示左手,1表示右手
350 | ///
351 | void ClearTheHandData(int handIndex)
352 | {
353 | //清除手掌
354 | m_PalmDatas [handIndex].Set (m_DefaultPointData);
355 |
356 | //清除 当前的五指信息。也可遍历所有含有的键值对
357 | //没有找到很好改变值的方式,如果每次判断isContainsKey,然后在改变值为Default,则需要遍历5次虽然节省了内存,但是增加的Cpu的处理时间.
358 | //清空后,在需要的时候产生一个新对象。
359 | m_FingerDatas[handIndex].Clear();
360 |
361 | //清除 当前缓存的信息
362 | m_FingerDatasBuffer [handIndex, m_CurBufIndex].Clear();
363 |
364 | }
365 |
366 | ///
367 | /// 获取手指的缓存数据,可能为空。本来想在运动的时候检测,但考虑到抖动因素,所以决定由动作本身保存缓存数据
368 | ///
369 | ///
370 | public Dictionary GetPreviousBufFingerData(int handIndex)
371 | {
372 | //Dictionary a;
373 | int preBufIndex = (m_CurBufIndex-1) < 0?BUFFER_MAX:m_CurBufIndex-1;//3->2,1->0,0->Max-1
374 | return m_FingerDatasBuffer[handIndex,preBufIndex];
375 | }
376 |
377 | //___________________Test_______________
378 | ///
379 | /// test Hand.Direction,
380 | /// 该方法只有在手掌平摊伸直时有意义。
381 | ///
382 | void HandDirection()
383 | {
384 | Frame frame = m_LeapCtrl.Frame ();
385 | Hand hand = frame.Hands [0];
386 | Vector3 v1 = VectorTrans.ToUnityVector3 (hand.PalmPosition);
387 | Vector3 v2 = VectorTrans.ToUnityVector3 (hand.Direction);
388 | Debug.DrawRay (v1,
389 | v2*100,
390 | Color.red);
391 | print (v1+","+v2);
392 | }
393 |
394 | ///
395 | /// 显示Vector的方向和大小
396 | ///
397 | /// Position.
398 | /// Dir.
399 | void ShowTheDatasPosAndDir(Vector pos,Vector dir)
400 | {
401 | UnityEngine.Vector3 unityDir = VectorTrans.ToUnityVector3 (dir);
402 | UnityEngine.Vector3 unityPos = VectorTrans.ToUnityVector3 (pos);
403 |
404 | Debug.DrawRay (unityPos, unityDir*10, Color.blue);
405 | print (unityPos+","+unityDir);
406 | }
407 |
408 |
409 | void ShowBaxis()
410 | {
411 | Debug.DrawRay (Vector3.zero, Vector3.forward*100, Color.blue);
412 | Debug.DrawRay (Vector3.zero, Vector3.right*100, Color.red);
413 | Debug.DrawRay (Vector3.zero, Vector3.up*100, Color.green);
414 | }
415 |
416 | //显示所有手指缓存数据
417 | void ShowAllFingersBufData()
418 | {
419 |
420 | }
421 |
422 | //显示所有手指数据
423 | void ShowAllFingersData()
424 | {
425 | //左右手
426 | for (int i=0; i<2; i++)
427 | {
428 | //遍历所有包含的手指
429 | if(m_FingerDatas[i]!=null)
430 | {
431 | var fingerDataValues = m_FingerDatas[i].Values;
432 |
433 | foreach (FingerData data in fingerDataValues)
434 | {
435 | ShowFingerData(i,m_PalmDatas[i], data);
436 | }
437 | }
438 | }
439 |
440 | }
441 |
442 | //显示单个手指的数据
443 | //1.掌心到手指的连线-红色
444 | //2.手指的方向-蓝色
445 | void ShowFingerData (int handIndex,PointData palmData,FingerData fingerData)
446 | {
447 | Vector3 handPos = VectorTrans.ToUnityVector3 (palmData.m_Position);
448 | Vector3 handDir = VectorTrans.ToUnityVector3 (palmData.m_Direction);
449 |
450 | Vector3 fingerPos = VectorTrans.ToUnityVector3 (fingerData.m_Position);
451 | Vector3 fingerDistalPos = VectorTrans.ToUnityVector3 (fingerData.m_Point.m_Position);
452 | Vector3 fingerDir = VectorTrans.ToUnityVector3 (fingerData.m_Point.m_Direction);
453 |
454 | Debug.DrawLine (handPos,fingerPos,Color.red);
455 | Debug.DrawLine(fingerPos, fingerDistalPos, Color.red);
456 | //手心到指尖的连线
457 | Debug.DrawLine(handPos, fingerDistalPos, Color.yellow);
458 |
459 | float vecLength = (fingerPos - fingerDistalPos).magnitude;
460 | Debug.DrawRay(fingerDistalPos, fingerDir * vecLength * 0.1f, Color.blue);
461 | Debug.DrawRay(handPos, handDir * vecLength * 0.1f, Color.blue);
462 | }
463 |
464 | //显示食指的骨根位置,近端位置,和指尖的位置及其连线
465 | void ShowIndexFinger()
466 | {
467 | Vector3 pos0=Vector3.zero;
468 | Vector3 pos1=Vector3.zero;
469 | Vector3 pos2=Vector3.zero;
470 | HandList hs= m_LeapCtrl.Frame().Hands;
471 | foreach(var f in hs.Leftmost.Fingers)
472 | {
473 | if(f.Type()==Finger.FingerType.TYPE_INDEX)
474 | {
475 | pos0 = VectorTrans.ToUnityVector3( f.Bone(Bone.BoneType.TYPE_METACARPAL).Center);
476 | pos1 = VectorTrans.ToUnityVector3(f.Bone(Bone.BoneType.TYPE_PROXIMAL).Center);
477 | pos2 = VectorTrans.ToUnityVector3(f.Bone(Bone.BoneType.TYPE_DISTAL).Center);
478 | }
479 | }
480 | Debug.DrawLine(pos0, pos1, Color.red);
481 | Debug.DrawLine(pos0, pos2, Color.yellow);
482 | Debug.DrawLine(pos1, pos2, Color.green);
483 | }
484 |
485 | ///适配性检测
486 | /// 观察手指可用的行为
487 | void TestVaildData()
488 | {
489 |
490 | }
491 |
492 | ///
493 | /// 检测指定手指的可用性
494 | ///
495 | void TestFingerData()
496 | {
497 | //左右手
498 | for (int i=0; i<2; i++)
499 | {
500 | //遍历所有包含的手指
501 | if(m_FingerDatas[i]!=null)
502 | {
503 | //获取index手指的数据
504 | //PointData? data = GetDicValue(m_FingerDatas[i],Finger.FingerType.TYPE_INDEX);
505 | //if(data.HasValue)
506 | //{
507 | // PointData trueData=(PointData)data;
508 |
509 | //}
510 |
511 | }
512 | }
513 | }
514 |
515 | ///获取dic中的数据
516 | PointData? GetDicValue(Dictionary dic,Finger.FingerType fingerType)
517 | {
518 | if (dic.ContainsKey (fingerType))
519 | return dic [fingerType];
520 | else
521 | return null;
522 | }
523 | }
524 |
--------------------------------------------------------------------------------
/GesturesDeal/BaseDataFilter/HandAndFingersPoint.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: aaa8605d276774f478236ddeba009797
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GesturesDeal/BaseDataFilter/LeapDriver.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 |
5 | ///
6 | /// 关联LeapMotion驱动,分配内存空间,创建接口
7 | ///
8 | public sealed class LeapDriver
9 | {
10 | private static Controller leapController=null;
11 | public static Controller GetLeapCtrl()
12 | {
13 | if (leapController == null) {
14 | leapController = new Controller ();
15 | }
16 | return leapController;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/GesturesDeal/BaseDataFilter/LeapDriver.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 95b7e8ec80834774991164d82724ce44
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/Motion.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a6d032c5dff334d458adbc466a818c33
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/FingerClick.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using System;
4 | using Leap;
5 |
6 | ///
7 | /// 1.当判定手指为FingerPoint姿势并且手指放置在相应的碰撞层上(按钮上)时,进入初始状态。
8 | /// 2.每隔0.2s判定手指-z轴的偏移量。
9 | /// 3.当达到前进偏移阈值(深度阈值)时,进入等待反弹状态。
10 | /// 4.反弹状态的检测根据z轴的偏移量增加而决定
11 | /// 5.检测到反弹后,继续检测反弹阈值,如果超过反弹阈值,则触发点击完成事件m_OnClicked.
12 | ///
13 | public class FingerClick : MonoBehaviour
14 | {
15 | [SerializeField] HandFingerPoint m_FingerPoint;
16 | [SerializeField] HandAndFingersPoint m_HandData;
17 | int m_CollisionLayer;//碰撞的层
18 | Action m_OnClicked;//完成点击事件
19 |
20 | Action m_OnEnterClick;//进入点击模式
21 | readonly float EnterDelayTime=0.2f;//进入点击模式后延迟多长多长事件触发
22 |
23 | //Motion
24 | readonly float CheckTimeStep = 0.1f;//运动状态检测的时间差。
25 | Vector m_PreviousPos = Vector.Zero;//上一次检测的位置
26 | Vector m_Off = Vector.Zero;
27 | //threshold
28 | readonly float DeepThreshold=25f;//向前偏移的深度阈值
29 | readonly float BackThreshold=16f;//返回时的反弹阈值
30 | readonly float ForwordPointThreshold=Mathf.PI *5 /18;//向前指向的阈值
31 |
32 | //点击的持续时间,点击的速度
33 | [SerializeField] float m_DeepClickingTime;//进入点击状态后,手指向前移动持续的时间
34 | [SerializeField]
35 | float m_BackClickingTime;//进入反弹状态后,手指返回移动持续的时间
36 | [SerializeField]
37 | float m_ClickSpeed;//点击时候的速度
38 |
39 | //手势识别中的几个状态节点
40 | [SerializeField]
41 | bool m_IsEnteredClick;//进入了识别状态
42 | [SerializeField]
43 | bool m_IsEnteredBackClick;//进入了反弹状态
44 | Action m_OnBackFunc;
45 |
46 | Vector m_EnterPos;//初始状态指尖的位置
47 | Vector m_EnterBackPos;//反弹状态的初始位置
48 |
49 | //点击状态点
50 | readonly float DeepClickRadianThreshold = Mathf.PI*30/180;//点击时每帧的方向偏离-z轴的夹角
51 |
52 | public bool IsEnteredClick
53 | {
54 | get
55 | {
56 | return m_IsEnteredClick;
57 | }
58 | }
59 | public bool IsEnteredBackClick
60 | {
61 | get
62 | {
63 | return m_IsEnteredBackClick;
64 | }
65 | }
66 |
67 | public Vector Off
68 | {
69 | get
70 | {
71 | return m_Off;
72 | }
73 | }
74 | //FinishClickAction Msg
75 | //手掌打开状态,事件注册接口
76 | public void RegisterClickedMsg(Action onClicked)
77 | {
78 | if (onClicked != null)
79 | {
80 | m_OnClicked += onClicked;
81 | }
82 |
83 | }
84 | public void CancelClickedMsg(Action onClicked)
85 | {
86 | if(onClicked!=null)
87 | {
88 | m_OnClicked -= onClicked;
89 | }
90 | }
91 |
92 | ///
93 | /// 进入点击状态事件的注册和注销操作
94 | ///
95 | ///
96 | public void RegisterEnterClickMsg(Action onEnterClick)
97 | {
98 | if (onEnterClick != null)
99 | {
100 | m_OnClicked += onEnterClick;
101 | }
102 | }
103 | public void CancelEnterClickMsg(Action onEnterClick)
104 | {
105 | if (onEnterClick != null)
106 | {
107 | m_OnClicked -= onEnterClick;
108 | }
109 | }
110 |
111 | public void RegisterBackClickMsg(Action onBack)
112 | {
113 | if (onBack != null)
114 | {
115 | m_OnBackFunc += onBack;
116 | }
117 | }
118 | public void CancelBackClickMsg(Action onBack)
119 | {
120 | if (onBack != null)
121 | {
122 | m_OnBackFunc -= onBack;
123 | }
124 | }
125 |
126 | // Update is called once per frame
127 | void Update ()
128 | {
129 | int clickIndex;
130 | //这里的判定本该分成预备状态、运动状态、终结状态。但是对于点击来讲,这三个状态的手势判定条件都相同(手势相同、阈值相同)
131 | //所以现在都用同一个控制器来表示
132 |
133 | if (EnterClickState(out clickIndex))
134 | {
135 | //初始状态
136 | if(!m_IsEnteredClick)
137 | {
138 | //标记进入点击状态,记录初始位置
139 | m_IsEnteredClick = true;
140 | m_EnterPos = m_HandData.FingerDatas[clickIndex][Finger.FingerType.TYPE_INDEX].m_Point.m_Position;
141 | m_PreviousPos = m_EnterPos;
142 |
143 | //延迟触发初始事件
144 | StartCoroutine(EnterClickDelay());
145 | }
146 | //非初始状态
147 | else
148 | //不是反弹状态
149 | if(!m_IsEnteredBackClick)
150 | {
151 | m_DeepClickingTime+=Time.deltaTime;
152 | //是否进入一次位置判定
153 | if (m_DeepClickingTime > CheckTimeStep)
154 | {
155 | m_DeepClickingTime = 0f;
156 | //收集当前手指索引的食指指尖位置数据
157 | Vector indexFingerTipPos = m_HandData.FingerDatas[clickIndex][Finger.FingerType.TYPE_INDEX].m_Point.m_Position;
158 | m_Off = indexFingerTipPos - m_PreviousPos;
159 |
160 | //是向前点击状态,偏移向量与-z轴(Leap坐标系)的夹角小于某个阈值
161 | if(m_Off.AngleTo(-Vector.ZAxis) DeepThreshold )
167 | {
168 | //标记进入了反弹状态,记录反弹状态的位置
169 | m_IsEnteredBackClick = true;
170 | m_EnterBackPos = indexFingerTipPos;
171 | if(m_OnBackFunc != null)
172 | {
173 | // print("aaa");
174 | m_OnBackFunc();
175 | }
176 | }
177 | }
178 | //不是前进状态
179 | else
180 | {
181 | //手势匹配失败,重置变量
182 | ResetClickState();
183 | }
184 | //更新m_PreviousPos的位置
185 | m_PreviousPos = indexFingerTipPos;
186 | }
187 | }
188 | else //进入了返回状态
189 | {
190 | m_BackClickingTime+=Time.deltaTime;
191 | //进入位置判定
192 | if ( m_BackClickingTime > CheckTimeStep )
193 | {
194 | m_BackClickingTime = 0f;
195 | Vector indexFingerTipPos = m_HandData.FingerDatas[clickIndex][Finger.FingerType.TYPE_INDEX].m_Point.m_Position;
196 | //是后退状态
197 | if (m_PreviousPos.z < indexFingerTipPos.z)
198 | {
199 | float backDeep = indexFingerTipPos.z - m_EnterBackPos.z;
200 | //满足了反弹阈值
201 | if( backDeep > BackThreshold )
202 | {
203 | print("Clicked");
204 | //触发事件,重置状态
205 | if (m_OnClicked!=null)
206 | {
207 | print("Clicked()");
208 | m_OnClicked();
209 | }
210 | ResetClickState();
211 | }
212 | }
213 | }
214 | }
215 | }
216 | //不是手指点击状态
217 | else
218 | {
219 | ResetClickState();
220 | m_IsEnteredClick = false;
221 | }
222 | }
223 |
224 | ///
225 | /// 进入点击状态后延迟触发一个进入事件
226 | ///
227 | ///
228 | IEnumerator EnterClickDelay()
229 | {
230 | float curDelayTime=0f;
231 | while(curDelayTime
248 | /// 在判定过程中,因为匹配失败或者完成匹配。都会重置点击状态
249 | ///
250 | void ResetClickState()
251 | {
252 | m_DeepClickingTime = 0f;
253 | m_ClickSpeed = 0f;
254 | m_BackClickingTime = 0f;
255 |
256 | m_IsEnteredBackClick = false;
257 | }
258 |
259 | ///
260 | /// 进入预备/运动/终结状态
261 | /// 检测到大致前向方向
262 | ///
263 | /// 选择哪个手进入的预备状态,只有返回true才有效
264 | ///
265 | bool EnterClickState(out int index)
266 | {
267 | bool isEnter = false;
268 | index = -1;
269 |
270 | for (int i = 0; i < 2; i++)
271 | {
272 | //是指向状态
273 | if (m_FingerPoint.IsEnterPointed[i])
274 | {
275 | Vector dir = m_FingerPoint.Dir[i];
276 | //是点击手势
277 | if (IsPointAsClickForwardDir(dir, ForwordPointThreshold))
278 | {
279 | isEnter = true;
280 | index = i;
281 | }
282 | }
283 | }
284 | return isEnter;
285 | }
286 |
287 | ///
288 | /// 判定一个手指指向是否是点击前方
289 | /// 这里阈值的判定与其说是+x轴,倒不如说是距离yz面的夹角程度更直接,但实现方式上选择了+x轴。
290 | ///
291 | ///
292 | /// 表示距离+x的夹角,标准是90度
293 | bool IsPointAsClickForwardDir(Vector dir,float threshold)
294 | {
295 | bool IsPointAsClick = false;
296 | float radian = dir.AngleTo(Vector.Right);
297 |
298 | if (radian > threshold && radian < Mathf.PI - threshold)
299 | {
300 | IsPointAsClick = true;
301 | }
302 | return IsPointAsClick;
303 | }
304 | }
305 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/FingerClick.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ac6a127ec2a6dd140b2e4dcd884105ec
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/FingerPinch.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using System;
4 |
5 | ///Log1:手势废弃,Leap API不能正确识别手指的前方,尤其是整根手指从骨根弯曲,不明白Finger.Direction的定义。
6 | ///
7 | /// 匹配手指捏住的动作
8 | /// 两种方案:
9 | /// 1.当匹配捏的初始状态后,等待一段时间后,若此刻是捏的终止状态,则说明完成了捏动作 X:决定第二种
10 | /// 2.当匹配初始后,每帧会追踪两手指尖的距离,当距离一直在减小,并最终处于捏的终止状态时,说明完成了捏动作
11 | /// 比较:方式2优点:可以记录每帧的变化时差,以此作为捏的快慢程度量。
12 | /// 方式2缺点:复杂,需要考虑手指的细微抖动所造成的位置比较————解决方案:每隔0.2秒做一次判定,不用协同程序,因为时间太短。
13 | /// 匹配只会涉及一只手,即如果检测到一只手处于捏动作中,那么不会再检测其他手,除非这只手退出了捏状态。
14 | /// 手势匹配细节:
15 | /// 1.捏动作的初始状态:
16 | /// 食指伸直,食指指尖与拇指指尖相距一定距离(初始距离阈值)
17 | /// 涉及变量:Action m_OnEnterPinch;bool m_IsEnteredPinch;const float EnterDisThreshold;
18 | /// 2.捏动作的运动过程
19 | /// 每隔0.2s检测食指和拇指指尖的距离,并记录。以前后两次距离的差值来表示速度
20 | /// 涉及变量:speed:float;m_PreviousDistance:float;const CheckTimeStep:float
21 | /// 3.捏动作结束状态:
22 | /// 食指伸直,食指指尖与拇指指尖相距一定距离(终结距离阈值)
23 | /// 涉及变量:Action m_OnPinched;const float PinchedDisThreshold;
24 | ///
25 | public class FingerPinch : MonoBehaviour
26 | {
27 | //[SerializeField] HandAndFingersPoint m_HandData;
28 | [SerializeField] HandPinchGroup m_HandPinch;
29 | //Enter Pinch
30 | Action m_OnEnterPinch;
31 | readonly float EnterDelayTime = 0.3f;
32 | [SerializeField] bool m_IsEnteredPinch;//是否进入了捏动作的匹配状态,即是否检测到捏的初始状态,并且到现在为止动作也是正常匹配中。
33 | readonly float EnterDisThreshold=80f;//>80mm判定进入Pinch状态
34 |
35 | //On Pinching
36 | [SerializeField] float m_Speed;//当前的两指尖距离-m_PreviousDistance,代表速度。
37 | [SerializeField] float m_PreviousDistance;
38 | float m_CurCheckTime;
39 | readonly float CheckTimeStep = 0.2f;//状态判定的时间间隔————当匹配捏的初始状态后每隔N秒做一次数据处理
40 |
41 | //Pinched
42 | readonly float PinchedDisThreshold=10f;//Leap单位,10mm
43 | Action m_OnPinched;//完成捏动作匹配后,触发事件链。
44 |
45 |
46 | [SerializeField] int m_HandIndex;//当前捏状态所识别的手索引
47 |
48 | // Update is called once per frame
49 | void Update ()
50 | {
51 | //没有进入初始状态时判定是否进入了初始状态
52 | if(!m_IsEnteredPinch)
53 | {
54 | //符合
55 | if(EnterPinchState(out m_HandIndex))
56 | {
57 | m_IsEnteredPinch = true;
58 | m_PreviousDistance = m_HandPinch.Distance[m_HandIndex];
59 | StartCoroutine(EnterPinchDelay());
60 | }
61 | }
62 |
63 | //当前处于捏状态中
64 | if ( m_IsEnteredPinch )
65 | {
66 | //时间判定
67 | m_CurCheckTime+=Time.deltaTime;
68 | if(m_CurCheckTime>CheckTimeStep)
69 | {
70 | m_CurCheckTime = 0f;
71 |
72 | float curDis = m_HandPinch.Distance[m_HandIndex];
73 | //依然处于捏动作过程中
74 | if( curDis < m_PreviousDistance )
75 | {
76 | //处于结束状态
77 | if (curDis < PinchedDisThreshold)
78 | {
79 | //触发相应操作
80 | ResetState();
81 | if (m_OnPinched != null)
82 | {
83 | m_OnPinched();
84 | }
85 | }
86 | //不是结束状态
87 | else
88 | {
89 | //记录数据
90 | m_Speed = m_PreviousDistance - curDis;
91 | m_PreviousDistance = curDis;
92 | }
93 | }
94 | //不匹配捏动作
95 | else
96 | {
97 | //退出捏动作
98 | ResetState();
99 | }
100 | }
101 | }
102 | }
103 |
104 | void ResetState()
105 | {
106 | m_CurCheckTime = 0f;
107 | m_Speed = 0f;
108 | m_PreviousDistance = 0f;
109 | m_IsEnteredPinch = false;
110 | m_HandIndex = -1;
111 | }
112 |
113 | ///
114 | /// 延迟发送缩放广播
115 | ///
116 | ///
117 | IEnumerator EnterPinchDelay()
118 | {
119 | float curDelayTime = 0f;
120 | while (curDelayTime < EnterDelayTime)
121 | {
122 | curDelayTime += Time.deltaTime;
123 | yield return null;
124 | }
125 | //进入了点击状态
126 | if (m_IsEnteredPinch)
127 | {
128 | //发送广播
129 | if (m_OnEnterPinch != null)
130 | {
131 | m_OnEnterPinch();
132 | }
133 | }
134 | }
135 |
136 | ///
137 | /// 初始动作的匹配判定
138 | ///
139 | bool EnterPinchState(out int handIndex)
140 | {
141 | bool isEnterPinch = false;
142 | handIndex = -1;
143 | for (int i = 0; i < 2; i++)
144 | {
145 | if (m_HandPinch.IsHandPinch[i])
146 | {
147 | //距离大于阈值
148 | if( m_HandPinch.Distance[i] > EnterDisThreshold )
149 | {
150 | isEnterPinch = true;
151 | handIndex = i;
152 | }
153 | }
154 | }
155 | return isEnterPinch;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/FingerPinch.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9c78dedfc79dc6d41acb7bde9c35bfda
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/FingerUnPinch.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using System;
4 |
5 | ///Log1:手势废弃,Leap API不能正确识别手指的前方,尤其是整根手指从骨根弯曲,不明白Finger.Direction的定义。
6 | ///
7 | /// 匹配手指捏住的动作
8 | /// 两种方案:
9 | /// 1.当匹配捏的初始状态后,等待一段时间后,若此刻是捏的终止状态,则说明完成了捏动作 X:决定第二种
10 | /// 2.当匹配初始后,每帧会追踪两手指尖的距离,当距离一直在减小,并最终处于捏的终止状态时,说明完成了捏动作
11 | /// 比较:方式2优点:可以记录每帧的变化时差,以此作为捏的快慢程度量。
12 | /// 方式2缺点:复杂,需要考虑手指的细微抖动所造成的位置比较————解决方案:每隔0.2秒做一次判定,不用协同程序,因为时间太短。
13 | /// 匹配只会涉及一只手,即如果检测到一只手处于捏动作中,那么不会再检测其他手,除非这只手退出了捏状态。
14 | /// 手势匹配细节:
15 | /// 1.捏动作的初始状态:
16 | /// 食指伸直,食指指尖与拇指指尖相距一定距离(初始距离阈值)
17 | /// 涉及变量:Action m_OnEnterPinch;bool m_IsEnteredPinch;const float EnterDisThreshold;
18 | /// 2.捏动作的运动过程
19 | /// 每隔0.2s检测食指和拇指指尖的距离,并记录。以前后两次距离的差值来表示速度
20 | /// 涉及变量:speed:float;m_PreviousDistance:float;const CheckTimeStep:float
21 | /// 3.捏动作结束状态:
22 | /// 食指伸直,食指指尖与拇指指尖相距一定距离(终结距离阈值)
23 | /// 涉及变量:Action m_OnPinched;const float PinchedDisThreshold;
24 | ///
25 | public class FingerUnPinch : MonoBehaviour
26 | {
27 | //[SerializeField] HandAndFingersPoint m_HandData;
28 | [SerializeField]
29 | HandPinchGroup m_HandPinch;
30 | //Enter Pinch
31 | Action m_OnEnterUnPinch;
32 | readonly float EnterDelayTime = 0.3f;
33 | [SerializeField] bool m_IsEnteredUnPinch;//是否进入了捏动作的匹配状态,即是否检测到捏的初始状态,并且到现在为止动作也是正常匹配中。
34 | readonly float EnterDisThreshold = 10f;//小于10mm才算进入UnPinch状态
35 |
36 | //On Pinching
37 | [SerializeField] float m_Speed;//当前的两指尖距离-m_PreviousDistance,代表速度。
38 | [SerializeField] float m_PreviousDistance;
39 | float m_CurCheckTime;
40 | readonly float CheckTimeStep = 0.2f;//状态判定的时间间隔————当匹配捏的初始状态后每隔N秒做一次数据处理
41 |
42 | //Pinched
43 | readonly float UnPinchedDisThreshold = 100f;//Leap单位,100mm
44 | Action m_OnUnPinched;//完成捏动作匹配后,触发事件链。
45 |
46 |
47 | [SerializeField] int m_HandIndex;//当前捏状态所识别的手索引
48 |
49 | // Update is called once per frame
50 | void Update()
51 | {
52 | //没有进入初始状态时判定是否进入了初始状态
53 | if (!m_IsEnteredUnPinch)
54 | {
55 | //符合
56 | if (EnterUnPinchState(out m_HandIndex))
57 | {
58 | m_IsEnteredUnPinch = true;
59 | m_PreviousDistance = m_HandPinch.Distance[m_HandIndex];
60 | StartCoroutine(EnterUnPinchDelay());
61 | }
62 | }
63 |
64 | //当前处于捏状态中
65 | if (m_IsEnteredUnPinch)
66 | {
67 | //时间判定
68 | m_CurCheckTime += Time.deltaTime;
69 | if (m_CurCheckTime > CheckTimeStep)
70 | {
71 | m_CurCheckTime = 0f;
72 |
73 | float curDis = m_HandPinch.Distance[m_HandIndex];
74 | //依然处于捏动作过程中
75 | if (curDis > m_PreviousDistance)
76 | {
77 | //处于结束状态
78 | if (curDis > UnPinchedDisThreshold)
79 | {
80 | //触发相应操作
81 | ResetState();
82 | if (m_OnUnPinched != null)
83 | {
84 | m_OnUnPinched();
85 | }
86 | }
87 | //不是结束状态
88 | else
89 | {
90 | //记录数据
91 | m_Speed = curDis - m_PreviousDistance;
92 | m_PreviousDistance = curDis;
93 | }
94 | }
95 | //不匹配捏动作
96 | else
97 | {
98 | //退出捏动作
99 | ResetState();
100 | }
101 | }
102 | }
103 | }
104 |
105 | void ResetState()
106 | {
107 | m_CurCheckTime = 0f;
108 | m_Speed = 0f;
109 | m_PreviousDistance = 0f;
110 | m_IsEnteredUnPinch = false;
111 | m_HandIndex = -1;
112 | }
113 |
114 | ///
115 | /// 延迟发送缩放广播
116 | ///
117 | ///
118 | IEnumerator EnterUnPinchDelay()
119 | {
120 | float curDelayTime = 0f;
121 | while (curDelayTime < EnterDelayTime)
122 | {
123 | curDelayTime += Time.deltaTime;
124 | yield return null;
125 | }
126 | //进入了点击状态
127 | if (m_IsEnteredUnPinch)
128 | {
129 | //发送广播
130 | if (m_OnEnterUnPinch != null)
131 | {
132 | m_OnEnterUnPinch();
133 | }
134 | }
135 | }
136 |
137 | ///
138 | /// 初始动作的匹配判定
139 | ///
140 | bool EnterUnPinchState(out int handIndex)
141 | {
142 | bool isEnterUnPinch = false;
143 | handIndex = -1;
144 | for (int i = 0; i < 2; i++)
145 | {
146 | if (m_HandPinch.IsHandPinch[i])
147 | {
148 | //距离小于阈值
149 | if (m_HandPinch.Distance[i] < EnterDisThreshold)
150 | {
151 | isEnterUnPinch = true;
152 | handIndex = i;
153 | }
154 | }
155 | }
156 | return isEnterUnPinch;
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/FingerUnPinch.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8b92c2a508a535f4d9a1cb9946c8c5a7
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/HandFaceToF.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 | using System;
5 |
6 | ///
7 | /// 判定两只手处于垂直向前状态
8 | /// 记录两只手之间的距离,速度。
9 | ///
10 | public class HandFaceToF : MonoBehaviour
11 | {
12 | [SerializeField] HandOpen m_HandOpen;
13 |
14 | //Update参数
15 | readonly float CheckStepTime = 0.1f;
16 | float m_CurCheckStepTime;
17 |
18 | //初始状态
19 | [SerializeField] bool m_IsEnteredFTF;
20 | Action m_OnEnterFunc;
21 | //运动状态
22 | float m_PreviousDis;
23 | [SerializeField] float m_Speed;//cur-pre.大于0表示放大
24 | //终结状态
25 | Action m_OnEndFunc;
26 | //延迟验证
27 | readonly float EnterDelayTime = 0.2f;
28 | float m_CurEnterDelayTime;
29 |
30 | //手势匹配
31 | readonly float VerToXAxisDirAdjust = -Mathf.PI/18;//垂直x轴的方向调节阈值[60,180-60]
32 | readonly float VerToYAxisDirAdjust = -Mathf.PI/18;//垂直y轴方向的调节阈值[60,180-60]
33 |
34 | public bool IsEntered
35 | {
36 | get
37 | {
38 | return m_IsEnteredFTF;
39 | }
40 | }
41 | public float Speed
42 | {
43 | get
44 | {
45 | return m_Speed;
46 | }
47 | }
48 | public void RegisterFunc(Action m_OnEnter, Action m_OnEnd)
49 | {
50 | if (m_OnEnter != null)
51 | {
52 | m_OnEndFunc += m_OnEnter;
53 | }
54 | if (m_OnEnd != null)
55 | {
56 | m_OnEndFunc += m_OnEnd;
57 | }
58 | }
59 |
60 | public void CancelFunc(Action m_OnEnter, Action m_OnEnd)
61 | {
62 | if (m_OnEnter != null)
63 | {
64 | m_OnEndFunc -= m_OnEnter;
65 | }
66 | if (m_OnEnd != null)
67 | {
68 | m_OnEndFunc -= m_OnEnd;
69 | }
70 | }
71 |
72 | // Update is called once per frame
73 | void Update ()
74 | {
75 | m_CurCheckStepTime+=Time.deltaTime;
76 | m_CurEnterDelayTime += Time.deltaTime;
77 | if(m_CurCheckStepTime>CheckStepTime)
78 | {
79 | m_CurCheckStepTime = 0f;
80 | //满足FaceToFace
81 | if(FTFState())
82 | {
83 | //当前不是进入状态
84 | if(!m_IsEnteredFTF)
85 | {
86 | if(m_CurEnterDelayTime > EnterDelayTime)
87 | {
88 | m_CurEnterDelayTime=0f;
89 |
90 | m_IsEnteredFTF=true;
91 | m_PreviousDis = m_HandOpen.PalmPos[0].DistanceTo(m_HandOpen.PalmPos[1]);
92 | if(m_OnEnterFunc!=null)
93 | {
94 | m_OnEnterFunc();
95 | }
96 | }
97 | }
98 | //已经进入FTF状态
99 | else
100 | {
101 | //记录数据
102 | float curDis = m_HandOpen.PalmPos[0].DistanceTo(m_HandOpen.PalmPos[1]);
103 | m_Speed = curDis-m_PreviousDis;
104 | m_PreviousDis = curDis;
105 | }
106 | }
107 | //不是FTF
108 | else
109 | {
110 | //这是终结状态
111 | if (m_IsEnteredFTF)
112 | {
113 | if(m_OnEndFunc!=null)
114 | {
115 | m_OnEndFunc();
116 | }
117 | }
118 | Reset();
119 | }
120 | }
121 | }
122 |
123 | void Reset()
124 | {
125 | m_IsEnteredFTF=false;
126 |
127 | //运动状态
128 | m_PreviousDis=0f;
129 | m_Speed=0f;//cur-pre.大于0表示放大
130 | m_CurCheckStepTime = 0f;
131 | m_CurEnterDelayTime=0f;
132 | }
133 |
134 | bool FTFState()
135 | {
136 | bool isFTF=false;
137 | if( m_HandOpen.IsEnterOpened[0] && m_HandOpen.IsEnterOpened[1] )
138 | {
139 | if(VectorCompare.VerDirToXAxis(m_HandOpen.Dir[0],VerToXAxisDirAdjust) &&
140 | VectorCompare.VerDirToYAxis(m_HandOpen.PalmDir[0], VerToYAxisDirAdjust) &&
141 | VectorCompare.VerDirToXAxis(m_HandOpen.Dir[1],VerToXAxisDirAdjust) &&
142 | VectorCompare.VerDirToYAxis(m_HandOpen.PalmDir[1], VerToYAxisDirAdjust))
143 | {
144 | isFTF = true;
145 | }
146 | }
147 | return isFTF;
148 | }
149 |
150 | ///
151 | /// 一个方向是否垂直+x轴
152 | ///
153 | ///
154 | /// 阈值
155 | /// 垂直true
156 | bool VerDirToXAxis(Vector dir,float threshold)
157 | {
158 | bool isVer=false;
159 | float radian = dir.AngleTo(Vector.XAxis);
160 |
161 | if( radian > threshold && radian < (Mathf.PI-threshold))
162 | {
163 | isVer = true;
164 | }
165 | return isVer;
166 | }
167 |
168 | bool VerDirToYAxis(Vector dir, float threshold)
169 | {
170 | bool isVer = false;
171 |
172 | float radian = dir.AngleTo(Vector.YAxis);
173 |
174 | if(radian>threshold && radian<(Mathf.PI - threshold))
175 | {
176 | isVer = true;
177 | }
178 | return isVer;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/HandFaceToF.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5384d49a8cb1c764bbe14ec732636422
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/HandOpenToFist.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using System;
4 | using Leap;
5 | ///
6 | /// 定位:水平前向
7 | /// 从伸手到握拳姿势
8 | /// 匹配:进入初始状态后,经过一个固定时间,判定手处于弯曲状态。表示成立。
9 | /// 之所以不用手指的弯曲程度计算,是因为不准
10 | ///
11 | public class HandOpenToFist : MonoBehaviour
12 | {
13 | [SerializeField] HandBend m_HandBend;
14 | [SerializeField] HandOpen m_HandOpen;
15 | //Update
16 | readonly float CheckStepTime = 0.2f;
17 | float m_CurCheckStepTime;
18 |
19 | //enter
20 | float m_CurEnterDelayTime;
21 | readonly float EnterDelayTimeThrehold=0.3f;
22 | Action m_OnEnterFunc;
23 | [SerializeField] bool m_IsEnteredOpen;
24 |
25 | //中间过程
26 | readonly float WaitBendTimeThreshold=2f;//进入初始状态1s后,若为弯曲状态,则说明动作成立。
27 |
28 | //end
29 | Action m_OnEndFunc;
30 |
31 | //record
32 | float m_PreviousRadian;
33 |
34 | //threshold
35 | readonly float ForwardThreshold=Mathf.PI /9;//指尖方向到-z轴的阈值
36 | readonly float DownThreshold=Mathf.PI /9;//指尖方向到-z轴的阈值
37 |
38 | public void RegisterFunc(Action onEnter,Action onEnd)
39 | {
40 | if(onEnd!=null)
41 | {
42 | m_OnEndFunc+=onEnd;
43 | }
44 | if(onEnter!=null)
45 | {
46 | m_OnEnterFunc+=onEnter;
47 | }
48 | }
49 |
50 | public void CancelFunc(Action onEnter,Action onEnd)
51 | {
52 | if(onEnd!=null)
53 | {
54 | m_OnEndFunc-=onEnd;
55 | }
56 | if(onEnter!=null)
57 | {
58 | m_OnEnterFunc-=onEnter;
59 | }
60 | }
61 |
62 |
63 | // Update is called once per frame
64 | void Update () {
65 | m_CurCheckStepTime += Time.deltaTime;
66 | m_CurEnterDelayTime += Time.deltaTime;
67 |
68 | //每隔一定时间做一次检测
69 | if(m_CurCheckStepTime>CheckStepTime)
70 | {
71 | m_CurCheckStepTime=0f;
72 | //没有进入判定状态
73 | if(!m_IsEnteredOpen)
74 | {
75 | int handIndex;
76 | //是初始状态
77 | if(EnterOpenState(out handIndex))
78 | {
79 | //满足了延迟时间
80 | if(m_CurEnterDelayTime>EnterDelayTimeThrehold)
81 | {
82 | m_CurEnterDelayTime=0f;
83 |
84 | //设定初始状态
85 | m_IsEnteredOpen=true;
86 | if(m_OnEnterFunc!=null)
87 | {
88 | m_OnEnterFunc();
89 | }
90 | //等待一个弯曲判定
91 | StartCoroutine(WaitForBendState(WaitBendTimeThreshold,handIndex));
92 | }
93 | }
94 | //不是初始状态
95 | else
96 | {
97 | m_CurEnterDelayTime=0f;
98 | }
99 | }
100 | }
101 | }
102 |
103 | ///
104 | /// 判断是否进入伸掌状态
105 | /// 没有考虑多只手的检测,影响是,右手有最终判定权。
106 | ///
107 | /// 选定的手的索引,返回true时有效
108 | /// true, if open state was entered, false otherwise.
109 | bool EnterOpenState(out int handIndex)
110 | {
111 | bool isEnter = false;
112 | handIndex = -1;
113 | for(int i=0;i<2;i++)
114 | {
115 | if(m_HandOpen.IsEnterOpened [i])
116 | {
117 | Vector palmDir = m_HandOpen.PalmDir[i];
118 | Vector fingerDir = m_HandOpen.Dir[i];
119 |
120 | //是水平向前的方向
121 | if(IsHorAndForwardOpenHand(palmDir,fingerDir))
122 | {
123 | isEnter =true;
124 | handIndex = i;
125 | }
126 | }
127 | }
128 |
129 | return isEnter;
130 | }
131 |
132 | ///
133 | /// 经过一段时间后,若指定的手处于弯曲状态,则触发握拳的完成事件,初始化成员变量
134 | ///
135 | /// The for bend state.
136 | /// 经过多长时间后触发
137 | /// 对哪只手进行弯曲状态的匹配
138 | IEnumerator WaitForBendState(float delayTime,int handIndex)
139 | {
140 | float curTime = 0f;
141 |
142 | while (curTime
167 | /// 判定一个摊开手掌的状态是否是水平向前
168 | ///
169 | /// true, if and forward open hand was hored, false otherwise.
170 | bool IsHorAndForwardOpenHand(Vector palmDir,Vector fingerDir )
171 | {
172 | bool isHorAndForward = false;
173 | if( fingerDir.AngleTo(-Vector.ZAxis) < ForwardThreshold &&
174 | palmDir.AngleTo(-Vector.YAxis) < DownThreshold )
175 | {
176 | isHorAndForward=true;
177 | }
178 | return isHorAndForward;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/HandOpenToFist.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c44dad6a3d0300541bc88e37bdc44cbd
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/SingleHandOpenTrack.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 | using System;
5 | ///
6 | /// 当检测到一直手后,进入追踪状态,记录单手运动的速度,掌心方向。
7 | /// 检测到HandOpen之后,延迟0.5s进入追踪状态。
8 | /// 关于速度:慢速[0,10],快速[25,40],建议以25为上限
9 | ///
10 | public class SingleHandOpenTrack : MonoBehaviour {
11 | [SerializeField] HandOpen m_HandOpen;
12 | Controller leap;
13 | //Update设定
14 | readonly float TrackStepTime = 0.1f;
15 | float m_CurTrackTime;
16 |
17 | //初始状态
18 | [SerializeField]
19 | bool m_IsSingleHandOpened;
20 | readonly float EnterDelayTime = 0.15f;//进入0.5s后触发
21 | float m_CurEnterDelayTime;
22 | Action m_OnEnterFunc;
23 |
24 | //运动状态
25 | [SerializeField] float m_Speed;//手掌运动速度,前后两个位置做差
26 | Vector m_PreviousPos;//前次的位置
27 | Vector m_Dir=Vector.Zero;//手掌运动的方向,CurPos-PrePos
28 |
29 | //结束状态
30 | Action m_OnEndFunc;
31 | public bool IsSingleHandOpened
32 | {
33 | get
34 | {
35 | return m_IsSingleHandOpened;
36 | }
37 | }
38 | public Vector Dir
39 | {
40 | get
41 | {
42 | return m_Dir;
43 | }
44 | }
45 |
46 | public float Speed
47 | {
48 | get
49 | {
50 | return m_Speed;
51 | }
52 | }
53 |
54 | public void RegisterFunc(Action m_OnEnter,Action m_OnEnd)
55 | {
56 | if(m_OnEnter!=null)
57 | {
58 | m_OnEndFunc += m_OnEnter;
59 | }
60 | if(m_OnEnd!=null)
61 | {
62 | m_OnEndFunc += m_OnEnd;
63 | }
64 | }
65 |
66 | public void CancelFunc(Action m_OnEnter, Action m_OnEnd)
67 | {
68 | if (m_OnEnter != null)
69 | {
70 | m_OnEndFunc -= m_OnEnter;
71 | }
72 | if (m_OnEnd != null)
73 | {
74 | m_OnEndFunc -= m_OnEnd;
75 | }
76 | }
77 | void Awake()
78 | {
79 | leap = LeapDriver.GetLeapCtrl();
80 | }
81 | // Update is called once per frame
82 | void Update ()
83 | {
84 | m_CurTrackTime+=Time.deltaTime;
85 | m_CurEnterDelayTime += Time.deltaTime;
86 |
87 | if(m_CurTrackTime>TrackStepTime)
88 | {
89 | m_CurTrackTime = 0f;
90 | //print(leap.Frame().Hands.Count);
91 | if (leap.Frame().Hands.Count == 1)
92 | {
93 | int index;
94 | int number = m_HandOpen.OpenNumber(out index);
95 | //只有一只手被检测到
96 |
97 | //如果只有一只手处于摊开状态
98 | if (number == 1)
99 | {
100 | //第一次伸掌
101 | if (!m_IsSingleHandOpened)
102 | {
103 | //满足延迟时间
104 | if (m_CurEnterDelayTime > EnterDelayTime)
105 | {
106 | m_CurEnterDelayTime = 0f;
107 |
108 | m_IsSingleHandOpened = true;
109 | m_PreviousPos = m_HandOpen.PalmPos[index];
110 | if (m_OnEndFunc != null)
111 | {
112 | m_OnEndFunc();
113 | }
114 | }
115 | }
116 | //已经处于伸掌状态
117 | else
118 | {
119 | Vector curDir = m_HandOpen.PalmPos[index];
120 | m_Dir = curDir - m_PreviousPos;
121 | m_Speed = curDir.DistanceTo(m_PreviousPos);
122 | m_PreviousPos = curDir;
123 | }
124 | }
125 | //不是伸掌状态
126 | else
127 | {
128 | //上一次是伸掌状态,说明这是一次手势的结束,触发结束事件
129 | if (m_IsSingleHandOpened)
130 | {
131 | if (m_OnEndFunc != null)
132 | {
133 | m_OnEndFunc();
134 | }
135 | }
136 | //初始化数据
137 | Reset();
138 | }
139 | }
140 | //不是伸掌状态
141 | else
142 | {
143 | //上一次是伸掌状态,说明这是一次手势的结束,触发结束事件
144 | if (m_IsSingleHandOpened)
145 | {
146 | if (m_OnEndFunc != null)
147 | {
148 | m_OnEndFunc();
149 | }
150 | }
151 | //初始化数据
152 | Reset();
153 | }
154 | }
155 |
156 | }
157 | ///
158 | /// 重置数据,如果判定失败时调用
159 | ///
160 | void Reset()
161 | {
162 | m_Speed = 0f;
163 | m_PreviousPos = Vector.Zero;
164 | m_Dir = Vector.Zero;
165 | m_IsSingleHandOpened = false;
166 | // m_CurTrackTime = 0f;
167 | m_CurEnterDelayTime = 0f;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/GustersPatch/Motion/SingleHandOpenTrack.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d77c06da365bb8149b6143623075ef83
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/State.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3c3f353e0a4acb24f9381216850007db
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/GustersPatch/State/FingerMatch.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 | ///
5 | /// 该方法提供对于单个手指匹配的算法,如伸直,弯曲
6 | /// 以后可能的改变:对于不同的场景可能要求有所不同,这里的阈值也许会随之改变
7 | ///
8 | public class FingerMatch
9 | {
10 | //弯曲状态的角度阈值
11 | static readonly float FingerBendState_Radian = Mathf.PI*4f / 18 ;//40度
12 | //伸直状态的角度阈值
13 | static readonly float FingerStrightState_Radian = Mathf.PI/12;//15度
14 |
15 | ///
16 | /// 手指伸直的状态,当根骨-指尖的方向和指向的偏差小于阀值时,判定手指为伸直状态。
17 | /// 注意无效的方向为零向量,先判定是零向量
18 | ///
19 | /// 对阈值做的微调
20 | ///
21 | public static bool StrightState(FingerData fingerData, float adjustBorder=0f)
22 | {
23 | bool isStright =false;
24 | Vector disalDir = fingerData.m_Point.m_Direction;
25 | //如果指尖方向为0向量,表示无效的数据
26 | if (!disalDir.Equals(Vector.Zero))
27 | {
28 | Vector fingerDir = fingerData.m_Point.m_Position - fingerData.m_Position;//指尖位置减去指根位置,由指根指向指尖的向量
29 | float radian = fingerDir.AngleTo(disalDir);
30 |
31 | if (radian < FingerStrightState_Radian + adjustBorder)
32 | {
33 | isStright = true;
34 | }
35 | }
36 | return isStright;
37 | }
38 |
39 | ///
40 | /// 判断一根手指是否处于弯曲状态
41 | ///
42 | /// 需要判定的手指数据
43 | /// 弯曲的阈值
44 | ///
45 | public static bool BendState(FingerData fingerData, float adjustBorder=0f)//,out float eulerAugle)
46 | {
47 | bool isBend = false;
48 |
49 | //eulerAugle = -1f;
50 | Vector disalDir = fingerData.m_Point.m_Direction;
51 | if( !disalDir.Equals(Vector.Zero) )
52 | {
53 | Vector fingerDir = fingerData.m_Point.m_Position - fingerData.m_Position;//指尖位置减去指根位置,指跟到指尖的向量
54 |
55 | float radian = fingerDir.AngleTo(disalDir);
56 | //eulerAugle = radian*180/Mathf.PI;
57 | //夹角超过定义的阈值时,认定为弯曲状态
58 | if (radian > FingerBendState_Radian + adjustBorder)
59 | {
60 | isBend = true;
61 | }
62 | }
63 |
64 | return isBend;
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/GustersPatch/State/FingerMatch.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a702dc512a165544c8a5dba83d7c3a13
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/State/HandBend.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 | using System.Collections.Generic;
5 | using System;
6 | ///
7 | /// 手势:除拇指以外的四指弯曲
8 | /// 匹配:2-3根手指(越接近垂直越难分辨),提供接口,默认识别3个手指,但是如果垂直方向的位置,指定2个手指即可
9 | /// 冲突:该状态与单指模式冲突,解决方案:同一场景下设定优先级
10 | ///
11 | public class HandBend : MonoBehaviour
12 | {
13 | [SerializeField] HandAndFingersPoint m_HandData;
14 |
15 | //开始弯曲和结束弯曲的触发事件,应该有时间淡入淡出设定,并不完善
16 | Action[] m_EnterBendFunc = new Action[2];
17 | Action[] m_ExitBendFunc = new Action[2];
18 |
19 | float[] m_BendingTime = new float[2];//此次弯曲的持续时间
20 | [SerializeField] bool[] m_IsEnterBended = new bool[2];//这次伸掌状态打开过
21 |
22 | int m_MatchNumber = 2;//手掌弯曲的状态需要几根手指匹配
23 | //const float FingerBendState_Radian = Mathf.PI*5.5f / 18 ;//55度
24 |
25 | Vector[] m_Dir = new Vector[2];//方向为掌心的指向,非弯曲状态设定为Zero
26 | float[] m_BendRadian = new float[2];//弯曲程度,用中指的弯曲度代替
27 |
28 | public bool[] IsEnterBended
29 | {
30 | get
31 | {
32 | return m_IsEnterBended;
33 | }
34 | }
35 |
36 | public float[] BendingTime
37 | {
38 | get
39 | {
40 | return m_BendingTime;
41 | }
42 | }
43 |
44 | public Vector[] Dir
45 | {
46 | get
47 | {
48 | return m_Dir;
49 | }
50 | }
51 | //手掌打开状态,事件注册接口
52 | public void AddOpenEnterMsg(Action leftOpen, Action rightOpen)
53 | {
54 | if (leftOpen != null)
55 | {
56 | m_EnterBendFunc[0] += leftOpen;
57 | }
58 |
59 | if (rightOpen != null)
60 | {
61 | m_EnterBendFunc[1] += rightOpen;
62 | }
63 | }
64 |
65 | //手掌打开结束状态,事件注册接口
66 | public void AddOpenExitMsg(Action leftOpen, Action rightOpen)
67 | {
68 | if (leftOpen != null)
69 | {
70 | m_ExitBendFunc[0] += leftOpen;
71 | }
72 |
73 | if (rightOpen != null)
74 | {
75 | m_ExitBendFunc[1] += rightOpen;
76 | }
77 | }
78 |
79 | ///
80 | /// set设定的时候限制在2,3两种取值
81 | ///
82 | public int MatchNumber
83 | {
84 | get
85 | {
86 | return m_MatchNumber;
87 | }
88 | set
89 | {
90 | if (value < 2)
91 | m_MatchNumber = 2;
92 | else if (value > 3)
93 | m_MatchNumber = 3;
94 | }
95 | }
96 | // Use this for initialization
97 | void Start () {
98 |
99 | }
100 |
101 | ///
102 | /// 每帧做手掌弯曲的判断
103 | ///
104 | void Update ()
105 | {
106 | BendCtrl(m_HandData.FingerDatas,m_HandData.PalmDatas);
107 | }
108 |
109 | void BendCtrl(Dictionary[] dic,PointData[] palmDatas)
110 | {
111 | Vector[] bendDir = new Vector[2];
112 | bool[] isBend = new bool[2];
113 | isBend[0] = BendState(dic[0], palmDatas[0], m_MatchNumber, out bendDir[0]);
114 | isBend[1] = BendState(dic[1], palmDatas[1], m_MatchNumber, out bendDir[1]);
115 |
116 | //有几组数据就遍历几次-限1或2次
117 | int count= dic.Length;
118 | for (int handIndex = 0; handIndex < count; handIndex++)
119 | {
120 | //这个if - else 结构依据m_IsEnterBended-isLeftOpen表示的二维坐标系,而出现的四个象限。
121 | //此次左手伸掌状态的最开始,触发
122 | //当不处于m_IsEnterBended状态,并且现在左手是伸掌状态才会触发
123 | if (!m_IsEnterBended[handIndex] && isBend[handIndex])
124 | {
125 | if (m_EnterBendFunc[handIndex] != null)
126 | {
127 | m_EnterBendFunc[handIndex]();
128 | }
129 | m_BendingTime[handIndex] = 0f;
130 | m_IsEnterBended[handIndex] = true;
131 |
132 | m_Dir[handIndex] = bendDir[handIndex];
133 | }
134 |
135 | //持续进行左手的伸掌状态
136 | //左手已经进入到伸掌状态,而且现在也是伸掌状态
137 | else if (m_IsEnterBended[handIndex] && isBend[handIndex])
138 | {
139 | m_BendingTime[handIndex] += Time.deltaTime;//该函数会在Update中调用。
140 | m_Dir[handIndex] = bendDir[handIndex];
141 | }
142 |
143 | //退出左手伸掌状态,这是此次持续伸掌的最后一瞬间伸掌
144 | //左手已经进入伸掌状态,并且现在不是伸掌状态
145 | else if (m_IsEnterBended[handIndex] && !isBend[handIndex])
146 | {
147 | if (m_ExitBendFunc[handIndex] != null)
148 | {
149 | m_ExitBendFunc[handIndex]();
150 | }
151 | m_IsEnterBended[handIndex] = false;
152 | m_BendingTime[handIndex] = 0f;
153 |
154 | m_Dir[handIndex] = bendDir[handIndex];
155 | }
156 |
157 | //本身不是处于伸掌状态,而且此次判断也不是伸掌状态
158 | else
159 | {
160 | m_IsEnterBended[handIndex] = false;
161 | m_BendingTime[handIndex] = 0f;
162 | m_Dir[handIndex] = Vector.Zero;
163 | }
164 | }
165 | }
166 | ///
167 | /// 判定一个手掌是否处于弯曲状态。
168 | /// 手掌中有matchNumber个手指满足即可,实际设定为2-3个
169 | ///
170 | ///
171 | ///
172 | /// 【返回值】:如果匹配成功方向为掌心的方向
173 | /// 是否为弯曲状态
174 | bool BendState( Dictionary dic_FingersData,PointData palmData, int matchNumber,out Vector dir )
175 | {
176 | bool isBend = false;
177 | dir = Vector.Zero;
178 | int count = 0;
179 | Dictionary fingersOutThumb = new Dictionary(dic_FingersData);
180 | fingersOutThumb.Remove(Finger.FingerType.TYPE_THUMB);
181 |
182 | var values = fingersOutThumb.Values;
183 |
184 | //float eulerAngule = 0f;
185 | //遍历四指,匹配个数满足设定个数认定手掌为弯曲,并且设定弯曲的方向为掌心方向。
186 | foreach (FingerData fingerData in values)
187 | {
188 | if (FingerMatch.BendState(fingerData))//,out eulerAngule))
189 | {
190 | count++;
191 | }
192 | }
193 | //print ("Bend Count:"+count+" BendEuler:"+eulerAngule);
194 | if (count >= matchNumber)
195 | {
196 | isBend = true;
197 | dir = palmData.m_Direction;
198 | }
199 |
200 | return isBend;
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/GustersPatch/State/HandBend.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a2c811986a39c3a4eac5c94187ccfbbd
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/State/HandFingerPoint.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | ///
8 | /// 手势:点击模式,拇指不考虑。食指处于伸直状态,其他三指处于弯曲状态
9 | /// 匹配方案:食指伸直,其他三个手指中有2-3个处于弯曲状态()
10 | ///
11 | public class HandFingerPoint : MonoBehaviour
12 | {
13 | [SerializeField] HandAndFingersPoint m_HandData;
14 |
15 | //开始弯曲和结束弯曲的触发事件,应该有时间淡入淡出设定,并不完善
16 | Action[] m_EnterPointFunc = new Action[2];
17 | Action[] m_ExitPointFunc = new Action[2];
18 |
19 | float[] m_PointingTime = new float[2];//此次指向手势的持续时间
20 | [SerializeField] bool[] m_IsEnterPointed = new bool[2];//这次伸掌状态打开过
21 |
22 | //匹配阈值设定
23 | int m_MatchNumber = 2;//手掌弯曲的状态需要几根手指匹配,在观察器
24 | //const float FingerBendState_Radian = Mathf.PI * 7 / 18;//70度
25 | //const float FingerStrightState_Radian = Mathf.PI / 12;//15度
26 | Vector[] m_Dir = new Vector[2];//方向为食指的指向,非弯曲状态设定为Zero
27 | Vector[] m_Pos = new Vector[2];//食指指尖的位置数据
28 | public bool[] IsEnterPointed
29 | {
30 | get
31 | {
32 | return m_IsEnterPointed;
33 | }
34 | }
35 |
36 | public float[] PointingTime
37 | {
38 | get
39 | {
40 | return m_PointingTime;
41 | }
42 | }
43 |
44 | public Vector[] Dir
45 | {
46 | get
47 | {
48 | return m_Dir;
49 | }
50 | }
51 | //手掌打开状态,事件注册接口
52 | public void AddOpenEnterMsg(Action leftOpen, Action rightOpen)
53 | {
54 | if (leftOpen != null)
55 | {
56 | m_EnterPointFunc[0] += leftOpen;
57 | }
58 |
59 | if (rightOpen != null)
60 | {
61 | m_EnterPointFunc[1] += rightOpen;
62 | }
63 | }
64 |
65 | //手掌打开结束状态,事件注册接口
66 | public void AddOpenExitMsg(Action leftOpen, Action rightOpen)
67 | {
68 | if (leftOpen != null)
69 | {
70 | m_ExitPointFunc[0] += leftOpen;
71 | }
72 |
73 | if (rightOpen != null)
74 | {
75 | m_ExitPointFunc[1] += rightOpen;
76 | }
77 | }
78 |
79 | ///
80 | /// set设定的时候限制在2,3两种取值
81 | ///
82 | public int MatchNumber
83 | {
84 | get
85 | {
86 | return m_MatchNumber;
87 | }
88 | set
89 | {
90 | if (value < 2)
91 | m_MatchNumber = 2;
92 | else if (value > 3)
93 | m_MatchNumber = 3;
94 | }
95 | }
96 |
97 | ///
98 | /// 每帧做手掌弯曲的判断
99 | ///
100 | void Update()
101 | {
102 | IndexPointCtrl(m_HandData.FingerDatas);
103 | }
104 |
105 | ///
106 | /// 多个手掌的手指数据是否处于食指指向状态,并对类的属性做出修改
107 | ///
108 | ///
109 | void IndexPointCtrl(Dictionary[] dic)
110 | {
111 | Vector[] indexPointDir = new Vector[2];
112 | bool[] isIndexPoint = new bool[2];
113 | isIndexPoint[0] = IndexPointState(dic[0], out indexPointDir[0]);
114 | isIndexPoint[1] = IndexPointState(dic[1], out indexPointDir[1]);
115 |
116 | //有几组数据就遍历几次-限1或2次
117 | int count = dic.Length;
118 | for (int handIndex = 0; handIndex < count; handIndex++)
119 | {
120 | //这个if - else 结构依据m_IsEnterBended-isLeftOpen表示的二维坐标系,而出现的四个象限。
121 | //此次左手伸掌状态的最开始,触发
122 | //当不处于m_IsEnterBended状态,并且现在左手是伸掌状态才会触发
123 | if (!m_IsEnterPointed[handIndex] && isIndexPoint[handIndex])
124 | {
125 | if (m_EnterPointFunc[handIndex] != null)
126 | {
127 | m_EnterPointFunc[handIndex]();
128 | }
129 | m_PointingTime[handIndex] = 0f;
130 | m_IsEnterPointed[handIndex] = true;
131 |
132 | m_Dir[handIndex] = indexPointDir[handIndex];
133 | }
134 |
135 | //持续进行左手的伸掌状态
136 | //左手已经进入到伸掌状态,而且现在也是伸掌状态
137 | else if (m_IsEnterPointed[handIndex] && isIndexPoint[handIndex])
138 | {
139 | m_PointingTime[handIndex] += Time.deltaTime;//该函数会在Update中调用。
140 | m_Dir[handIndex] = indexPointDir[handIndex];
141 | }
142 |
143 | //退出左手伸掌状态,这是此次持续伸掌的最后一瞬间伸掌
144 | //左手已经进入伸掌状态,并且现在不是伸掌状态
145 | else if (m_IsEnterPointed[handIndex] && !isIndexPoint[handIndex])
146 | {
147 | if (m_ExitPointFunc[handIndex] != null)
148 | {
149 | m_ExitPointFunc[handIndex]();
150 | }
151 | m_IsEnterPointed[handIndex] = false;
152 | m_PointingTime[handIndex] = 0f;
153 |
154 | m_Dir[handIndex] = indexPointDir[handIndex];
155 | }
156 |
157 | //本身不是处于伸掌状态,而且此次判断也不是伸掌状态
158 | else
159 | {
160 | m_IsEnterPointed[handIndex] = false;
161 | m_PointingTime[handIndex] = 0f;
162 | m_Dir[handIndex] = Vector.Zero;
163 | }
164 | }
165 | }
166 |
167 | ///
168 | /// 判定一个手掌是否处于食指指向状态。
169 | /// 手掌中有matchNumber个手指满足即可,实际设定为2个。
170 | ///
171 | ///
172 | ///
173 | /// 【返回值】:如果匹配成功方向为掌心的方向
174 | /// 是否为食指指向状态。
175 | bool IndexPointState(Dictionary dic_FingersData, out Vector dir)
176 | {
177 | bool isBend = false;
178 | dir = Vector.Zero;
179 | int count = 0;
180 | Dictionary fingersOutThumbAndIndex = new Dictionary(dic_FingersData);
181 | fingersOutThumbAndIndex.Remove(Finger.FingerType.TYPE_THUMB);
182 |
183 | //如果不存在食指的信息就不需要继续判断了
184 | if (fingersOutThumbAndIndex.ContainsKey(Finger.FingerType.TYPE_INDEX))
185 | {
186 | FingerData indexFinger = fingersOutThumbAndIndex[Finger.FingerType.TYPE_INDEX];
187 | //食指处于伸直状态才继续进行判断
188 | if (FingerMatch.StrightState(indexFinger))
189 | {
190 | fingersOutThumbAndIndex.Remove(Finger.FingerType.TYPE_INDEX);
191 |
192 | var values = fingersOutThumbAndIndex.Values;
193 | //遍历四指,匹配个数满足设定个数认定手掌为弯曲,并且设定弯曲的方向为掌心方向。
194 | foreach (FingerData fingerData in values)
195 | {
196 | if (FingerMatch.BendState(fingerData))
197 | {
198 | count++;
199 | }
200 | }
201 |
202 | //log
203 | //print("FingerPoint bend count:"+count);
204 |
205 | //判定弯曲手指的个数是否符合要求
206 | //判定食指是否是伸直状态
207 | if (count >= m_MatchNumber)
208 | {
209 | isBend = true;
210 | dir = indexFinger.m_Point.m_Direction;//食指的方向
211 | }
212 | }
213 | }
214 | return isBend;
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/GustersPatch/State/HandFingerPoint.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: aefe2c53e78dbca429d8566b0d57678c
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/State/HandOpen.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | ///
8 | /// 匹配是摊开手的状态,通知相应的操作。在动作匹配中也用到
9 | /// 细分为水平摊开,垂直摊开,一般摊开
10 | /// 数据匹配方式:除大拇指以外的四个手指中有3个手指是伸直状态,并且中指必须是伸直状态
11 | ///
12 | public class HandOpen : MonoBehaviour
13 | {
14 | [SerializeField] HandAndFingersPoint m_HandData;
15 |
16 | Action[] m_EnterOpenFunc=new Action[2];
17 | Action[] m_ExitOpenFunc=new Action[2];
18 |
19 | float[] m_OpeningTime =new float[2];
20 | [SerializeField] bool[] m_IsEnterOpened = new bool[2];//这次伸掌状态时候打开过
21 | Vector[] m_Dir=new Vector[2];//伸掌的手掌方向用中指指尖方向表示,zero表示非伸掌状态
22 | Vector[] m_PalmPos = new Vector[2];
23 | Vector[] m_PalmDir = new Vector[2];
24 | //设定匹配时手指需要满足的个数
25 | int m_MatchNumber = 3;//2或3
26 |
27 | //中间变量
28 | Vector[] m_OpenDir = new Vector[2];
29 |
30 | //borderSettings
31 | // const float FingerStrightState_Radian = Mathf.PI/12;//15度
32 |
33 | public Vector[] Dir
34 | {
35 | get {
36 | return m_Dir;
37 | }
38 | }
39 |
40 | public Vector[] PalmPos
41 | {
42 | get
43 | {
44 | return m_PalmPos;
45 | }
46 |
47 | }
48 |
49 | public Vector[] PalmDir
50 | {
51 | get
52 | {
53 | return m_PalmDir;
54 | }
55 |
56 | }
57 |
58 | public bool[] IsEnterOpened
59 | {
60 | get {
61 | return m_IsEnterOpened;
62 | }
63 | }
64 |
65 | public float[] OpeningTime
66 | {
67 | get
68 | {
69 | return m_OpeningTime;
70 | }
71 | }
72 |
73 | ///
74 | /// 只能在下一帧起作用
75 | ///
76 | /// The set match number.
77 | public int SetMatchNumber
78 | {
79 | set
80 | {
81 | m_MatchNumber = value;
82 | }
83 | }
84 | //手掌打开状态,事件注册接口
85 | public void AddOpenEnterMsg(Action leftOpen, Action rightOpen)
86 | {
87 | if (leftOpen != null)
88 | {
89 | m_EnterOpenFunc[0] += leftOpen;
90 | }
91 |
92 | if (rightOpen != null)
93 | {
94 | m_EnterOpenFunc[1] += rightOpen;
95 | }
96 | }
97 |
98 | //手掌打开结束状态,事件注册接口
99 | public void AddOpenExitMsg(Action leftOpen, Action rightOpen)
100 | {
101 | if (leftOpen != null)
102 | {
103 | m_ExitOpenFunc[0] += leftOpen;
104 | }
105 |
106 | if (rightOpen != null)
107 | {
108 | m_ExitOpenFunc[1] += rightOpen;
109 | }
110 | }
111 |
112 | void Awake()
113 | {
114 | if (m_HandData == null)
115 | Debug.LogError("It's no ref");
116 | }
117 |
118 | void Update()
119 | {
120 | // print ("m_IsEnterOpened0:"+m_IsEnterOpened[0]+" 1:"+m_IsEnterOpened[1]);
121 |
122 | OpenCtrl( m_HandData.FingerDatas ,m_HandData.PalmDatas);
123 | }
124 |
125 | ///
126 | /// 判断左右手是否处于伸掌状态,触发相应事件和消息,记录相应的值
127 | ///
128 | /// 代表左右手的数据源
129 | void OpenCtrl(Dictionary[] dic,PointData[] palmData)
130 | {
131 |
132 | bool[] isOpen = new bool[2];
133 |
134 | isOpen[0] = OpenState(dic[0],palmData[0], out m_OpenDir[0],out m_PalmPos[0],out m_PalmDir[0]);
135 | isOpen[1] = OpenState(dic[1], palmData[1], out m_OpenDir[1], out m_PalmPos[1], out m_PalmDir[1]);
136 |
137 | //int count = dic.Length;
138 | for (int handIndex = 0; handIndex < 2; handIndex++)
139 | {
140 | //这个if - else 结构依据m_IsEnterOpened-isLeftOpen表示的二维坐标系,而出现的四个象限。
141 | //此次左手伸掌状态的最开始,触发
142 | //当不处于m_IsEnterOpened状态,并且现在左手是伸掌状态才会触发
143 | if (!m_IsEnterOpened[handIndex] && isOpen[handIndex])
144 | {
145 | if (m_EnterOpenFunc[handIndex] != null)
146 | {
147 | m_EnterOpenFunc[handIndex]();
148 | }
149 | m_OpeningTime[handIndex] = 0f;
150 | m_IsEnterOpened[handIndex] = true;
151 |
152 | m_Dir[handIndex] = m_OpenDir[handIndex];
153 | // m_PalmPos[handIndex] = m_PalmPos[handIndex];
154 |
155 | }
156 |
157 | //持续进行左手的伸掌状态
158 | //左手已经进入到伸掌状态,而且现在也是伸掌状态
159 | else if (m_IsEnterOpened[handIndex] && isOpen[handIndex])
160 | {
161 | m_OpeningTime[handIndex] += Time.deltaTime;//该函数会在Update中调用。
162 | m_Dir[handIndex] = m_OpenDir[handIndex];
163 | // m_PalmPos[handIndex] = m_PalmPos[handIndex];
164 | }
165 |
166 | //退出左手伸掌状态,这是此次持续伸掌的最后一瞬间伸掌
167 | //左手已经进入伸掌状态,并且现在不是伸掌状态
168 | else if (m_IsEnterOpened[handIndex] && !isOpen[handIndex])
169 | {
170 | if (m_ExitOpenFunc[handIndex] != null)
171 | {
172 | m_ExitOpenFunc[handIndex]();
173 | }
174 | m_IsEnterOpened[handIndex] = false;
175 | m_OpeningTime[handIndex] = 0f;
176 |
177 | m_Dir[handIndex] = m_OpenDir[handIndex];
178 | // m_PalmPos[handIndex] = m_PalmPos[handIndex];
179 | }
180 | //本身不是处于伸掌状态,而且此次判断也不是伸掌状态
181 | else
182 | {
183 | m_IsEnterOpened[handIndex] = false;
184 | m_OpeningTime[handIndex] = 0f;
185 | m_Dir[handIndex] = Vector.Zero;
186 | m_PalmPos[handIndex] = Vector.Zero;
187 | m_PalmDir[handIndex] = Vector.Zero;
188 | }
189 | }
190 | }
191 |
192 | ///
193 | /// 通过给定手指数据,判断是否处于伸手状态
194 | /// 当三个手指符合不包括拇指,判定为伸掌状态
195 | ///
196 | ///
197 | ///
198 | bool OpenState( Dictionary dic_FingersData,PointData palmData,out Vector dir,out Vector palmPos,out Vector palmDir)
199 | {
200 | bool isOpen = false;
201 | dir = Vector.Zero;
202 | palmPos = Vector.Zero;
203 | palmDir = Vector.Zero;
204 | int count = 0;
205 | Dictionary fingersOutThumb = new Dictionary(dic_FingersData);
206 | fingersOutThumb.Remove(Finger.FingerType.TYPE_THUMB);
207 |
208 | var values = fingersOutThumb.Values;
209 |
210 | foreach( FingerData fingerData in values )
211 | {
212 | if (FingerMatch.StrightState(fingerData))
213 | {
214 | count++;
215 | }
216 | }
217 | // print ("FingerStright Count:"+count);
218 | if (count >= m_MatchNumber)
219 | {
220 |
221 | isOpen = true;
222 | }
223 |
224 | //指定输出的伸手方向为中指指向
225 | //如果没有识别中指,判定为false
226 | //如果中指不是伸直状态,判定为false
227 | if (fingersOutThumb.ContainsKey(Finger.FingerType.TYPE_MIDDLE))
228 | {
229 | FingerData middleFingerData = fingersOutThumb[Finger.FingerType.TYPE_MIDDLE];
230 | if (FingerMatch.StrightState(middleFingerData))
231 | {
232 | dir = middleFingerData.m_Point.m_Direction;
233 | palmPos = palmData.m_Position;
234 | palmDir = palmData.m_Direction;
235 | }
236 | else
237 | {
238 | isOpen = false;
239 | }
240 | }
241 | else
242 | {
243 | isOpen = false;
244 | }
245 |
246 | return isOpen;
247 | }
248 |
249 | ///
250 | /// 计算当前手掌伸开的个数
251 | /// 返回个数
252 | ///
253 | /// 记录手掌打开的索引值,返回值大于0时有效
254 | /// 当前处于摊开的手掌个数
255 | public int OpenNumber(out int index)
256 | {
257 | int number = 0;
258 | index =0;
259 | if(m_IsEnterOpened[0]==true)
260 | {
261 | if(m_IsEnterOpened[1]==true)
262 | {
263 | number = 2;
264 | }
265 | else
266 | {
267 | number = 1;
268 | index = 0;
269 | }
270 | }
271 | else if(m_IsEnterOpened[1]==true)
272 | {
273 | number = 1;
274 | index = 1;
275 | }
276 | else
277 | {
278 | number = 0;
279 | }
280 | return number;
281 | }
282 | }
283 |
284 |
--------------------------------------------------------------------------------
/GustersPatch/State/HandOpen.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 771fc0f3a6b76694a9651acf7c08e006
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/GustersPatch/State/HandPinchGroup.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using Leap;
4 |
5 | ///Log1:手势废弃,Leap API不能正确识别手指的前方,尤其是整根手指从骨根弯曲,不明白Finger.Direction的定义。
6 | ///在这里,不能正确匹配手指伸直的判定
7 | ///
8 | /// 捏手势判定
9 | /// 可用的信息:
10 | /// 1.食指和拇指指尖之间的距离
11 | /// 2.是否是捏手势
12 | /// 【注意】捏手势的优先级应当设定为很低,因为它匹配成功只需要食指伸直。
13 | ///
14 | public class HandPinchGroup : MonoBehaviour
15 | {
16 | [SerializeField] HandAndFingersPoint m_HandData;
17 | bool[] m_IsHandPinch = new bool[2];
18 | float[] m_Distance = new float[2];
19 | readonly float EnterDisThreshold;//要大于这个阈值才认定进入
20 |
21 | public bool[] IsHandPinch
22 | {
23 | get
24 | {
25 | return m_IsHandPinch;
26 | }
27 | }
28 | public float[] Distance
29 | {
30 | get
31 | {
32 | return m_Distance;
33 | }
34 | }
35 | // Use this for initialization
36 | void Start () {
37 |
38 | }
39 |
40 | // Update is called once per frame
41 | void Update ()
42 | {
43 | for (int i = 0; i < 2; i++)
44 | {
45 | //含有中指和食指的数据
46 | if (m_HandData.FingerDatas[i].ContainsKey(Finger.FingerType.TYPE_INDEX) &&
47 | m_HandData.FingerDatas[i].ContainsKey(Finger.FingerType.TYPE_THUMB))
48 | {
49 | FingerData indexFingerData = m_HandData.FingerDatas[i][Finger.FingerType.TYPE_INDEX];
50 | Vector indexPos = indexFingerData.m_Point.m_Position;
51 | Vector thumbPos = m_HandData.FingerDatas[i][Finger.FingerType.TYPE_THUMB].m_Point.m_Position;
52 |
53 | //print("IndexToThumb:" + indexPos.DistanceTo(thumbPos) );
54 | print((indexFingerData.m_Point.m_Position-indexFingerData.m_Position).AngleTo(indexFingerData.m_Point.m_Direction)*180/Mathf.PI);
55 | if (FingerMatch.StrightState(indexFingerData))
56 | {
57 | m_IsHandPinch[i] = true;
58 | m_Distance[i] = indexPos.DistanceTo(thumbPos);
59 |
60 | }
61 | else
62 | {
63 | m_IsHandPinch[i] = false;
64 | m_Distance[i] = 0f;
65 | }
66 | }
67 | else
68 | {
69 | m_IsHandPinch[i] = false;
70 | m_Distance[i] = 0f;
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/GustersPatch/State/HandPinchGroup.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6496059320948834fb98a6b23bd5f444
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Leap-Motion-In-Unity3D
2 | ### **Leap Motion体感控制器在Unity3D中完成自定义的手势识别方案**
3 | ### **包含以下几部分:**
4 | + 基本数据结构
5 | + 手指的匹配
6 | + 手势状态的匹配
7 | + 手势运动的匹配
8 |
9 | ### **特点:**
10 | + 手势采用事件响应,所以外部可以定制手势特定时刻触发的事件
11 | + 手势通过阈值调节匹配的精确度
12 | + 动作通过离散的数据监测保证匹配行为的合理
13 |
14 | [ **博文链接:从最开始到编写算法的思路** ](http://blog.csdn.net/u012289636/article/details/46883731)
15 |
--------------------------------------------------------------------------------