Kinect C++简单切水果的一点总结

 资源下载地址  

http://download.csdn.net/detail/zhoupeng39/7204925

      一、整体的思路

  既然是Kinect开发,首先解决的就是识别的问题,其实网上切水果视频里面的主要就是有后面黑色背影,手部运动形成的水果刀,黑色背影就是深度图像的获取,然后根据它的ID编号,判断人体的部分。移动的水果刀,是利用骨骼图像找出手的位置,然后确定连线,画出水果刀。当然,这些资源的获取在程序里面都是在一个单独的线程里面执行的。

   然后才是切水果游戏的开发,关于这个游戏,我没有想太多,其实就是水果的移动,然后连线判断是否经过矩形(程序里面,我只是用一个点来判断),然后后面没有写切成两半的效果,用了一个粒子模型画出切开之后的画面,其中切中之后出现短暂的到刀锋效果,就是一个四边形,然后具体的就是判断水果的类型,然后对应的加分惩罚等等,这些细节的东西我写的比较少,毕竟主要是Kinect的使用。

     二. 程序的细节和代码

      1.这里直接贴出获取手的骨骼位置的代码,都是些基础的代码,至于深度图像的,看看这个人的博客 http://blog.csdn.net/zouxy09/article/category/1273380

      

int GetHandsPoints()
{   
    HANDLE skeletonEvent=CreateEvent(NULL,true,false,NULL);
    HRESULT hr=NuiInitialize(NUI_INITIALIZE_FLAG_USES_SKELETON);
    if (FAILED(hr))
    {
        MessageBox(AfxGetMainWnd()->m_hWnd,"初始化失败","错误",MB_OK);
        return -1;
    }
    //骨骼数据
    hr=NuiSkeletonTrackingEnable(skeletonEvent,0);
    if (FAILED(hr))
    {   
        MessageBox(AfxGetMainWnd()->m_hWnd,"打开骨骼失败","错误",MB_OK);
        NuiShutdown();
        return -1;
    }
    while(1)
    {   
       if (WaitForSingleObject(skeletonEvent,INFINITE)==0)
       {
         NUI_SKELETON_FRAME skeletonFrame = {0};  
         bool bFoundSkeleton = false;    
         if(NuiSkeletonGetNextFrame( 0, &skeletonFrame ) == S_OK )  
         {
           if( skeletonFrame.SkeletonData[0].eTrackingState == NUI_SKELETON_TRACKED )      
                  bFoundSkeleton = true;     
         }
        if (bFoundSkeleton)
       {
         NuiTransformSmooth(&skeletonFrame, NULL);
         float fx,fy; 
         if( skeletonFrame.SkeletonData[0].eTrackingState == NUI_SKELETON_TRACKED &&     
                  skeletonFrame.SkeletonData[0].eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_SHOULDER_CENTER] != NUI_SKELETON_POSITION_NOT_TRACKED)  
            { 
                //左手
                if (skeletonFrame.SkeletonData[0].eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_HAND_LEFT] != NUI_SKELETON_POSITION_NOT_TRACKED)
                      {
                      NuiTransformSkeletonToDepthImage(skeletonFrame.SkeletonData[0].SkeletonPositions[NUI_SKELETON_POSITION_HAND_LEFT],&fx,&fy,NUI_IMAGE_RESOLUTION_640x480);
                      HandsPoints[0].x=(int)fx;
                      HandsPoints[0].y=(int)fy; 
                      if (IsRunGame)
                      {
                      IsEndofXC1=FALSE;
// 利用队列存储坐标点 m_pt1.AddPt(HandsPoints[
0],IsEndofXC1); } else { PanDuan(HandsPoints[0]); } } // 右手 if (skeletonFrame.SkeletonData[0].eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_HAND_RIGHT] != NUI_SKELETON_POSITION_NOT_TRACKED) { NuiTransformSkeletonToDepthImage(skeletonFrame.SkeletonData[0].SkeletonPositions[NUI_SKELETON_POSITION_HAND_RIGHT],&fx,&fy,NUI_IMAGE_RESOLUTION_640x480); HandsPoints[1].x=(int)fx; HandsPoints[1].y=(int)fy; if (IsRunGame) { IsEndofXC2=FALSE; m_pt2.AddPt(HandsPoints[1],IsEndofXC2); } else { PanDuan(HandsPoints[1]); AfxGetMainWnd()->Invalidate(FALSE); } }
//结束时叉行手势的识别 GetElbowPoints(skeletonFrame); } } } } NuiShutdown();
return 0; }

    2.其实整个代码的过程中纠结最久的就是手的坐标的存储,使用队列存储坐标点,限制里面点的个数,在指针的使用上一开始总是出现访问的错误,下面是代码。

  

void CLinePt::AddPt(CPoint point,BOOL& IsEnd)
{
    NewPoint* tmpPoint;
    tmpPoint=(NewPoint*)malloc(sizeof(NewPoint));
    tmpPoint->pt=point;
    tmpPoint->next=NULL;
    if (TotalNum<=Max_Point) TotalNum++;
    if (TotalNum==1)
    {
        Header=tmpPoint;
        End=tmpPoint;
    }
    else
    {
      if (TotalNum>Max_Point)
      {  
          //这里本来是要释放头结点的资源,但是Delete之后一直出现访问错误
          Header=Header->next;
          End->next=tmpPoint;
          //更新尾节点
          End=tmpPoint;
          TotalNum=Max_Point;
       }
      else
      {
          End->next=tmpPoint;
          End=tmpPoint;
      }
    }
    IsEnd=TRUE;
}

     至于画刀的效果,我就简化了很多,直接通过设定线条的宽度变化,实现粗细的变化,当然细节大家可以自己填充。

   3.关于水果的移动和简单的粒子系统,当然我只是初步的接触了下游戏的编程,上面的也只是基础的内容,大家可以学习一下大神的博客  http://blog.csdn.net/crocodile__

 

//增加到下一帧
void
CGoods::AddFrame() { Current_Frame++; if (Current_Frame>14) Current_Frame=0; } void CGoods::Move() { Current_X+=XSpeed; Current_Y+=YSpeed; if (Current_X>=Width||Current_X<=(-m_goodsImg.GetWidth())||(Current_Y>=Height&&YSpeed>0)) IsExist=FALSE; if (IsExist) Current_Rect.SetRect(Current_X,Current_Y,Current_X+m_goodsImg.GetWidth(),Current_Y+m_goodsImg.GetHeight()/15); }
//改变速度 正负变化
void CGoods::ChangeSpeed() { if (YSpeed<0) { YSpeed+=4; if (YSpeed>=0) YSpeed=0; } else { YSpeed+=4; } } //画图 void CGoods::Draw(CDC* pDC) { if (IsExist) { CRect srcRect; srcRect.SetRect(0,Current_Frame*m_goodsImg.GetHeight()/15,m_goodsImg.GetWidth(),(Current_Frame+1)*m_goodsImg.GetHeight()/15); m_goodsImg.TransparentBlt(pDC->m_hDC,Current_Rect,srcRect,RGB(255,0,255)); } else { if (IsCut) { if (!IsShowKnife) { //画刀锋的效果 GetKnifePoints(); CPen tmp(PS_SOLID,3,RGB(0,255,0)); CPen* oldPen=pDC->SelectObject(&tmp); pDC->Polygon(pt,4); pDC->SelectObject(oldPen); IsShowKnife=TRUE; }
//开启粒子的效果
if (!GoodsLizi.IsStart) { GoodsLizi.InitBall(Current_X+m_goodsImg.GetWidth()/2,Current_Y+m_goodsImg.GetHeight()/30); GoodsLizi.IsStart=TRUE; GoodsLizi.IsEnd=FALSE; //建立定时器 AfxBeginThread(PlayCutMusic,NULL); AfxGetMainWnd()->SetTimer(GOODS_LIZI_CHANGE,30,NULL); } if (!GoodsLizi.IsEnd) GoodsLizi.DrawBall(pDC); } } }

      至于粒子的效果,其实也很简单,就是从同一个点分别坐标轴的四个象限生成不同的速度,然后变化移动直至消失,具体的可以看我里面的代码

void CLizi::MoveBall()
{
  if(BallsCount>0) 
  {
    for(int i=0;i<30;i++)
    {
      if (Balls[i].IsExist)
      {
          Balls[i].x+=Balls[i].cx;
          Balls[i].y+=Balls[i].cy;
          Balls[i].lasted++;
          Balls[i].CurrentTh-=1;
          if (Balls[i].CurrentTh<=0) Balls[i].CurrentTh=1;
          if (Balls[i].lasted>20||Balls[i].x<-10||Balls[i].x>(Width+10)||Balls[i].y<-10||Balls[i].y>(Height+10))
          {
              Balls[i].IsExist=FALSE;
              BallsCount--;
          }
      }
    }
  }
  else
      if (IsStart&&!IsEnd) IsEnd=TRUE;
}

void CLizi::DrawBall(CDC* pDC)
{
     for(int i=0;i<30;i++)
     {
         if (Balls[i].IsExist)
         {   
             m_lizi.TransparentBlt(pDC->m_hDC,Balls[i].x,Balls[i].y,Balls[i].CurrentTh,Balls[i].CurrentTh,RGB(255,0,255));
         }
     }
}

    3.其实上面也就差不多了,至于其中的什么碰撞检测,时间定时器的设置,都不是很难,对于游戏现在我感触最深的就是并发的重要性,就是多线程的问题,如何同步,在我的代码里用的很浅,我自己用的也不是很熟。

    4.至于开始界面填充球的效果,首先要说悬浮的手的效果,微软称之为磁性移动的手,它相当于给人一个直观的移动的提示,我临时写的,只是模仿一下,用此来检测手的骨骼的获取,然后填充球的效果,我本来想的就是通过这个来动态的调整人的位置,不过C++的资料太少,关于Kinect基本都是C#的资料,有很多内容获取不到,下面是这一部分的代码

double ridus=cirCleRect.Width()/2.0;
  int centerx=cirCleRect.left+ridus;
  int centery=cirCleRect.top+ridus;
  if (Angle>0)
  {
   CRgn testRgn;
   HRGN tRgn;
   pDC->BeginPath();
   pDC->SetBkMode(TRANSPARENT);
   double angle=Angle/180.0*PI;
   int x1=centerx-ridus*sin(angle);
   int y1=centery+ridus*cos(angle);
   int x2=centerx+ridus*sin(angle);
   int y2=centery+ridus*cos(angle);
   pDC->MoveTo(CPoint(x1,y1));
   pDC->LineTo(CPoint(x2,y2));
   pDC->Arc(cirCleRect,CPoint(x1,y1),CPoint(x2,y2));
   pDC->EndPath();
   tRgn=::PathToRegion(pDC->m_hDC);
   testRgn.Attach(tRgn);
   CBrush* red=new CBrush();
   red->CreateSolidBrush(RGB(255,0,0));
   pDC->FillRgn(&testRgn,red);
   int progress=Angle/180.0*100.0;
   CString tmpp;
   char c='%';
   tmpp.Format("%d %c",progress,c);
   pDC->SetBkMode(TRANSPARENT);
   pDC->SetTextColor(RGB(0,255,0));
   pDC->TextOut(centerx-50,centery-40,tmpp);

    5.关于最后的静态手势,也是做着玩的,交叉手,就是判断线的角点书不是在四个点之间,还是很好写的。

     三.自己的一些感悟

  其实之前是学习OpenCV的,但是当中的数学知识真的很难,不是一时半会儿就能搞定的,后来就偷闲搞搞Kinect,现在有不得不接触下C#,花个两三天谢谢这个小游戏,没什么,就是对以前只是的反复的使用,防止自己忘记,当然代码很挫,毕竟都是自己瞎折腾的,非计算机专业,也没什么训练,就这样写写被,希望大家指证代码里面的错误,当然有问题可以交流下。我们是靠着网络资源成长的,总归要给别人留点什么,是不是!!

     下面粘出我很敬佩的大神的一篇博客里面的内容,共勉!!!

   

             这句话一直写在我C++笔记本的扉页上。
             每当我对前路迷茫的时候,就会翻开扉页,看着这段文字淡淡的笔迹发一会儿呆,然后就渐渐释然了。
             今天我把它留在自己的博客里,希望它也能帮助到那些迷茫的朋友们。

            

             

              总有一天你将破蛹而出,成长得比人们期待的还要美丽。
              但这个过程会很痛,会很辛苦,有时候还会觉得灰心。
              面对着汹涌而来的现实,觉得自己渺小无力。
              但这,也是生命的一部分。做好现在你能做的,然后,一切都会好的。
              我们都将孤独地长大,不要害怕

            

原文地址:https://www.cnblogs.com/fightfuture/p/3669691.html