前两天刚开始接触Unity3D,看了个简单的教程后,想尝试做点东西,然而却苦于自己不会制作素材,刚接触没几天,也不知道什么地方的素材比较好用。随便逛了下论坛,突然间发现了http://unity3d.com/support/resources/files/CarTutorial.zip官方的这个资源,觉得素材非常不错,倍感兴奋;不过,个人感觉是开起来难度非常大~~~~~冬天比较冷,于是想用Kinect连上去试一试,来锻炼锻炼身体,无论如何,先从最简单的做起,今天顺便做出第一篇个人的KinectU3D由浅入深教程并且提供自己制作的开发中间件。大家注意,我是将数据传输到Unity3D的C#脚本内部来处理的,而不是通过键盘模拟形式,也许有人会问我为啥不用键盘模拟模拟,就这个游戏而言,的确不会有啥区别。吾曰:我做这个整合不是仅仅为了这个游戏,例如,以后如果有更加复杂的项目,例如市面上喊得比较火的试衣镜,那你怎么用键盘模拟?(http://v.youku.com/v_show/id_XMjU4MjExNjgw.html )最后,连接成功,自定义了四种简单的姿势,前倾后仰左倾右倾分别来代替前后左右的方向控制,并且,注意玩的时候要举起双手到胸前,基本上就是下面图中的样子(有个镜面问题,看到左上角从kinect传输过来的RGB流,左右刚好相反)
连玩之后,先说明这么几个问题:
(1)本来是想把Camera镜头放到车里面的驾驶座那儿,这样子会更加身临其境一些,但是,这辆车里面实在太丑了,于是,就不改变视角了;
(2)还是那个问题,本身这个游戏键盘控制起来都不算舒服,连上kinect后开起来的难度会比原来的还大一点~当然,最重要的不是为了让大家玩好这个游戏,而是希望大家能通过这个例子,自己好好研究研究,能做出其他更好的kinect、Unity3D游戏;
(3)我的电脑CPU很低,延时大一点,如果电脑不好的话,建立把左上角的视频呈现(RGB)关掉,等会儿说。
(4)我的kinect还是旧版的kinect xbox 360 ,目前无法开启near mode,也就是近端模式(坐在电脑前也能使用,但是识别的是上半身)。
(5)要是试过碰到什么不好的地方请留言提意见;碰到bug的话,更要及时回复我。
下面就教大家来一步步搞完这个东西:
一、写出一个Unity和Kinect数据通信的中间件,主要是把kinect端的骨骼点数据(人的20个骨骼点在空间的三维坐标)和RGB流接收过来,骨骼点数据如下图所示:
如果你不会写,那直接用我自己写的中间件吧,这篇文章是以我自己写的中间件为基础的。此外,在此推荐一篇文章:http://unity3d.9ria.com/?p=1764。推荐大家使用卡耐基梅隆那个kinectWrapper,上去wiki看看使用法。这玩意儿功能还凑合,但是,没有kinect基础的人很难用得了。如果追求傻瓜式开发法,毕竟在天地会开贴要考虑到大部分只会Unity3D,不懂kinect的朋友,那么,就用我写的东西,等给大家介绍傻瓜式应用法。目前比较简单,只加入了用到的东西,只有彩色数据和骨骼数据(分别对应视频块和20个骨骼三维坐标点),特别是骨骼点数据,千万不要小看他,大部分的动作姿势都是依靠这些点的运动规律再结合计时器来完成的,像KTU一样绑定一个人体模型需要大家自己去动下脑子,做下坐标换算,目前读取出来的坐标是最原始的kinect坐标,原始的坐标比较容易被理解,也方便于大家自我改造。以下是坐标范围的官方解释:
The skeleton(骨骼) position data ranges are:
Values of x range from approximately -2.2 to +2.2.
//x是平行于kinect的方向,以人正对于kinect摄像头前方而言,正//中间是0,人的左边是-2.2~0,人的右边是0~2.2,单位是米。
Values of y range from approximately -1.6 to +1.6.
//y向上,越上越大
Values of z range from 0.0 to 4.0.
//剩下的一维从摄像头到人的直线方向就是z,离得越远越大。
之后会逐步添加新的功能进去,并且及时发布傻瓜式使用法,只要别太过于心浮气躁就能使用。
二、下面代码加文字说明,我的这个简单的wrapper的大体意思,要么暂且称它为EggWrapper。
打开EggWrapper.cs,先看最下面,这些方法是最核心的地方,分别对应着KUInteractive.dll中的方法,调用dll的方法,完成对kinect的操作:
然后依次从上往下看,介绍下主要属性和方法:
VideoWidth 、VideoHeight :Kinect端RGB彩色数据分辨率。
UpdateRGB:动态更新RGB数据,为了实时获得最新数据,调用时将该方法置入update函数中,处理后返回值是Color[],像素块数据。
updateSkeletons:动态更新骨骼三维数据,为了实时获得最新数据,调用时将该方法置入update函数中
hasSkeleton:判断是否已经识别人体骨骼
getKinectTime:获得时间,单位是毫秒。(用时间来结合骨骼坐标来做人体姿态运算)
GetSkeletonPosition:获取骨骼坐标,传入参数为枚举类型Joints中的元素,依次对应相应的人体骨骼点。
三、最后,代码加文字加赛车实践说明具体使用法。
(1)无论如何,请先安装kinect sdk1.5(我用的是1.5,暂且不确定就这个玩意儿而言1.6行不行),安装完之后,插上 kinect usb,自动在线安装kinect 驱动,xp系统印象里是不行的。
(2)解压EggWrapper,将文件夹”kinect”复制到Assets下面
(3)贴上主文件代码解释
- using System;
- using UnityEngine;
- using System.Runtime.InteropServices;
- using System.Collections;
- using System.Text;
- using System.Reflection;
- public class KinectAction: MonoBehaviour
- {
- //需要哪些骨骼点,就定义哪些骨骼点
- Vector3 hipCenter=new Vector3(0,0,0);
- Vector3 handRight=new Vector3(0,0,0);
- Vector3 kneeRight=new Vector3(0,0,0);
- Vector3 kneeLeft=new Vector3(0,0,0);
- Vector3 spine=new Vector3(0,0,0);
- Vector3 shoulderRight=new Vector3(0,0,0);
- Vector3 shoulderLeft=new Vector3(0,0,0);
- Vector3 head=new Vector3(0,0,0);
- Vector3 handLeft=new Vector3(0,0,0);
- //是否开启视频流
- private bool useRGB =true;
-
- //定义二维纹理,用以绘制
- private Texture2D texture;
-
- public bool flg;
- float RSpeed=3;
- float LSpeed=-3;
- float FSpeed=10;
- float BSpeed=-10;
-
-
- float vs;
- float hs;
-
- private void Start()
- {
- //启动kinect
- int status=EggWrapper.NuiContextInit();
-
- if(status!=6){
- print("kinect init error.............................");
- return;
- }
- //因为姿势在上半身,稍微提高一下kinect摄像头的角度
- EggWrapper.SetCameraAngle(12);
-
- if (useRGB) {
- texture=new Texture2D(EggWrapper.VideoWidth,EggWrapper.VideoHeight);
- }
-
-
- }
-
- private void OnApplicationQuit(){
- EggWrapper.NuiContextUnInit();
- }
-
-
-
-
- private void Update() {
- //捕捉动作
- updateSkeleton();
- //如果开启RGB数据显示,那么会在左上角有块视频流显示区域
- if (useRGB) {
- getVideo();
- }
-
- }
-
- //实时接收Color[] 数据块,并且设置texture
- private void getVideo(){
- Color32[] cols;
- cols=EggWrapper.UpdateRGB();
- texture.SetPixels32(cols);
- texture.Apply();
- }
-
- //绘制视频流,注意EggWrapper.VideoWidth / 3, EggWrapper.VideoHeight / 3是将640*480的大小缩小3倍
- private void OnGUI() {
- if (useRGB ) {
- GUI.DrawTexture(new Rect(0, 0, EggWrapper.VideoWidth / 3, EggWrapper.VideoHeight / 3), texture, ScaleMode.ScaleToFit, false);
- }
- }
-
-
- private void updateSkeleton()
- {
- EggWrapper.updateSkeletons();
- if(EggWrapper.hasSkeleton()){
- setPositions();
- //此处缩小下捕捉区域的范围,大家可以自定义
- if( hipCenter.x>-0.5 && hipCenter.x<0.5){
- doActions();
- }
- }
-
- }
-
- //获得骨骼点的三维坐标
- private void setPositions(){
- hipCenter=EggWrapper.GetSkeletonPosition(EggWrapper.Joints.HIP_CENTER);
- handRight=EggWrapper.GetSkeletonPosition(EggWrapper.Joints.HAND_RIGHT);
- handLeft=EggWrapper.GetSkeletonPosition(EggWrapper.Joints.HAND_LEFT);
- spine=EggWrapper.GetSkeletonPosition(EggWrapper.Joints.SPINE);
- head=EggWrapper.GetSkeletonPosition(EggWrapper.Joints.HEAD);
- }
-
- private void doActions(){
- hs=0;
- vs=0;
- //举起双手到胸前
- if(handLeft.y>spine.y-0.1&&handRight.y>spine.y-0.1){
- flg=true;
- }else{
- flg=false;
- }
-
- //以下定义四个比较简单的肢体动作,分别是左倾右倾前倾后仰
- if(flg){
- if(head.x
- //print("left............................");
- hs=Time.deltaTime*LSpeed;
- }
- if(head.x>spine.x+0.2){
- //print("right............................");
- hs=Time.deltaTime*RSpeed;
- }
- if(head.z
- //print("front............................");
- vs=Time.deltaTime*FSpeed;
- }
-
- if(head.z>hipCenter.z+0.1){
- //print("back............................");
- vs=Time.deltaTime*BSpeed;
- }
- }
-
- //用下面的方法获得Car.js文件的throttle,steer变量,分别控制纵向和横向
- Component AComponent = gameObject.GetComponent("Car");
- FieldInfo fieldInfo = AComponent.GetType().GetField("throttle");
- FieldInfo fieldInfo1 = AComponent.GetType().GetField("steer");
- fieldInfo.SetValue(AComponent,vs);
- fieldInfo1.SetValue(AComponent,hs);
- }
- }
复制代码
(4)将Car.js的以下两个变量改为public
public var throttle : float = 0;
public var steer : float = 0;
并且,锁定到以下方法,注释掉键盘控制
(5)将KinectAction.cs拖到Car上
|