C++实现俄罗斯方块(windows API)分享!

本文分享的这些俄罗斯方块代码是我最近放假在家里自己写的,虽然以前有过看别人写的代码,但是那个游戏代码好像不是很全面,因为无法实现全部的方块和实现随机的产生任意方向的方块,现在也基本上是忘光了当时的代码,下面的这些代码是我最近写的,没有参考其他人的代码,真正写俄罗斯方块起来感觉真的是挺难的,关键是在于方块的旋转。当然下面的代码仅仅是一个框架,只能够实现大致上的功能,还不全面,贴出来和大家交流学习。

编译器是code::block  +  MinGW ,感觉CB这个IDE真的是太强大,太棒了,下面的代码直接复制到VC里面运行应该不会出错,有个问题一直不知道怎么解决,就是更新客户区时窗口总是闪不知道有哪位达人能指点我一下。有的都是windows API写的,对windows编程还不是很懂,望大家多多留言,指点一下本人。

  #include <windows.h>   #include <iostream>   #include <cstdlib>   #include <ctime>   using namespace std;   #define CellWidth 20   #define MAP_WIDTH 12   #define MAP_HEIGHT 18   #define ID_TIMER 1   class map_floor;   class Block;   LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);   /* Make the class name into a global variable */   char szClassName[ ] = "CodeBlocksWindowsApp";   int WINAPI WinMain (HINSTANCE hThisInstance,              HINSTANCE hPrevInstance,              LPSTR lpszArgument,              int nCmdShow)   {     HWND hwnd;        /* This is the handle for our window */     MSG messages;      /* Here messages to the application are saved */     WNDCLASSEX wincl;    /* Data structure for the windowclass */        /* The Window structure */     wincl.hInstance = hThisInstance;     wincl.lpszClassName = szClassName;     wincl.lpfnWndProc = WindowProcedure;   /* This function is called by windows */     wincl.style = CS_DBLCLKS|CS_HREDRAW | CS_VREDRAW;         /* Catch double-clicks */     wincl.cbSize = sizeof (WNDCLASSEX);        /* Use default icon and mouse-pointer */     wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);     wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);     wincl.hCursor = LoadCursor (NULL, IDC_ARROW);     wincl.lpszMenuName = NULL;         /* No menu */     wincl.cbClsExtra = 0;           /* No extra bytes after the window class */     wincl.cbWndExtra = 0;           /* structure or the window instance */     /* Use Windows's default colour as the background of the window */     wincl.hbrBackground =(HBRUSH) GetStockObject(WHITE_BRUSH);//COLOR_BACKGROUND;        /* Register the window class, and if it fails quit the program */     if (!RegisterClassEx (&wincl))       return 0;        /* The class is registered, let's create the program*/     hwnd = CreateWindowEx (         0,         /* Extended possibilites for variation */         szClassName,     /* Classname */         "Code::Blocks Template Windows App",    /* Title Text */         WS_OVERLAPPEDWINDOW, /* default window */         CW_USEDEFAULT,    /* Windows decides the position */         CW_USEDEFAULT,    /* where the window ends up on the screen */         CW_USEDEFAULT,         /* The programs width */         CW_USEDEFAULT,         /* and height in pixels */         NULL,    /* The window is a child-window to desktop */         NULL,        /* No menu */         hThisInstance,    /* Program Instance handler */         NULL         /* No Window Creation data */         );        /* Make the window visible on the screen */     ShowWindow (hwnd, nCmdShow);        /* Run the message loop. It will run until GetMessage() returns 0 */     while (GetMessage (&messages, NULL, 0, 0))     {       /* Translate virtual-key messages into character messages */       TranslateMessage(&messages);       /* Send message to WindowProcedure */       DispatchMessage(&messages);     }        /* The program return-value is 0 - The value that PostQuitMessage() gave */     return messages.wParam;   }      enum{e_LINE,e_CORNER,e_STAIR,e_TANCK,e_TIAN};   const int TOTAL_BLOCK_STYLE = 5;//方块类型有4种   class Block   {     public:         Block(int x = 100, int y = 100);         Block(const Block & rh)//复制构造函数,可能没什么用,但是还是定义它吧         {           this->m_style = rh.m_style;           this->m_direct = rh.m_direct;           for(int i = 0 ; i < 4 ; i ++)             this->m_block[i] = rh.m_block[i];         }         Block & operator = (const Block& rh)//重载=号,实现方块的赋值         {           this->m_style = rh.m_style;           this->m_direct = rh.m_direct;           for(int i = 0 ; i < 4 ; i ++)             this->m_block[i] = rh.m_block[i];           return *this;         }         ~Block(){}     int   create_block(int x = 100 , int y = 100);     //显示在游戏区内移动的方块     int   show_block(HDC hdc,const POINT& GameLeftTop);     //显示将要出现的方块,即游戏区左边的方块     int   show_next_block(HDC hdc);     //旋转,该函数比较难实现,代码量也比较大,以后有时间在慢慢优化,关于解析看定义处     int   rotate();     //产生随机方块     int   random_block();        //下面为方块移动的成员函数     int   get_block_height(){ return m_block[1].y;}     int   move_down(const RECT& GameClient);     int   move_left(const RECT& GameClient);     int   move_right(const RECT& GameClient);     int   move_up(const RECT& GameClient);     int   move_to(int x , int y);     //检测方块是否在游戏区内   //  int   check_block(const map_floor& map, const RECT& GameClent);     int   check_block(const map_floor& map, const POINT& LeftTopScrCdnt);     int   print_to_map(map_floor& map , const POINT& LeftTopScrCdnt);     private:     int   m_style;//方块的风格样式,具体看定义的枚举变量     int   m_direct;//方块的方向,是对m_style的具体数据    POINT   m_block[4];//下标为1的方块是中心坐标,旋转都是围绕着该方块进行,这样可以利于旋转和逻辑清晰   };   class map_floor   {     public:       map_floor()       {         ZeroMemory(m_block_bar,sizeof(int )*12*18);       }       ~map_floor(){}     void show_block_bar(HDC hdc , const POINT& LeftTopScrCdnt)     {       for(int i = 0 ; i < MAP_HEIGHT ; ++ i)       {         for(int j = 0 ; j < MAP_WIDTH ; ++ j)         {           if(m_block_bar[i][j])           {             Rectangle(hdc,LeftTopScrCdnt.x + j*CellWidth , LeftTopScrCdnt.y + i*CellWidth,                    LeftTopScrCdnt.x + (j+1)*CellWidth , LeftTopScrCdnt.y + (i+1)*CellWidth);           }         }       }        }     friend class Block;     protected:        private:        int     m_block_bar[MAP_HEIGHT][MAP_WIDTH];//游戏区的地板,用18*12的二维数组表示         };      Block::Block(int x , int y)   {   //  ZeroMemory(m_block_bar,sizeof(int )*12*18);     srand( (unsigned)time( NULL ) );//初始化随机数,用于产生方块   //  POINT pt = {100,100};     create_block(x,y);   }   int Block::random_block()   {     m_style = rand()%TOTAL_BLOCK_STYLE;   //  m_style = e_CORNER; //测试之用   //  m_style = e_LINE; //测试之用     if(m_style == e_STAIR || m_style == e_TANCK)       m_direct = rand()%4;     else if(m_style == e_LINE)       m_direct = rand()%2;     else if(m_style == e_CORNER)       m_direct = rand()%8;     else if(m_style == e_TIAN)       m_direct = 0;     m_direct = 1;      }   int  Block::check_block(const map_floor& map, const POINT& LeftTopScrCdnt)   {     int x , y ; //x , y 为方块相对于地图的坐标,左上角为(0,0)     for(int i = 0 ; i < 4 ; i ++)     {       x = (m_block[i].x - LeftTopScrCdnt.x)/CellWidth;       y = (m_block[i].y - LeftTopScrCdnt.y)/CellWidth;       if(x < 0 || x >= MAP_WIDTH || y >= MAP_HEIGHT)//不用检测y < 0 的情况       return 0;       if(y < 0) continue;       if(map.m_block_bar[y][x])       return 0;     }     return 1;   }   int Block::move_down(const RECT& GameClient)//下移,由计时器消息调用   {     int i;   //  for (i = 0 ; i < 4 ; i ++ )   //  {   //   if(m_block[i].y == GameClient.bottom - CellWidth)   //   return 0;   //  }     for (i = 0; i < 4 ;i ++ )     {       m_block[i].y += CellWidth;     }     return 1;   }   int Block::move_up(const RECT& GameClient)   {     move_to(m_block[1].x,m_block[1].y - CellWidth);     return 1;   }   int Block::move_left(const RECT& GameClient)   {     move_to(m_block[1].x - CellWidth,m_block[1].y);     return 1;   }   int Block::move_right(const RECT& GameClient)   {     move_to(m_block[1].x + CellWidth , m_block[1].y);     return 1;   }   int Block::create_block(int x , int y)   {     m_block[1].x = x;     m_block[1].y = y;     random_block();     rotate();     return 1;   }   int Block::move_to(int x , int y)   {     int Vx = x - m_block[1].x;     int Vy = y - m_block[1].y;     for(int i = 0 ; i < 4 ; i ++)     {       m_block[i].x += Vx;       m_block[i].y += Vy;     }   }   int Block::print_to_map(map_floor& map , const POINT& LeftTopScrCdnt)   {     int x , y;     int i , j;     for(i = 0 ; i < 4 ; i ++ )     {       x = (m_block[i].x - LeftTopScrCdnt.x)/CellWidth;       y = (m_block[i].y - LeftTopScrCdnt.y)/CellWidth;       if(x<0 || x >= MAP_WIDTH || y <0 || y >= MAP_HEIGHT)//为保安全 ,测试之用,完成后将被注释掉         return 0;       map.m_block_bar[y][x] = 1 ;       for(j = 0 ; j < MAP_WIDTH ; j ++)       {         if(map.m_block_bar[y][j] != 1)           break;       }       if(MAP_WIDTH == j)       {         for(j = 0 ; j < MAP_WIDTH ; j ++)         {           map.m_block_bar[y][j] = 5;//数字5代表要消掉的行         }       }        }     int idx;     for(i = 0 ; i < MAP_WIDTH ; i ++)     {       for(idx = j = MAP_HEIGHT - 1 ; j >= 0 ; j --)       {         if(map.m_block_bar[j][i] != 5)         {           map.m_block_bar[idx--][i] = map.m_block_bar[j][i];         }       }       while(idx >= 0)       {         map.m_block_bar[idx--][i] = 0;       }     }     return 1;   }      //下面该函数功能是实现方块旋转,可以说是整个【俄罗斯方块】的难点所在,也是其核心部分   //方块用以数组block【4】表示,其余3个方格都将围绕block【1】旋转,方块由于有不对称方块   //存在,我原本是要分7种,但是后面代码量太大了,所以我将方块根据样式归为了四种,分别是:   //   //e_LINE 线形   就是一条线的那个,这个是对称的方块,只需分两个方向分别为横向和纵向,方向   //        用m_direct保持,其他的方块一样   //   //e_TANCK 坦克形 这个是方块是对称的,分四种方向,根据m_direct对4进行求余的方法可以大大缩减   //        代码量,对于下面两种方块也是利用了求余的方式化简许多,才使得代码不会那么冗余,   //        这是后面我才想到的方法。   //   //e_STAIR 楼梯形 这个方块相对前面两种来说有点难度,主要是因为它不是对称的,但是相对下面的这种   //        来说比较简单,原本我没用对m_direct求余的方法时,我将它分为了e_STAIR_BACK和e_STAIR_FRONT   //        两类来讨论的,后面发现代码可以缩减才将其归为一类只要记住block【0】和block【1】的位置不会   //        变化,变化的是block【2】和block【3】,block【2】相对block【1】上移或下移,x坐标与block【1】   //        相同,block【3】.y一直在block【1】下面一行,相对其左右变化   //   //e_CORNER 角形 这个方块个人觉得是最难旋转的方块,与上面一种异样,原本我将它分为e_CORNER_FRONT , e_CORNER_BACK   //        两类,每类有四个方向的变化,后来根据求余可以将同一个方向的变化变为一种,只是block【3】号方块要   //        根据m_direct方向来进行调整      int Block::rotate()   {         switch (m_style)         {           case e_LINE:           {             switch(m_direct)             {               case 0://横向转为纵向               {                 for(int i = 0 ; i < 4 ; i ++)                   {                     m_block[i].x = m_block[1].x;                     m_block[i].y = m_block[1].y + (1-i)*CellWidth;                   }                 m_direct = 1;               }                 break;               case 1://纵向转为横向               {                 for(int i = 0 ; i < 4 ; i ++)                 {                     m_block[i].y = m_block[1].y;                     m_block[i].x = m_block[1].x + (1-i)*CellWidth;                 }                 m_direct = 0;               }                 break;             }           }             break;           //下面为楼梯风格的方块,由于其不是对称的分类为正反两种,正反种风格各有两种变化,           //m_direct% == 0是正反两面的同种变化           case e_STAIR:           {             int flag;             flag = m_direct < 2 ? 1 : -1;             m_block[0].x = m_block[1].x + flag*CellWidth;             m_block[0].y = m_block[1].y;             m_block[2].x = m_block[1].x;             m_block[3].y = m_block[1].y + CellWidth;             if(m_direct%2 == 0)             {               m_block[2].y = m_block[1].y - CellWidth;               m_block[3].x = m_block[1].x + flag*CellWidth;               m_direct++;             }             else             {               m_block[2].y = m_block[1].y + CellWidth;               m_block[3].x = m_block[1].x - flag*CellWidth;               if(m_direct < 2) m_direct = 0;               else       m_direct = 2;             }           }             break;           //角形方块,与楼梯形方块一样非对称,有正反俩个种,每种有四种变化,           //下面根据m_direct%4的值将这些变化归类解决,对于正,反面对应的相同           //变化的方向,只有block【3】方格位置不一样,可以看我画的图对比即可了解           case e_CORNER:           {             switch (m_direct%4)             {               case 0:               {                 m_block[0].x = m_block[1].x+CellWidth;                 m_block[0].y = m_block[2].y = m_block[1].y;                 m_block[2].x = m_block[1].x-CellWidth;                 m_block[3].x = m_block[1].x-CellWidth;                 if(m_direct>=4) m_block[3].y = m_block[1].y-CellWidth;                 else       m_block[3].y = m_block[1].y+CellWidth;                 m_direct ++;               }                 break;               case 1:               {                 m_block[0].x = m_block[2].x = m_block[1].x;                 m_block[0].y = m_block[1].y+CellWidth;                 m_block[2].y = m_block[1].y-CellWidth;                 if(m_direct>=4)   m_block[3].x = m_block[1].x+CellWidth;                 else       m_block[3].x = m_block[1].x-CellWidth;                 m_block[3].y = m_block[1].y-CellWidth;                 m_direct ++;               }                 break;               case 2:               {                 m_block[0].x = m_block[1].x-CellWidth;                 m_block[0].y = m_block[2].y = m_block[1].y;                 m_block[2].x = m_block[1].x+CellWidth;                 m_block[3].x = m_block[1].x+CellWidth;                 if (m_direct>=4)  m_block[3].y = m_block[1].y+CellWidth;                 else       m_block[3].y = m_block[1].y-CellWidth;                    m_direct ++;               }                 break;               case 3:               {                 m_block[0].x = m_block[2].x = m_block[1].x;                 m_block[0].y = m_block[1].y-CellWidth;                 m_block[2].y = m_block[1].y+CellWidth;                 if(m_direct>=4)  { m_block[3].x = m_block[1].x-CellWidth; m_direct = 4;}                 else       { m_block[3].x = m_block[1].x+CellWidth; m_direct = 0;}                 m_block[3].y = m_block[1].y+CellWidth;               }                 break;               default:                 break;             }              }             break;           case e_TANCK://坦克形方块,与线形方块一样是对称的,分四种变化           {             switch (m_direct%2)             {               case 0:               {                 m_block[0].x = m_block[2].x = m_block[1].x;                 m_block[0].y = m_block[1].y - CellWidth;                 m_block[2].y = m_block[1].y + CellWidth;                 int flag = m_direct == 0 ? 1 : -1;                 m_block[3].x = m_block[1].x + flag*CellWidth;                 m_block[3].y = m_block[1].y;                 m_direct++;               }                 break;               case 1:               {                 m_block[0].y = m_block[2].y = m_block[1].y;                 m_block[0].x = m_block[1].x - CellWidth;                 m_block[2].x = m_block[1].x + CellWidth;                 m_block[3].x = m_block[1].x;                 int flag = m_direct == 3 ? -1:1;                 m_block[3].y = m_block[1].y + flag*CellWidth;                 if(m_direct == 3) m_direct = 0;                 else m_direct++;                  }                 break;               default:                 break;             }              }             break;           case e_TIAN:           {             m_block[0].y = m_block[1].y;             m_block[0].x = m_block[1].x + CellWidth;             m_block[2].x = m_block[1].x;             m_block[2].y = m_block[1].y + CellWidth;             m_block[3].x = m_block[1].x + CellWidth;             m_block[3].y = m_block[1].y + CellWidth;           }             break;              default:             break;         }              return 0;   }   int Block::show_block(HDC hdc,const POINT& GameLeftTop)   {     for (int i = 0 ; i < 4 ; i ++ )     {       if(m_block[i].y >= GameLeftTop.y)         Rectangle(hdc,m_block[i].x,m_block[i].y,m_block[i].              x+CellWidth,m_block[i].y+CellWidth);       if(i==0)//测试所用,完成后将会被注释掉       {MoveToEx(hdc,m_block[i].x,m_block[i].y,NULL);       LineTo(hdc,m_block[i].x+CellWidth,m_block[i].y+CellWidth);}        }     return 1;   }   int Block::show_next_block(HDC hdc)   {      for (int i = 0 ; i < 4 ; i ++ )     {          Rectangle(hdc,m_block[i].x,m_block[i].y,m_block[i].              x+CellWidth,m_block[i].y+CellWidth);     }     return 1;   }   Block block , next_block , try_block;   map_floor map;int d = 0;   LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)   {      HDC     hdc ;      PAINTSTRUCT ps ;      //游戏客户区      static RECT GameClient;      //一个方格的像素为CellWidth = 20 游戏区宽 12 个方格 高 18 个方格      const int  Width = 240 ,Height = 360;      static POINT LeftTopScrCdnt;//游戏区得左上角坐标         switch (message)      {      case WM_CREATE:        SetTimer(hwnd,ID_TIMER,500,NULL);        return 0 ;      case WM_SIZE:        GetClientRect(hwnd,&GameClient);        LeftTopScrCdnt.x = (GameClient.right-GameClient.left)/2 - Width/2;        LeftTopScrCdnt.y = GameClient.top + 50;        GameClient.left  = LeftTopScrCdnt.x;        GameClient.top  = LeftTopScrCdnt.y;        GameClient.right = LeftTopScrCdnt.x + Width;        GameClient.bottom = LeftTopScrCdnt.y + Height;        //创建下一个将要出现的方块        next_block.create_block(GameClient.right+2*CellWidth,(GameClient.bottom+GameClient.top)/2-3*CellWidth);        block.move_to((GameClient.right+GameClient.left)/2,GameClient.top-CellWidth);           break;      case WM_TIMER:        block.move_down(GameClient);        if(!block.check_block(map,LeftTopScrCdnt))//检测方块的碰撞,如果则说明方块到底底部,将其上移然后打印进地图        {          block.move_up(GameClient);          if(!block.check_block(map,LeftTopScrCdnt) ||            block.get_block_height() <= LeftTopScrCdnt.y )//检测游戏是否结束            {              KillTimer(hwnd,ID_TIMER);              d = 4;            }          block.print_to_map(map,LeftTopScrCdnt);          SendMessage(hwnd,WM_KEYDOWN,VK_ESCAPE,0);        }        InvalidateRect(hwnd,NULL,true);        break;      case WM_PAINT:        hdc = BeginPaint (hwnd, &ps) ;        MoveToEx(hdc,LeftTopScrCdnt.x,LeftTopScrCdnt.y,NULL);        Rectangle(hdc,GameClient.left,GameClient.top,GameClient.right,GameClient.bottom);//游戏区边框        SelectObject(hdc,GetStockObject(BLACK_BRUSH));        map.show_block_bar(hdc,LeftTopScrCdnt);        block.show_block(hdc,LeftTopScrCdnt);        next_block.show_next_block(hdc);        EndPaint (hwnd, &ps);        break;      case WM_KEYDOWN:        InvalidateRect(hwnd,NULL,true);        switch (wParam)        {         case VK_SPACE:         {           try_block = block;           try_block.rotate();           if(try_block.check_block(map ,LeftTopScrCdnt))             block = try_block;           break;         }         case VK_LEFT:         {           block.move_left(GameClient);           if(!block.check_block(map ,LeftTopScrCdnt))             block.move_right(GameClient);         }           break;         case VK_RIGHT:         {           block.move_right(GameClient);           if (!block.check_block(map ,LeftTopScrCdnt))             block.move_left(GameClient);         }           break;         case VK_DOWN:         {   //        block.move_down(GameClient);            SendMessage(hwnd,WM_TIMER,0,0);         }           break;            case VK_ESCAPE://测试用,完成后将会被注释掉         {           block = next_block;           next_block.create_block(GameClient.right+2*CellWidth,(GameClient.bottom+GameClient.top)/2-3*CellWidth);           block.move_to((GameClient.right+GameClient.left)/2,GameClient.top-CellWidth);         }           break;         default:           break;        }        break;      case WM_DESTROY:        PostQuitMessage (0) ;        return 0 ;      }      return DefWindowProc (hwnd, message, wParam, lParam) ;   }   

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/482414.html

(0)
上一篇 2020年11月9日
下一篇 2020年11月9日

精彩推荐