使用LeapMotion做手势识别,控制物体的放大缩小/移动/旋转/单选
需要做的效果是:
- 伸出食指和中指,物体旋转
- 食指中指加拇指,物体移动
- 五指伸出手掌垂直,物体进行放大缩小
- 食指单指伸出,进入选择状态,选择单个物体控制
准备工作
需要做的是对手指状态的判断,看了一些其他博客,设置阈值判断手指的伸直弯曲没看明白,我用的是对手指末端指向指尖向量的判断来处理手指的状态.
导入LeapMotion官方包之后,在LeapMotion文件下选择Core->Examples目录下选择Desktop
在这个Demo上进行后续的操作;
我做的这个是单手操作的,所以手的识别处理没做,指定左手操作.
场景列表如下,因为有食指选中的效果,所以将所有物体放在一个父节点下,子物体都要有碰撞盒.父物体可以不带碰撞盒.
代码编写
新建一个脚本Manager,添加命名空间using Leap.Unity;
因为我的是食指单指伸出进入选中状态,所以需要获取指尖的坐标,在Hierarchy面板中,将HandModels下的物体拖到对应的属性中;
食指单指的状态
根据各个手指的末端指向手指的向量来判断手指是否是伸直状态.用一个Bool值来记录是否进入单指状态,默认是False;
在伸直状态下用射线检测碰撞的物体,当遇到的是单个物体,用来记录单指状态的bool值 变为true;
shexian这个bool值是用来判断射线检测是否开启,默认为true;当检测到是单个物体的时候shexian=false;
void IndexDanZhi()
{
Vector3 tippos = indexL.GetTipPosition();//当前手指指尖的位置坐标
Vector3 direction = indexL.GetBoneDirection(1);//末端到指尖的向量
Vector3 Tdirection = ThumbL.GetBoneDirection(2);//拇指
Vector3 Mdirection = Middle.GetBoneDirection(1);//中指
Vector3 Pdirection = Pinky.GetBoneDirection(1);//无名指
muzhi = Tdirection;
zhongzhi = Mdirection;
if (direction.y > 0 && Mdirection.y < -0.1f&& Pdirection.y < 0 && Tdirection.x < 0)
{
Debug.Log("单指运行");
Vector2 radion = Camera.main.WorldToScreenPoint(tippos);
ray = Camera.main.ScreenPointToRay(radion);
Debug.DrawRay(ray.origin, ray.direction, Color.red);
play.GetComponent<BoxCollider>().enabled = false;
if (Physics.Raycast(ray, out hit, int.MaxValue))
{
if (hit.transform.tag == "cube")
{
foreach (var box in cubes)
{
if (box.name.Equals(hit.transform.name) && shexian)
{
if (box.GetComponent<MeshRenderer>().material.color == Color.white)
{
box.GetComponent<MeshRenderer>().material.color = Color.red;
danzhigo = box;
IsDanZhi = true;
//shexian = false;
}
else
{
danzhigo.GetComponent<MeshRenderer>().material.color = Color.white;
//shexian = true;
}
}
else if (!box.name.Equals(hit.transform.name) && shexian)
{
{
IsDanZhi = false;
box.GetComponent<MeshRenderer>().material.color = Color.white;
}
}
}
//IsDanZhi = true;
//for (int j = 0; j < cubes.Length; j++)
//{
// if (cubes[j].name.Equals(hit.transform.name) && shexian)
// {
// if (cubes[j].GetComponent<MeshRenderer>().material.color == Color.white)
// {
// cubes[j].GetComponent<MeshRenderer>().material.color = Color.red;
// danzhigo = cubes[j];
// shexian = false;
// }
// else
// {
// danzhigo.GetComponent<MeshRenderer>().material.color = Color.white;
// shexian = true;
// }
// }
//else
//{
// cubes[j].GetComponent<MeshRenderer>().material.color = Color.white;
// IsDanZhi = false;
// // shexian = false;
//}
//}
}
shexian = false;
print((shexian));
}
else
{
shexian = true;
Debug.Log("离开物体"+shexian);
}
}
}
旋转
void IsRotation()
{
Vector3 tippos = indexL.GetTipPosition();//当前手指指尖的位置坐标
Vector3 direction = indexL.GetBoneDirection(1);//食指末端到指尖的向量
Vector3 Tdirection = ThumbL.GetBoneDirection(2);//拇指
Vector3 Mdirection = Middle.GetBoneDirection(1);//中指
Vector3 Pdirection = Pinky.GetBoneDirection(1);//无名指
if (direction.y > 0 && Mdirection.y > 0 && Tdirection.x < 0 && Pdirection.y < 0)
{
Debug.Log("旋转运行");
// Vector2 radion = Camera.main.WorldToScreenPoint(tippos);
// ray = Camera.main.ScreenPointToRay(radion);
// Debug.DrawRay(ray.origin, ray.direction, Color.red);
// if (Physics.Raycast(ray, out hit, int.MaxValue))
// {
if (IsDanZhi == false)
{
//foreach (var v in cubes)
//{
// ToRotate(v.transform);
//}
ToRotate(play.transform);
}
// }
if (IsDanZhi)
{
ToRotate(danzhigo.transform);
}
}
}
//旋转控制
void ToRotate(Transform po)
{
if (palmtra.position.x > 0)
{
po.Rotate(0, -5, 0);
}
if (palmtra.position.x < 0)
{
po.Rotate(0, 5, 0);
}
if (palmtra.position.y < 0)
{
po.Rotate(-5, 0, 0);
}
if (palmtra.position.y > 0)
{
po.Rotate(5, 0, 0);
}
}
移动
/// <summary>
/// 移动
/// </summary>
void IsMove()
{
Vector3 tippos = indexL.GetTipPosition();//当前手指指尖的位置坐标
Vector3 direction = indexL.GetBoneDirection(1);//食指末端到指尖的向量
Vector3 Tdirection = ThumbL.GetBoneDirection(2);//拇指
Vector3 Mdirection = Middle.GetBoneDirection(1);//中指
Vector3 Pdirection = Pinky.GetBoneDirection(1);//无名指
if (direction.y > 0 && Mdirection.y > 0 && Tdirection.x > 0.1f && Pdirection.y < 0)
{
Debug.Log("移动运行");
Vector2 radion = Camera.main.WorldToScreenPoint(tippos);
// ray = Camera.main.ScreenPointToRay(radion);
// Debug.DrawRay(ray.origin, ray.direction, Color.red);
// if (Physics.Raycast(ray, out hit, int.MaxValue))
//{
if (IsDanZhi == false)
{
play.transform.position = new Vector3(palmtra.position.x, palmtra.transform.position.y, disz);
}
//}
if (IsDanZhi)
{
danzhigo.transform.position = new Vector3(palmtra.transform.position.x, palmtra.transform.position.y, danzhigo.transform.position.z);
}
}
}
放大缩小
/// <summary>
/// 放大缩小
/// </summary>
void IsScace()
{
Vector3 tippos = indexL.GetTipPosition();//当前手指指尖的位置坐标
Vector3 direction = indexL.GetBoneDirection(1);//末端到指尖的向量
Vector3 Tdirection = ThumbL.GetBoneDirection(2);//拇指
Vector3 Mdirection = Middle.GetBoneDirection(1);//中指
Vector3 Pdirection = Pinky.GetBoneDirection(1);//无名指
float ang = Vector3.Angle(palmtra.forward, Vector3.forward);
e = ang;
if (direction.y > 0 && Mdirection.y > 0 && Pdirection.y > 0 && Tdirection.x > 0 && ang > 80 && ang < 110)
{
Debug.Log("缩放运行");
Vector3 v3 = new Vector3(palmtra.position.z - 0.05f, palmtra.position.z - 0.05f, palmtra.position.z - 0.05f);
if (IsDanZhi == false)
{
//play.transform.localScale = v3;
foreach (var V in cubes)
{
V.transform.localScale = v3; ;
}
}
if (IsDanZhi)
{
danzhigo.transform.localScale = v3;
}
}
}
完整脚本
using System.Collections;
using System.Collections.Generic;
using Leap;
using Leap.Unity;
using Leap.Unity.Infix;
using UnityEngine;
using UnityEngine.UI;
public class Manager : MonoBehaviour
{
public Transform[] bonesL;//左手所有手指指尖
public HandModelBase leftHand;//左手
public Transform palmtra;//左手掌心
public RigidFinger indexL;//左手食指
public RigidFinger ThumbL;//左手拇指
public RigidFinger Middle;//左手中指
public RigidFinger Pinky;//左手无名指
public GameObject play;//物体的整体
public GameObject[] cubes;//场景中的物体
public Vector3 shouzhang, muzhi, zhongzhi;//测试用
public bool IsDanZhi = false;
public float e;//手掌和Z正方向的夹角,测试用
private static GameObject danzhigo;//选中的物体
List<HandModelBase> handModelList = new List<HandModelBase>();
private float disz;
void Start()
{
disz = play.transform.position.z;
}
// Update is called once per frame
void Update()
{
// 如果两个手都没有识别 清空 list 表
if (!leftHand.IsTracked)
{
if (handModelList.Contains(leftHand))
{
handModelList.Remove(leftHand);
}
}
// 鼠标移动 根据 list[0] 去操作,判断识别手的先后关系
if (leftHand != null && leftHand.IsTracked)
{
if (!handModelList.Contains(leftHand))
{
handModelList.Add(leftHand);
}
}
shouzhang = palmtra.position;
IndexDanZhi();
IsRotation();
IsMove();
IsScace();
// QueDan();
}
void LateUpdate()
{
}
Ray ray;
RaycastHit hit;
/// <summary>
/// 食指单指伸出
/// </summary>
void IndexDanZhi()
{
Vector3 tippos = indexL.GetTipPosition();//当前手指指尖的位置坐标
Vector3 direction = indexL.GetBoneDirection(1);//末端到指尖的向量
Vector3 Tdirection = ThumbL.GetBoneDirection(2);//拇指
Vector3 Mdirection = Middle.GetBoneDirection(1);//中指
Vector3 Pdirection = Pinky.GetBoneDirection(1);//无名指
muzhi = Tdirection;
zhongzhi = Mdirection;
if (direction.y > 0 && Mdirection.y < -0.1f&& Pdirection.y < 0 && Tdirection.x < 0)
{
Debug.Log("单指运行");
Vector2 radion = Camera.main.WorldToScreenPoint(tippos);
ray = Camera.main.ScreenPointToRay(radion);
Debug.DrawRay(ray.origin, ray.direction, Color.red);
play.GetComponent<BoxCollider>().enabled = false;
if (Physics.Raycast(ray, out hit, int.MaxValue))
{
if (hit.transform.tag == "cube")
{
foreach (var box in cubes)
{
if (box.name.Equals(hit.transform.name) && shexian)
{
if (box.GetComponent<MeshRenderer>().material.color == Color.white)
{
box.GetComponent<MeshRenderer>().material.color = Color.red;
danzhigo = box;
IsDanZhi = true;
//shexian = false;
}
else
{
danzhigo.GetComponent<MeshRenderer>().material.color = Color.white;
//shexian = true;
}
}
else if (!box.name.Equals(hit.transform.name) && shexian)
{
{
IsDanZhi = false;
box.GetComponent<MeshRenderer>().material.color = Color.white;
}
}
}
//IsDanZhi = true;
//for (int j = 0; j < cubes.Length; j++)
//{
// if (cubes[j].name.Equals(hit.transform.name) && shexian)
// {
// if (cubes[j].GetComponent<MeshRenderer>().material.color == Color.white)
// {
// cubes[j].GetComponent<MeshRenderer>().material.color = Color.red;
// danzhigo = cubes[j];
// shexian = false;
// }
// else
// {
// danzhigo.GetComponent<MeshRenderer>().material.color = Color.white;
// shexian = true;
// }
// }
//else
//{
// cubes[j].GetComponent<MeshRenderer>().material.color = Color.white;
// IsDanZhi = false;
// // shexian = false;
//}
//}
}
shexian = false;
print((shexian));
}
else
{
shexian = true;
Debug.Log("离开物体"+shexian);
}
}
}
public bool shexian = true;
/// <summary>
/// 食指和中指,控制旋转
/// </summary>
void IsRotation()
{
Vector3 tippos = indexL.GetTipPosition();//当前手指指尖的位置坐标
Vector3 direction = indexL.GetBoneDirection(1);//食指末端到指尖的向量
Vector3 Tdirection = ThumbL.GetBoneDirection(2);//拇指
Vector3 Mdirection = Middle.GetBoneDirection(1);//中指
Vector3 Pdirection = Pinky.GetBoneDirection(1);//无名指
if (direction.y > 0 && Mdirection.y > 0 && Tdirection.x < 0 && Pdirection.y < 0)
{
Debug.Log("旋转运行");
// Vector2 radion = Camera.main.WorldToScreenPoint(tippos);
// ray = Camera.main.ScreenPointToRay(radion);
// Debug.DrawRay(ray.origin, ray.direction, Color.red);
// if (Physics.Raycast(ray, out hit, int.MaxValue))
// {
if (IsDanZhi == false)
{
//foreach (var v in cubes)
//{
// ToRotate(v.transform);
//}
ToRotate(play.transform);
}
// }
if (IsDanZhi)
{
ToRotate(danzhigo.transform);
}
}
}
//旋转控制
void ToRotate(Transform po)
{
if (palmtra.position.x > 0)
{
po.Rotate(0, -5, 0);
}
if (palmtra.position.x < 0)
{
po.Rotate(0, 5, 0);
}
if (palmtra.position.y < 0)
{
po.Rotate(-5, 0, 0);
}
if (palmtra.position.y > 0)
{
po.Rotate(5, 0, 0);
}
}
/// <summary>
/// 移动
/// </summary>
void IsMove()
{
Vector3 tippos = indexL.GetTipPosition();//当前手指指尖的位置坐标
Vector3 direction = indexL.GetBoneDirection(1);//食指末端到指尖的向量
Vector3 Tdirection = ThumbL.GetBoneDirection(2);//拇指
Vector3 Mdirection = Middle.GetBoneDirection(1);//中指
Vector3 Pdirection = Pinky.GetBoneDirection(1);//无名指
if (direction.y > 0 && Mdirection.y > 0 && Tdirection.x > 0.1f && Pdirection.y < 0)
{
Debug.Log("移动运行");
Vector2 radion = Camera.main.WorldToScreenPoint(tippos);
// ray = Camera.main.ScreenPointToRay(radion);
// Debug.DrawRay(ray.origin, ray.direction, Color.red);
// if (Physics.Raycast(ray, out hit, int.MaxValue))
//{
if (IsDanZhi == false)
{
play.transform.position = new Vector3(palmtra.position.x, palmtra.transform.position.y, disz);
}
//}
if (IsDanZhi)
{
danzhigo.transform.position = new Vector3(palmtra.transform.position.x, palmtra.transform.position.y, danzhigo.transform.position.z);
}
}
}
/// <summary>
/// 放大缩小
/// </summary>
void IsScace()
{
Vector3 tippos = indexL.GetTipPosition();//当前手指指尖的位置坐标
Vector3 direction = indexL.GetBoneDirection(1);//末端到指尖的向量
Vector3 Tdirection = ThumbL.GetBoneDirection(2);//拇指
Vector3 Mdirection = Middle.GetBoneDirection(1);//中指
Vector3 Pdirection = Pinky.GetBoneDirection(1);//无名指
float ang = Vector3.Angle(palmtra.forward, Vector3.forward);
e = ang;
if (direction.y > 0 && Mdirection.y > 0 && Pdirection.y > 0 && Tdirection.x > 0 && ang > 80 && ang < 110)
{
Debug.Log("缩放运行");
Vector3 v3 = new Vector3(palmtra.position.z - 0.05f, palmtra.position.z - 0.05f, palmtra.position.z - 0.05f);
if (IsDanZhi == false)
{
//play.transform.localScale = v3;
foreach (var V in cubes)
{
V.transform.localScale = v3; ;
}
}
if (IsDanZhi)
{
danzhigo.transform.localScale = v3;
}
}
}
}