Kinect体感SDK接入以及切水果案例代码分析

本次解析案例所需工具:XBOX2代,UnitykinectSDK版本:Kinect for unity sdk v2.9,unity版本:2017.4.35
1:新建unity工程,其他插件看项目需求需要再导入:在这里插入图片描述2:新建场景,并创建空物体:GameManager,并在该物体上挂载图中对应的脚本:KinectManager是初始化体感设备接入数据的,必须要挂载。
在这里插入图片描述
3:获取体感设备得到的图像,在Update中实时检测(可能发生出界丢失图像的后果,所以需要实时监测)

    if ( KinectManager.Instance.IsInitialized())
            {
                //获取体感设备监测到的实时图像
                //  GetComponent<UnityEngine.UI.RawImage>().texture = KinectManager.Instance.GetUsersClrTex();
                //获取体感设备监测到的彩色图像 ,显示的非真是图像,表达能力有限,具体插上设备自己看!
                GetComponent<UnityEngine.UI.RawImage>().texture = KinectManager.Instance.GetUsersLblTex();
            }

4:手势识别检测代码:

 using System;
using System.Collections.Generic;

using UnityEngine; 

public class HandTrailRender : MonoBehaviour
{
    private RectTransform canvasRectTransform;
    public bool isHandLeft ; 
    float handTimer = 0.5f;
    private float maxHandTimer = 0.5f;
    #region 变量
    private TrailRenderer trailrenderer;
    Transform mSelfTrans; 
    /// <summary>
    /// 线段的z轴坐标
    /// </summary>
    const float LINE_POS_Z = 10;
    Vector2 maxPos;
    #endregion
    //定义跟踪的骨骼
    private KinectInterop.JointType handRightType = KinectInterop.JointType.HandRight;
    private KinectInterop.JointType handLeftType = KinectInterop.JointType.HandLeft;

    //手部跟踪平滑度 
    private float distanceToCamera = 10f;
     
    void Awake()
    { 
        trailrenderer = transform.GetComponent<TrailRenderer>();
        mSelfTrans = transform;
    }
   
    void Update()
    { 
        //得到左右手的屏幕位置 
        Vector3 handrightPos = GetjointPos((int)handRightType);
        Vector3 handLeftPos = GetjointPos((int)handLeftType);
        
        transform.position = isHandLeft ? handLeftPos : handrightPos;
      
    }
    //获取手势的在屏幕上的坐标
    private Vector3 GetjointPos(int handType)
    {
        if (KinectManager.Instance!=null&&KinectManager.Instance.IsInitialized())
        {
            if (KinectManager.Instance.IsUserDetected())
            {
                long userID= KinectManager.Instance.GetPrimaryUserID();
                if (GameManager.instance.kinectUserIDs!=null&&GameManager.instance.kinectUserIDs.Count>0)
                {
                    userID = GameManager.instance.kinectUserIDs[0];
                }
                else
                {
                    userID = KinectManager.Instance.GetPrimaryUserID();
                }
                if (KinectManager.Instance.IsJointTracked(userID, handType))
                {
                    //得到手势坐标
                    Vector3 handPos = KinectManager.Instance.GetJointKinectPosition(userID, handType);
                    
                    if (handPos != Vector3.zero)
                    {
                        // 3 d位置深度
                        Vector2 posDepth = KinectManager.Instance.MapSpacePointToDepthCoords(handPos);
                        ushort depthValue = KinectManager.Instance.GetDepthForPixel((int)posDepth.x, (int)posDepth.y);

                        if (depthValue > 0)
                        {
                            // 颜色pos深度pos
                            Vector2 posColor = KinectManager.Instance.MapDepthPointToColorCoords(posDepth, depthValue);

                            float xNorm = (float)posColor.x / KinectManager.Instance.GetColorImageWidth();
                            float yNorm = 1.0f - (float)posColor.y / KinectManager.Instance.GetColorImageHeight();

                            Vector3 vPosOverlay = Camera.main.ViewportToWorldPoint(new Vector3(xNorm, yNorm, distanceToCamera));

                            return vPosOverlay; 
                        }
                    } 
                }
            }
        }
        return Vector3.zero;
    }   
}

5:防止手势在不动的情况下被检测到碰到了物体可以在FixUpdate和Update中分别得到手势的坐标,当两个值在比较近的范围内视为没有动,值越大则在运动,可能有更好的方法判断是否在挥手,只是我没找到,反正我用自己的这个方法还行,至少达到我想要的效果;

	Vector3 updatehandPos;
	Vector3 fixupdatehandPos;
	private KinectInterop.JointType handRightType = KinectInterop.JointType.HandRight;
	void FixedUpdate()
	{
		fixupdatehandPos = GetVecDis();
	}
	void Update()
	{
		updatehandPos = GetVecDis();
		if (Vector3.Distance(updatehandPos, fixupdatehandPos) > 0.05f)
			print("手在动");
		else
			print("手未动");
	 

	} 
	private Vector3 GetVecDis()
	{
		if (KinectManager.Instance != null && KinectManager.Instance.IsInitialized())
		{
			if (KinectManager.Instance.IsUserDetected())
			{
				long userID = KinectManager.Instance.GetPrimaryUserID();
				if (GameManager.instance.kinectUserIDs != null && GameManager.instance.kinectUserIDs.Count > 0)
				{
					userID = GameManager.instance.kinectUserIDs[0];
				}
				else
				{
					userID = KinectManager.Instance.GetPrimaryUserID();
				}
			 
				if (KinectManager.Instance.IsJointTracked(userID, (int)handRightType))
				{
					//得到手势坐标
					var handPos = KinectManager.Instance.GetJointKinectPosition(userID, (int)handRightType);
					return handPos;
			  
				}
			}
		}
		return Vector3.zero;
	}

6:KinectSDK核心的代码都在KinectManager中,可以具体详看,这里不做解析,再看下实例话的代码如下,比较粗糙,能达到效果就行。。。。

using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
using UnityEngine.Diagnostics;

public class GameManager : MonoBehaviour
{ 
     
    public GameObject handImage; 
    /// <summary>
    /// 获得的积分
    /// </summary>
    [HideInInspector]
    public float mGetScore { get; set; }
    /// <summary>
    /// 毒品元素实例化对象的父级
    /// </summary>
    public GameObject SpawnManager; 
    //画板边界定义
    private int maxX = 800;
    private int minX = -800;
    private int minY = -650;
    //定义水果上抛的力
    private int forceX = 8;
    private int forceY = 15;
    int fruitCount ;
    /// <summary>
    /// [Header("炸弹的预设")]
    /// </summary>
    [Header("炸弹的预设")]
    public GameObject bomb; 
    /// <summary>
    /// 产生时间
    /// </summary>
    float spawnerTimer = ItemConfig.PRODUCEITEM_TIME;
    bool isPlaying = true;

    #region 单例以及游戏资源加载变量
    /// <summary>
    /// 单例对象
    /// </summary> 
    public static GameManager instance = null;
    
    /// <summary>
    /// 读取游戏状态
    /// </summary>
    [HideInInspector]
    public E_GameState mGameState;
    [HideInInspector]
    public List<DataModen> datasModen = null;
    [HideInInspector]
    public bool isHandMoveCut = false;
    [HideInInspector]
    public GameObject panelStar = null;
    #endregion
    private void Awake()
    {
        instance = this;
    }
    // Start is called before the first frame update
    void Start()
    {
        fruitCount = Random.Range(1, 2);
        Screen.SetResolution(1920,1080,true);
        MusicMgr.Instance.PlayBkMusic("待机");
        InitResetGame(true);
        if (panelStar==null)
            panelStar = Instantiate(Resources.Load<GameObject>("Prefabs/PanelStart"), SpawnManager.transform);
        if (!System.IO.Directory.Exists(ItemConfig.LoadPath))
            System.IO.Directory.CreateDirectory(ItemConfig.LoadPath);
        mGameState = E_GameState.ready; 
        datasModen = UtilsJson<DataModen>.UseNetFrameReadJsonConfigFiles(ItemConfig.LoadPath, "ItemData.json");
        if (datasModen == null || datasModen.Count <= 0)
            print("MJ   =======>  对象为空或者对象数量为0");
        var timer = UtilsJson<GameDataTimer>.ReadJsonConfigFile(ItemConfig.LoadPath, "GameTimer.json");
#if UNITY_EDITOR
        ItemConfig.MAX_GAMETIME = 10;
#elif UNITY_STANDALONE_WIN
        ItemConfig.MAX_GAMETIME = timer.mTimer;
#endif
        ItemConfig.COUNTDOWNTIME = timer.mCountdown;
        Invoke("SetFruitCout",5f);
    }
    public void InitResetGame(bool isActive) 
    { 
        handImage.SetActive(isActive); 
        mGameState = isActive ? E_GameState.ready : E_GameState.inGame; 
    }
    public void SetFruitCout()
    {
        fruitCount = Random.Range(1,5);
    }
    /// <summary>
    /// 产生水果 炸弹 ,也能控制销毁
    /// </summary>
    private void OnSpwaner(bool isFruit)
    {
        //播放音乐
        // audioSource.Play(); 

        //实例化水果
        int fruitIndex = Random.Range(0, datasModen.Count);
        GameObject go;

        int fruitX = Random.Range(minX, maxX);
        go = Instantiate(bomb, new Vector3(fruitX, minY, 0), bomb.transform.rotation);
        go.transform.SetParent(SpawnManager.transform);
        //14是炸弹   
        int fIndex = isFruit ? Random.Range(0, 14) :14;
        go.GetComponent<FruitItem>().SetType(fIndex);
        RectTransform fruitRT = go.transform as RectTransform;
        fruitRT.anchoredPosition3D = new Vector3(0, 0, 0);
        fruitRT.anchoredPosition = new Vector2(fruitX, minY);
        fruitRT.localScale = new Vector3(1, 1, 1);
 
        //水果速度
        // Vector3 velocity = new Vector3(-x * Random.Range(0.2f, 0.5f), -Physics.gravity.y * Random.Range(0.8f, 1.5f), 0);
        Rigidbody2D rigidbody = go.transform.GetComponent<Rigidbody2D>();
        float xxx = fruitX > 0 ? -Random.Range(1, forceX) : Random.Range(1, forceX);
        rigidbody.velocity = new Vector2(xxx, Random.Range(forceY -3, forceY));

    }

    public List<long> kinectUserIDs;
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Application.Quit();
        }
        try
        {
            if (mGetScore <= 0)
            {
                mGetScore = 0;
            }
            if (KinectManager.Instance.IsInitialized())
            {
                kinectUserIDs = KinectManager.Instance.GetAllUserIds();
                for (int i = 0; i < kinectUserIDs.Count; i++)
                {
                    print("当前用户列表=;;;;;" + kinectUserIDs[i]);
                }
            }
            if (mGameState == E_GameState.inGame)
            {
                if (!isPlaying)
                {
                    return;
                }

                spawnerTimer -= Time.deltaTime;
                if (0 >= spawnerTimer)
                {
                    //到时间就开始产生水果,产生多个水果
                 
                    for (int i = 0; i < fruitCount; i++)
                        OnSpwaner(true);
                    spawnerTimer = ItemConfig.PRODUCEITEM_TIME;

                    //随机产生炸弹
                    int bombNum = Random.Range(0, 100);
                    if (bombNum > 70)
                    {
                        OnSpwaner(false);
                    }
                }
            }
        }
        catch (System.Exception)
        {
 
        }
    }
}

7:sdk中手势识别有个枚举类型,定义了相当多的手势:

public enum Gestures
{
	None = 0,
	RaiseRightHand,//右手举起过肩并保持至少一秒
	RaiseLeftHand,//左手举起过肩并保持至少一秒
	Psi,//双手举起过肩并保持至少一秒
	Tpose,//触摸
	Stop,//双手下垂
	Wave,//左手或右手举起来回摆动
	Click,//左手或右手在适当的位置停留至少2.5秒
	SwipeLeft,//右手向左挥
	SwipeRight,//左手向右挥
	SwipeUp,//左手或者右手向上挥
	SwipeDown,//左手或者右手向下挥
	RightHandCursor,//假手势,用来使光标随着手移动
	LeftHandCursor,//假手势,用来使光标随着手移动
	ZoomIn,//手肘向下,两手掌相聚至少0.7米,然后慢慢合在一起
	ZoomOut,//手肘向下,左右手掌合在一起(求佛的手势),然后慢慢分开
	Wheel,//想象一下你双手握着方向盘,然后左右转动
	Jump,//在1.5秒内髋关节中心至少上升10厘米 (跳)
	Squat,//在1.5秒内髋关节中心至少下降10厘米 (下蹲)
	Push,//在1.5秒内将左手或右手向外推
	Pull,//在1.5秒内将左手或右手向里拉
	ShoulderLeftFront,//左肩前倾
	ShoulderRightFront,//右肩前倾
	LeanLeft, //身体向左倾斜
	LeanRight, //身体向右倾斜
	LeanForward,//身体向前倾斜
	LeanBack,//身体向后倾斜
	KickLeft,//踢左脚
	KickRight,//踢右脚
	Run,//跑
	RaisedRightHorizontalLeftHand,//左手平举
	RaisedLeftHorizontalRightHand,//右手平举
	
	//自定义手势
	UserGesture1 = 101,
	UserGesture2 = 102,
	UserGesture3 = 103,
	UserGesture4 = 104,
	UserGesture5 = 105,
	UserGesture6 = 106,
	UserGesture7 = 107,
	UserGesture8 = 108,
	UserGesture9 = 109,
	UserGesture10 = 110,
}

8:打包注意事项,工程中测试没问题,打包出来不行,可能原因有很多,首先检查下打包后的目录与运行程序同级的目录中是否导入了Kinect相关的Dll文件,这些需要手动导入:文件在工程目录下,Assets的同级目录中可以找到。
在这里插入图片描述
9:体感设备的KinectSDK(非Unity)出现如下错误的解决方案:查看服务项是否开启,或查看电源以及线路是否正常;多次查到该问题的原因基本都是这两个问题。
在这里插入图片描述
最后该作品已经部署到湖南岳阳禁毒博物馆中,体感切水果互动项目,有想法的可以去体验体验,觉得有用的点个赞再走,有宝贵意见的欢迎大家留言提示,这里会针对性的进行更改并更新,感谢!!!
在这里插入图片描述

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有点朦

您的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值