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

                

                 

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

                

  • 相关阅读:
    干将莫邪
    Rancher
    Rancher 1.6 版本 只能在 linux 下用
    野蛮人大作战
    Oracle
    Gradle
    正则表达式 我遇到的
    Redis
    NestJS 用TypeScript开发 nodeJS后端
    Koa2
  • 原文地址:https://www.cnblogs.com/fightfuture/p/3669691.html
Copyright © 2020-2023  润新知