前几天环境配好了,连上Kinect能跑示例项目KinectView。花了很大的力气找插件的API手册,原来插件并没有官方手册,目前官网也只有两个示例项目。于是自己看代码,想首先学会怎样写一个手势触发特定事件。主要看了BodySourceManager.cs和BodySourceView.cs,中午成功写出了如果左手高过头顶,自动截屏保存图片。
思路是if判断,如果左手的y坐标值大于等于头顶的y坐标值条件为真,触发事件,调用Unity的一个API叫Application.Capturescreenshot()。在BodySourceView.cs中的RefreshBodyObject()函数中修改,这个函数会在Update()中调用,每一帧都会更新。红框部分是自己添加的代码,其余均为无改动的示例源码。
Kinect.JoinType可以按F12查看插件中的源代码,可知是枚举类型,如图
简要分析一下示例程序中的RefreshBodyObejct函数。这个函数主要做的是获取关节点坐标,在Unity中画出人体骨骼。画骨骼直接用Unity的线渲染LineRender(在前面代码中已经调用它的SetVertexCount方法设置画得线有两个端点)。遍历Dictionary类型的_Bonemap(保存的是Key-Value键值对,Value是Key的下一个关联结点),key就是画线段的源位置,value就是线段的目标位置。
完整代码如下
BodySourceManager.cs
using UnityEngine;
using System.Collections;
using Windows.Kinect;
public class BodySourceManager : MonoBehaviour
{
private KinectSensor _Sensor;
private BodyFrameReader _Reader;
private Body[] _Data = null;
public Body[] GetData()
{
return _Data;
}
void Start ()
{
_Sensor = KinectSensor.GetDefault();
if (_Sensor != null)
{
_Reader = _Sensor.BodyFrameSource.OpenReader();
if (!_Sensor.IsOpen)
{
_Sensor.Open();
}
}
}
void Update ()
{
if (_Reader != null)
{
var frame = _Reader.AcquireLatestFrame();
if (frame != null)
{
if (_Data == null)
{
_Data = new Body[_Sensor.BodyFrameSource.BodyCount];
}
frame.GetAndRefreshBodyData(_Data);
frame.Dispose();
frame = null;
}
}
}
void OnApplicationQuit()
{
if (_Reader != null)
{
_Reader.Dispose();
_Reader = null;
}
if (_Sensor != null)
{
if (_Sensor.IsOpen)
{
_Sensor.Close();
}
_Sensor = null;
}
}
}
BodySourceView.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Kinect = Windows.Kinect;
public class BodySourceView : MonoBehaviour
{
public Material BoneMaterial;
public GameObject BodySourceManager;
private Dictionary _Bodies = new Dictionary();
private BodySourceManager _BodyManager;
private Dictionary _BoneMap = new Dictionary()
{
{ Kinect.JointType.FootLeft, Kinect.JointType.AnkleLeft },
{ Kinect.JointType.AnkleLeft, Kinect.JointType.KneeLeft },
{ Kinect.JointType.KneeLeft, Kinect.JointType.HipLeft },
{ Kinect.JointType.HipLeft, Kinect.JointType.SpineBase },
{ Kinect.JointType.FootRight, Kinect.JointType.AnkleRight },
{ Kinect.JointType.AnkleRight, Kinect.JointType.KneeRight },
{ Kinect.JointType.KneeRight, Kinect.JointType.HipRight },
{ Kinect.JointType.HipRight, Kinect.JointType.SpineBase },
{ Kinect.JointType.HandTipLeft, Kinect.JointType.HandLeft },
{ Kinect.JointType.ThumbLeft, Kinect.JointType.HandLeft },
{ Kinect.JointType.HandLeft, Kinect.JointType.WristLeft },
{ Kinect.JointType.WristLeft, Kinect.JointType.ElbowLeft },
{ Kinect.JointType.ElbowLeft, Kinect.JointType.ShoulderLeft },
{ Kinect.JointType.ShoulderLeft, Kinect.JointType.SpineShoulder },
{ Kinect.JointType.HandTipRight, Kinect.JointType.HandRight },
{ Kinect.JointType.ThumbRight, Kinect.JointType.HandRight },
{ Kinect.JointType.HandRight, Kinect.JointType.WristRight },
{ Kinect.JointType.WristRight, Kinect.JointType.ElbowRight },
{ Kinect.JointType.ElbowRight, Kinect.JointType.ShoulderRight },
{ Kinect.JointType.ShoulderRight, Kinect.JointType.SpineShoulder },
{ Kinect.JointType.SpineBase, Kinect.JointType.SpineMid },
{ Kinect.JointType.SpineMid, Kinect.JointType.SpineShoulder },
{ Kinect.JointType.SpineShoulder, Kinect.JointType.Neck },
{ Kinect.JointType.Neck, Kinect.JointType.Head },
};
void Update ()
{
if (BodySourceManager == null)
{
return;
}
_BodyManager = BodySourceManager.GetComponent();
if (_BodyManager == null)
{
return;
}
Kinect.Body[] data = _BodyManager.GetData();//GetData();
if (data == null)
{
return;
}
List trackedIds = new List();
foreach(var body in data)
{
if (body == null)
{
continue;
}
if(body.IsTracked)
{
trackedIds.Add (body.TrackingId);
}
}
List knownIds = new List(_Bodies.Keys);
// First delete untracked bodies
foreach(ulong trackingId in knownIds)
{
if(!trackedIds.Contains(trackingId))//List方法测试一个元素是否在List内
{
Destroy(_Bodies[trackingId]);//先Destroy掉GameObject
_Bodies.Remove(trackingId);//再将键值对从_Bodies字典中移除
}
}
foreach(var body in data)
{
if (body == null)
{
continue;
}
if(body.IsTracked)
{
if(!_Bodies.ContainsKey(body.TrackingId))
{
_Bodies[body.TrackingId] = CreateBodyObject(body.TrackingId);
}
RefreshBodyObject(body, _Bodies[body.TrackingId]);
}
}
}
private GameObject CreateBodyObject(ulong id)
{
GameObject body = new GameObject("Body:" + id);
for (Kinect.JointType jt = Kinect.JointType.SpineBase; jt <= Kinect.JointType.ThumbRight; jt++)
{
GameObject jointObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
LineRenderer lr = jointObj.AddComponent();
lr.SetVertexCount(2);//设置线段数
lr.material = BoneMaterial;//物体的材质
lr.SetWidth(0.05f, 0.05f);//设置线的开始和结束宽度
jointObj.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
jointObj.name = jt.ToString();
jointObj.transform.parent = body.transform;
}
return body;
}
private void RefreshBodyObject(Kinect.Body body, GameObject bodyObject)
{
for (Kinect.JointType jt = Kinect.JointType.SpineBase; jt <= Kinect.JointType.ThumbRight; jt++)
{
Kinect.Joint sourceJoint = body.Joints[jt];
Kinect.Joint? targetJoint = null;
if(_BoneMap.ContainsKey(jt))
{
targetJoint = body.Joints[_BoneMap[jt]];//所以之前字典中设置就是一个一个关节连起来
}
Transform jointObj = bodyObject.transform.FindChild(jt.ToString());
jointObj.localPosition = GetVector3FromJoint(sourceJoint);
LineRenderer lr = jointObj.GetComponent();
if(targetJoint.HasValue)
{
//Debug.Log (jointObj.localPosition);ok
lr.SetPosition(0, jointObj.localPosition);
lr.SetPosition(1, GetVector3FromJoint(targetJoint.Value));
lr.SetColors(GetColorForState (sourceJoint.TrackingState), GetColorForState(targetJoint.Value.TrackingState));
}
else
{
lr.enabled = false;
}
if(body.Joints[Kinect.JointType.HandLeft].Position.Y>=body.Joints[Kinect.JointType.Head].Position.Y){
Debug.Log("handleft:"+body.Joints[Kinect.JointType.HandLeft].Position.Y);
Debug.Log("head:"+body.Joints[Kinect.JointType.Head].Position.Y);
Debug.Log("My LeftHand is Higher than head!~");
Application.CaptureScreenshot ("Screenshoot.png", 0);
}
}
}
private static Color GetColorForState(Kinect.TrackingState state)
{
switch (state)
{
case Kinect.TrackingState.Tracked:
return Color.green;
case Kinect.TrackingState.Inferred:
return Color.red;
default:
return Color.black;
}
}
private static Vector3 GetVector3FromJoint(Kinect.Joint joint)
{
return new Vector3(joint.Position.X * 10, joint.Position.Y * 10, joint.Position.Z * 10);
}
}
自己琢磨代码还是很开心的,代码值得完整的分析在此不赘述了。写博文是有点折腾,但是今天看到别人的技术博客和心得分享真的是很励志(http://www.the5fire.com/ http://blog.csdn.net/the_fire?viewmode=list)。