在Unity中使用Kinect移动、举手,固定区域识别

发布于:2025-06-26 ⋅ 阅读:(23) ⋅ 点赞:(0)

1、原始KinectManager中有举手、大字、蹲下等动作,但是不能获取移动、或站在固定区域识别用户,所以还得自己实现

2、导入驱动K2Examples

3、实现

        首先就得排除KinectManager,得自己拿到用户的ID,先写区域识别用户,比如我们可以定制一块距离相机1米、最远距离2米、左右最远距离是0.3,那么站在相机前0.5米是不会识别的

新建脚本PlayerMove.cs

public class PlayerMove : MonoBehaviour
{
    [SerializeField] private long currentUser = 0;
    public float aroundDistanceMin;
    public float aroundDistanceMax;
    private const int headID = (int)KinectInterop.JointType.Head;
    public float leftRightDistanceMin;
    public float rightRightDistanceMax;

    void Update()
    {
        FindUser();
        if (currentUser > 0)
        {
            Debug.Log("有用户进入");
        }
        else
        {
            Debug.Log("无用户");
        }
    }
    private void FindUser()
    {
        var kinectManager = KinectManager.Instance;
        currentUser = 0;
        Vector3 v3 = Vector3.zero;
        var min = aroundDistanceMin;
        var max = aroundDistanceMax;
        foreach (var item in kinectManager.GetAllUserIds())
        {
            if (kinectManager.GetJointTrackingState(item, headID) == KinectInterop.TrackingState.Tracked)
            {
                var pos = kinectManager.GetUserPosition(item);
                var z = kinectManager.GetUserPosition(item).z;
                var x = kinectManager.GetUserPosition(item).x;
                if (z < max && z > min && x < rightRightDistanceMax && x > leftRightDistanceMin)
                {
                    if (v3 == Vector3.zero || pos.z < v3.z)
                    {
                        v3 = pos;
                        currentUser = item;
                    }
                }
            }
        }
    }
}

currentUser:用户在KinectManager中分配的ID

aroundDistanceMin:Z轴最小值

aroundDistanceMax:Z轴最大值

headID:Kinect的动作枚举

leftRightDistanceMin:左边的最大距离

rightRightDistanceMax:右边的最大距离

 注意,获取用户必须在Update中更新,否则更新不及时

经过测试后可以打印,而且识别准确,我们继续把举手、移动加入,这个得写进

if (currentUser > 0)中

先写举手,举手得获取手得位置,判断手是否举过肩头,这样就算完成举手动作了,我们写成方法返回bool就行

/// <summary>
/// 计算手是否举起
/// </summary>
/// <returns></returns>
private bool CalculateRaiseHand(int handId)
{
    var kinectManager = KinectManager.Instance;
    var shoulderWidth = (kinectManager.GetJointPosition(currentUser, shoulderLeft) - kinectManager.GetJointPosition(currentUser, shoulderRight)).magnitude;
    var y = kinectManager.GetJointPosition(currentUser, headID).y + shoulderWidth * 0.1f;
    return kinectManager.GetJointPosition(currentUser, handId).y > y;
}

传入的int handId 是手的枚举,不过单纯返回bool没用,因为当用户一直举手,那我们就会一直触发,非常麻烦,所以还得加入时间,把举手时长给出去,这样才是能用的,而且非常适合做填充小人

[Serializable] public class FloatArgTypeEvent : UnityEvent<float> { }
private const int rightHandID = (int)KinectInterop.JointType.HandRight;
[Header("举起右手事件")] public FloatArgTypeEvent raiseRightHand;//参数:举起手的时间
[SerializeField] private float raiseRightHandTIme;
[SerializeField] private bool rightSign;
private void MatchRaiseHand()
{
    if (CalculateRaiseHand(rightHandID))
    {
        if (rightSign == false)
            raiseRightHand.Invoke(raiseRightHandTIme += Time.deltaTime);
    }
    else
    {
        rightSign = false;
        raiseRightHandTIme = 0;
    }
}

我们新建一个UnityEvent,把举手的时长传出去然后在Update调用,完整代码:

using System;
using UnityEngine;
using UnityEngine.Events;
[Serializable] public class FloatArgTypeEvent : UnityEvent<float> { }
public class PlayerMove : MonoBehaviour
{
    [SerializeField] private long currentUser = 0;
    public float aroundDistanceMin;
    public float aroundDistanceMax;
    private const int headID = (int)KinectInterop.JointType.Head;
    public float leftRightDistanceMin;
    public float rightRightDistanceMax;
    [SerializeField] private bool rightSign;
    private const int rightHandID = (int)KinectInterop.JointType.HandRight;
    [Header("举起右手事件")] public FloatArgTypeEvent raiseRightHand;//参数:举起手的时间
    [SerializeField] private float raiseRightHandTIme;
    void Update()
    {
        FindUser();
        if (currentUser > 0)
        {
            MatchRaiseHand();
            Debug.Log("有用户进入");
        }
        else
        {
            Debug.Log("无用户");
        }
    }
    private void FindUser()
    {
        var kinectManager = KinectManager.Instance;
        currentUser = 0;
        Vector3 v3 = Vector3.zero;
        var min = aroundDistanceMin;
        var max = aroundDistanceMax;
        foreach (var item in kinectManager.GetAllUserIds())
        {
            if (kinectManager.GetJointTrackingState(item, headID) == KinectInterop.TrackingState.Tracked)
            {
                var pos = kinectManager.GetUserPosition(item);
                var z = kinectManager.GetUserPosition(item).z;
                var x = kinectManager.GetUserPosition(item).x;
                if (z < max && z > min && x < rightRightDistanceMax && x > leftRightDistanceMin)
                {
                    if (v3 == Vector3.zero || pos.z < v3.z)
                    {
                        v3 = pos;
                        currentUser = item;
                    }
                }
            }
        }
    }
    private void MatchRaiseHand()
    {
        if (CalculateRaiseHand(rightHandID))
        {
            if (rightSign == false)
                raiseRightHand.Invoke(raiseRightHandTIme += Time.deltaTime);
        }
        else
        {
            rightSign = false;
            raiseRightHandTIme = 0;
        }
    }
    /// <summary>
    /// 计算手是否举起
    /// </summary>
    /// <returns></returns>
    private bool CalculateRaiseHand(int handId)
    {
        var kinectManager = KinectManager.Instance;
        var shoulderWidth = (kinectManager.GetJointPosition(currentUser, shoulderLeft) - kinectManager.GetJointPosition(currentUser, shoulderRight)).magnitude;
        var y = kinectManager.GetJointPosition(currentUser, headID).y + shoulderWidth * 0.1f;
        return kinectManager.GetJointPosition(currentUser, handId).y > y;
    }
}

到这里就完成了区域识别用户、举手,我们还差一个位移,位移就相对简单了,获取用户的头或者脖子或者肩的中央位置,然后根据位置移动就行

int jointType = (int)KinectInterop.JointType.SpineShoulder;
if (KinectManager.Instance.IsJointTracked(currentUser, jointType))
{
    Vector3 rightHandpos = KinectManager.Instance.GetJointKinectPosition(currentUser, jointType);
    var pos = player.transform.localPosition;
    var x = Mathf.Lerp(worldCornersXMin, worldCornersXMax, Mathf.InverseLerp(camleftRihtDistanceMin, camleftRightDistanceMax, rightHandpos.x));
    pos.x = x;
    player.transform.localPosition = Vector3.Lerp(player.transform.localPosition, pos, Time.deltaTime * moveSpeed);
}

我这里是获取用户的两肩中央位置,这样精确一些然后用插值去位移,下面是完整代码

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
[Serializable] public class FloatArgTypeEvent : UnityEvent<float> { }
public class PlayerMove : MonoBehaviour
{
    [SerializeField] private long currentUser = 0;
    public float aroundDistanceMin;
    public float aroundDistanceMax;
    public float leftRightDistanceMin;
    public float rightRightDistanceMax;
    private const int headID = (int)KinectInterop.JointType.Head;
    public float moveSpeed;
    public float camleftRihtDistanceMin;
    public float camleftRightDistanceMax;
    public Transform player;
    public RawImage UserTexture;
    public bool isPlaying;
    public float worldCornersXMin;
    public float worldCornersXMax;
    private const int shoulderLeft = (int)KinectInterop.JointType.ShoulderLeft;
    private const int shoulderRight = (int)KinectInterop.JointType.ShoulderRight;
    private const int rightHandID = (int)KinectInterop.JointType.HandRight;
    [SerializeField] private bool rightSign;

    [Header("举起右手事件")] public FloatArgTypeEvent raiseRightHand;//参数:举起手的时间
    [SerializeField] private float raiseRightHandTIme;
    void Update()
    {
        FindUser();
        if (currentUser > 0)
        {
            MatchRaiseHand();
            int jointType = (int)KinectInterop.JointType.SpineShoulder;
            if (KinectManager.Instance.IsJointTracked(currentUser, jointType))
            {
                Vector3 rightHandpos = KinectManager.Instance.GetJointKinectPosition(currentUser, jointType);
                var pos = player.transform.localPosition;
                var x = Mathf.Lerp(worldCornersXMin, worldCornersXMax, Mathf.InverseLerp(camleftRihtDistanceMin, camleftRightDistanceMax, rightHandpos.x));
                pos.x = x;
                player.transform.localPosition = Vector3.Lerp(player.transform.localPosition, pos, Time.deltaTime * moveSpeed);
            }
        }
        else
        {
            var horizontal = Input.GetAxis("Horizontal");
            if (horizontal != 0 && isPlaying)
            {
                var pos = player.transform.localPosition;
                pos += horizontal * 2f * Time.deltaTime * Vector3.right;
                pos.x = Mathf.Clamp(pos.x, worldCornersXMin, worldCornersXMax);
                player.transform.localPosition = new Vector3(Mathf.MoveTowards(player.transform.localPosition.x, pos.x, 80 * Time.deltaTime), player.transform.localPosition.y, player.transform.localPosition.z);
            }
        }
    }
    private void FindUser()
    {
        var kinectManager = KinectManager.Instance;
        currentUser = 0;
        Vector3 v3 = Vector3.zero;
        var min = aroundDistanceMin;
        var max = aroundDistanceMax;
        foreach (var item in kinectManager.GetAllUserIds())
        {
            if (kinectManager.GetJointTrackingState(item, headID) == KinectInterop.TrackingState.Tracked)
            {
                var pos = kinectManager.GetUserPosition(item);
                var z = kinectManager.GetUserPosition(item).z;
                var x = kinectManager.GetUserPosition(item).x;
                if (z < max && z > min && x < rightRightDistanceMax && x > leftRightDistanceMin)
                {
                    if (v3 == Vector3.zero || pos.z < v3.z)
                    {
                        v3 = pos;
                        currentUser = item;
                    }
                }
            }
        }
    }
    private void MatchRaiseHand()
    {
        if (CalculateRaiseHand(rightHandID))
        {
            if (rightSign == false)
                raiseRightHand.Invoke(raiseRightHandTIme += Time.deltaTime);
        }
        else
        {
            rightSign = false;
            raiseRightHandTIme = 0;
        }
    }
    /// <summary>
    /// 计算手是否举起
    /// </summary>
    /// <returns></returns>
    private bool CalculateRaiseHand(int handId)
    {
        var kinectManager = KinectManager.Instance;
        var shoulderWidth = (kinectManager.GetJointPosition(currentUser, shoulderLeft) - kinectManager.GetJointPosition(currentUser, shoulderRight)).magnitude;
        var y = kinectManager.GetJointPosition(currentUser, headID).y + shoulderWidth * 0.1f;
        return kinectManager.GetJointPosition(currentUser, handId).y > y;
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到