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;
}
}