MFC俄罗斯方块程序解析

MFC俄罗斯方块程序解析
流程:
1. 创建CRussiaGame类(游戏功能,可扩展)(代码在最下面)
2. 创建游戏窗前画初始界面(OnPaint()中调用m_RussiaGame.DrawJiemian( 18, 12, this);)
3. 开始游戏
4. 初始化游戏信息(调用m_RussiaGame.StartInit();)
(1)//游戏数组数据清0
(2)//之前方块和当前方块清0
(3)//初始化当前图形(第一次调用之生成当前图形)
(4)//得到当前图形,和之前图形
(5)//把得到的图形放到数组中
5.重画界面(直接调用OnPaint())
6.使用定时器(SetTimer(1,500,NULL);)(游戏开始自动运行)
(1)OnTimer(UINT_PTR nIDEvent)中的处理代码,方块自动运行

void CMFCRussicDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    if (nIDEvent==1)
    {
        if (m_RussiaGame.end==TRUE)
        {
            KillTimer(1);
            AfxMessageBox("Game Over");//保证定时器先关闭,这时该对话框只调用一次
        }
        else
        {
        //方块自动匀速下落
            m_RussiaGame.Move(m_MyEnum_AUTO); 
        //并现在的图形放到游戏数组中
            m_RussiaGame.NowIntoRussia();
            //重划界面
            OnPaint();
        }

    }   
    CDialog::OnTimer(nIDEvent);
}

(2)处理来自键盘的按键处理
对话框程序截取按键信息,由于封包键盘的信息已部分自动处理过了,不能直接OnKeyDown,可能会收不到按键信息,处理方法如下:先在PreTranslateMessage(MSG* pMsg)中处理下信息

BOOL CMFCRussicDlg::PreTranslateMessage(MSG* pMsg)
{
    // TODO: 在此添加专用代码和/或调用基类
    if (pMsg->message == WM_KEYDOWN)
    {

        OnKeyDown((UINT)pMsg->wParam, (UINT)pMsg->lParam, (UINT)pMsg->lParam);

    }
    else
    {
        return CDialog::PreTranslateMessage(pMsg);
    }
}

void CMFCRussicDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    if (m_RussiaGame.end==FALSE)
    {
        switch (nChar)
        {
        case VK_LEFT://处理键盘->
            m_RussiaGame.Move(m_MyEnum_LEFT);
            break;
        case VK_UP://处理键盘向上箭头
            m_RussiaGame.Move(m_MyEnum_UP);
            break;
        case VK_RIGHT://处理键盘向右箭头
            m_RussiaGame.Move(m_MyEnum_RIGHT);
            break;
        case VK_DOWN://处理键盘向下箭头
            m_RussiaGame.Move(m_MyEnum_DOWN);
            break;
        default:
            break;

        }
    }
    CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}

方块在移动时
(a)要判断是否重叠或过界,则取下移动
(b)不可以向下移动时,要判断是否可以消行
(c)方块旋转选择时,要判断是否有重叠或过界,有的话取消
游戏结束判断:
当新出现的方块,在游戏数组已经有重叠,说明游戏到顶了,游戏结束,这个放在消行函数中判断(需要判断消行,证明当前方块到底了,已停止运动,要生成新的方块)

CRusssia类的代码

class CRussiaGame
{
public:
    CRussiaGame();
    virtual ~CRussiaGame();
public:
    int m_RowCount;         //游戏窗口行数
    int m_ColumnCount;      //游戏窗口列数
    int Russia[100][100];   //游戏数组
    int Now[4][4];          //当前图形
    int Will[4][4];         //上一个图行
    bool end;               //游戏结束状态
    CPoint NowPos;          //当前棋子左上角的位置

    /*方向键(←): VK_LEFT(37)方向键(↑): VK_UP(38)方向键(→): VK_RIGHT(39)方向键(↓): VK_DOWN(40)*/
    enum MyEnum{m_MyEnum_LEFT=37, m_MyEnum_UP, m_MyEnum_RIGHT, m_MyEnum_DOWN, m_MyEnum_AUTO};//m_MyEnum_AUTO表示无操作是自动向下移动

public:
    void DrawJiemian(int m_RowCount, int m_ColumnCount, CWnd* pWnd);//窗口界面画图函数
    void Draw_Now_Will();           //画当前图形和前一个图形,并把现在的图形放到游戏数组中
    void StartInit();                   //游戏开始所需初始化信息
    void NowIntoRussia();                       //把现在的图形放到游戏数组中
    void Move(int movekey);                 //现在图形在游戏数组中移动
    bool CHeckNoMeet(int (*a)[4], int movekey, CPoint p);//方块移动时,如果过届或重叠应取消
    void LineDelete();              //消行函数,(可扩展计分功能)
    void change();                  //棋子转换,并如果出界,或重叠,则还原       
};



CRussiaGame::CRussiaGame()
{
}


CRussiaGame::~CRussiaGame()
{
}

//窗口界面画图函数
void CRussiaGame::DrawJiemian(int m_RowCount, int m_ColumnCount, CWnd* pWnd)
{
    //获取游戏界面相关画图画图工具
    CDC* pDC = pWnd->GetDC();
    CClientDC dc(pWnd);
    CDC dcCompatible;
    CDC dcCompatible1;
    //画笔选择
    CPen pen(PS_SOLID, 0, RGB(0, 0, 255));
    CPen* oldpen = dc.SelectObject(&pen);

    //载入背景图片
    CBitmap bitmap;
    bitmap.LoadBitmap(IDB_BITMAP1);


    dcCompatible.CreateCompatibleDC(pDC);
    dcCompatible.SelectObject(&bitmap);

    //填充背景图片
    for (int i = 0; i < m_RowCount; i++)                    
    {
        for (int j = 0; j< m_ColumnCount; j++)
        {
            pDC->BitBlt(0 + j * 35, 20 + i * 35, 35, 35,
                &dcCompatible, 0, 0, SRCCOPY);
        }

    }
    DeleteDC(dcCompatible);
    //画网格线,从上往下
    for (int i = 0, j = 0; i <=m_ColumnCount; i++)
    {
        dc.MoveTo(j, 20);
        dc.LineTo(j, 20 + 35 *m_RowCount);
        j += 35;
    }
    //从左往右
    for (int i = 0, j = 0; i <= m_RowCount; i++)
    {
        dc.MoveTo(0, 20 + j);
        dc.LineTo(m_ColumnCount * 35, 20 + j);
        j += 35;
    }

    CBitmap bitmap1;
    bitmap1.LoadBitmap(IDB_BITMAP2);
    dcCompatible1.CreateCompatibleDC(pDC);
    dcCompatible1.SelectObject(&bitmap1);


    for (int i = 0; i < m_RowCount; i++)
    {
        for (int j = 0; j < m_ColumnCount; j++)
        {
            if (Russia[i][j]==1)
            {
                pDC->BitBlt(0 + j * 35, 20 + i * 35, 35, 35,&dcCompatible1, 0, 0, SRCCOPY);
            }
        }

    }
    DeleteDC(dcCompatible1);

    dc.SelectObject(oldpen);
    pDC->Detach();

}

//画当前图形和前一个图形,
void CRussiaGame::Draw_Now_Will()
{   
    int i, j;

    //将之前的出现的方块放到现在的方块中,并把之前的方块数字清0
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            Now[i][j] = Will[i][j];
            Will[i][j] = 0;
        }
    }

    //初始化随机种子
    srand((unsigned)time(NULL));
    int nTemp = rand() % 7;             //7表示可以方块的种类

    switch (nTemp)
    {
    case 0:
        Will[0][0] = 1;
        Will[0][1] = 1;
        Will[1][0] = 1;
        Will[1][1] = 1;
        break;
    case 1:
        Will[0][0] = 1;
        Will[1][0] = 1;
        Will[1][1] = 1;
        Will[1][2] = 1;
        break;
    case 2:
        Will[0][0] = 1;
        Will[0][1] = 1;
        Will[0][2] = 1;
        Will[0][3] = 1;
        break;
    case 3:
        Will[0][2] = 1;
        Will[1][0] = 1;
        Will[1][1] = 1;
        Will[1][2] = 1;
        break;
    case 4:
        Will[0][0] = 1;
        Will[0][1] = 1;
        Will[1][1] = 1;
        Will[1][2] = 1;
        break;
    case 5:
        Will[0][1] = 1;
        Will[0][2] = 1;
        Will[1][0] = 1;
        Will[1][1] = 1;
        break;
    case 6:
        Will[0][1] = 1;
        Will[1][0] = 1;
        Will[1][1] = 1;
        Will[1][2] = 1;
        break;

    //方块扩展备用
    case 7:
        break; 
    case 8:
        break;
    default:
        break;
    }

    NowPos.x = m_ColumnCount / 2;
    NowPos.y= 0;

}

//游戏开始所需初始化信息
void CRussiaGame::StartInit()                   
{
    end = FALSE;
    m_ColumnCount = 12;
    m_RowCount = 18;
    //游戏数组数据清0
    for (int i = 0; i < m_RowCount; i++)
    {
        for (int j = 0; j < m_ColumnCount; j++)
        {
            Russia[i][j] = 0;
        }
    }
    //之前方块和当前方块清0
    for (size_t i = 0; i < 4; i++)
    {
        for (size_t j = 0; j < 4; j++)
        {
            Now[i][j] = 0;
        }
    }

    //初始化当前图形(第一次调用之生成当前图形)
    Draw_Now_Will();
    Sleep(500);
    //得到当前图形,和之前图形
    Draw_Now_Will();
    //把得到的图形放到数组中
    NowIntoRussia();
}


//把现在的图形放到游戏数组中
void CRussiaGame::NowIntoRussia()
{
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            if (Now[j][i] == 1)//一定要检测,防止其他是1的地方被清0了
            {
                Russia[NowPos.y + j][NowPos.x + i] = 1;
            }
        }
    }
}

//现在图形在游戏数组中移动
void CRussiaGame::Move(int movekey)
{
    if (end)return;

        switch (movekey)
        {
        case m_MyEnum_LEFT:
            if (!CHeckNoMeet(Now, m_MyEnum_LEFT, NowPos)) { break; }
            NowPos.x -= 1;
            break;
        case m_MyEnum_UP:
            change();
            break;
        case m_MyEnum_RIGHT:
            if (!CHeckNoMeet(Now, m_MyEnum_RIGHT, NowPos)) { break; }
            NowPos.x += 1;
            break;
        case m_MyEnum_DOWN:
            if (!CHeckNoMeet(Now, m_MyEnum_DOWN, NowPos)) { break; }
            NowPos.y += 1;
            break;
        case m_MyEnum_AUTO:
            if (!CHeckNoMeet(Now, m_MyEnum_AUTO, NowPos))
            {
                LineDelete();//消行设计
                NowIntoRussia();
                break;
            }
            NowPos.y += 1;

            break;
        default:
            break;
        }



    //AfxMessageBox("Game Over");没有问题
}

//方块移动时,如果过届或重叠应取消,返回True表示没有过届或重叠,false表示过届或重叠
bool  CRussiaGame::CHeckNoMeet(int (*a)[4], int movekey, CPoint p)
{

    int i, j;

    //把原先位置清0
    for ( i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            if (a[j][i] == 1)
            {
                Russia[p.y + j][p.x + i] = 0;
            }
        }
    }


    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            if (a[j][i]==1)//首先把所有存图的点找出来
            {
                switch (movekey)
                {
                    case m_MyEnum_LEFT:
                        if ((p.x +i- 1) <0) goto exit;
                        if (Russia[p.y + j][p.x + i-1] == 1) goto exit;
                        break;
                    case m_MyEnum_UP:

                        break;
                    case m_MyEnum_RIGHT:
                        if ((p.x +i+ 1)>=m_ColumnCount) goto exit;
                        if (Russia[p.y + j][p.x + i +1] == 1) goto exit;
                        break;
                    case m_MyEnum_DOWN:
                        if ((p.y +j+ 1) >= m_RowCount) goto exit;
                        if (Russia[p.y + j + 1][p.x + i] == 1) goto exit;
                        break;
                    case m_MyEnum_AUTO:
                        if ((p.y+j+1) >= m_RowCount) goto exit;
                        if (Russia[p.y+j+1][p.x+i]==1) goto exit;
                        break;
                    default:
                        break;
                }

            }
        }
    }
//移动位置,重新给数组赋值
    switch (movekey)
    {
        case m_MyEnum_LEFT:
            p.x--;
            break;
        case m_MyEnum_UP:

            break;
        case m_MyEnum_RIGHT:
            p.x++;
            break;
        case m_MyEnum_DOWN:
            p.y++;
            break;
        case m_MyEnum_AUTO:
            p.y++;
            break;
        default:
            break;
    }

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            if (Now[j][i] == 1)
            {
                Russia[p.y + j][p.x + i] = 1;
            }
        }
    }



    return true;
    //移动过界或重叠,返回失false,还原原样
exit:
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            if (Now[j][i] == 1)
            {
                Russia[p.y + j][p.x + i] = 1;
            }
        }
    }
    return false;


}

//消行函数,(可扩展计分功能,增强难度<调速>),
void  CRussiaGame::LineDelete()
{
    int m = 0;      //本次共消去的行数
    bool flag = FALSE;      //消行标记
    for (int  i = 0; i < m_RowCount; i++)
    {
        flag = TRUE;
        for (int j = 0; j < m_ColumnCount; j++)
        {
            if (Russia[i][j]==0)
            {
                flag = FALSE;
                break;
            }
        }
        //如果要消行
        if (flag)
        {
            m++;
            for (int  k = i; k>0 ; k--)
            {
                //上行给下行
                for (int  l = 0; l < m_ColumnCount; l++)
                {
                    Russia[k][l] = Russia[k - 1][l];
                }
            }
            //第一行为0
            for (int l = 0; l < m_ColumnCount; l++)
            {
                Russia[0][l] = 0;
            }

        }

    }
    Draw_Now_Will();//需要判断消行,证明当前方块到底了,已停止运动,要生成新的方块

    //判断游戏是否结束
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            if (Now[j][i] == 1)
            {
                if (Russia[NowPos.y+j][ NowPos.x+i] == 1)//到达顶点,游戏结束
                {
                    end = TRUE;
                    AfxMessageBox("Game Over1");

                    return ;
                }
            }   
        }

    }
    //AfxMessageBox("Game Over1");//没有问题
}

//方块旋转
void CRussiaGame::change()
{
    int temp[4][4];
    int i, j;

    //清空图形在游戏数组的数据
    for (i = 0; i < 4; i++)
    {
        for (j  = 0; j < 4; j++)
        {
            if (Now[i][j]==1)
            {
                Russia[NowPos.y+i][NowPos.x+j] = 0;
            }
        }
    }

    //把Now数据放到Temp中
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            temp[i][j] = Now[i][j];
        }
    }
    //进行旋转
    for (i = 0; i < 2; i++)
    {
        for (j = 0; j < 2; j++)
        {
            int t;
            t = Now[i][j];
            Now[i][j] =Now[3-j][i] ;
            Now[3 - j][i] = Now[3-i][3-j];
            Now[3 - i][3 - j]=Now[j][3-i];
            Now[j][3 - i] = t;

        }
    }

    //判断是否过界,如果过界则还原数据
    //(1)循环把所有存图的点找出来
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            if (Now[i][j] == 1)
            {
                //判断点放到数组中过界
                if (NowPos.x+j<0|| NowPos.x + j>=m_ColumnCount||NowPos.y+i<0|| NowPos.y + i>=m_RowCount)
                {
                    goto exit;
                }
                //判断是否重叠,如果重叠则还原数据
                if (Russia[NowPos.y + i][NowPos.x + j] ==1)
                {
                    goto exit;
                }
            }
        }
    }
    //所有点没有过界或重叠,把旋转后得到Now中数据放到游戏数组中
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            if (Now[i][j] == 1)
            {
                Russia[NowPos.y + i][NowPos.x + j] = 1;
            }
        }
    }
    return;



exit:
    //还原数据
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            Now[i][j] = temp[i][j];
        }
    }

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            if (Now[i][j] == 1)
            {
                Russia[NowPos.y + i][NowPos.x + j] = 1;
            }
        }
    }
}

你可能感兴趣的:(MFC)