C++ 俄罗斯方块(windows API)

来源:互联网 发布:我佛瓷杯淘宝 编辑:程序博客网 时间:2024/05/21 10:57
下面这些俄罗斯方块代码是我最近放假在家里自己写的,虽然以前有过看别人写的别人写的代码,但是那个游戏代码好像不是很全面,因为无法实现全部的方块和实现随机的产生任意方向的方块,现在也基本上是忘光了当时的代码,下面的这些代码是我最近写的,没有参考其他人的代码,真正写俄罗斯方块起来感觉真的是挺难的,关键是在于方块的旋转。当然下面的代码仅仅是一个框架,只能够实现大致上的功能,还不全面,贴出来和大家交流学习。编译器是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 1class 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) ;}

 
原创粉丝点击