基于MFC框架的贪吃蛇程序

MFC的作业

要求

使用MFC的文本视图框架来写,实现贪吃蛇的基本功能:食物随机出现,键盘操作,蛇吃食物变长,蛇撞到边界和自己游戏结束,添加蛇身速度选择的按钮控件。
### 代码实现 SnakeDoc.h和SnakeDoc.cpp中的变量声明与初始化如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class CSnakeDoc : public CDocument
{
protected: // create from serialization only
CSnakeDoc();
DECLARE_DYNCREATE(CSnakeDoc)
// Attributes
public:
struct{
int body_x;
int body_y;
int direction;
int length;
}snake[100];
struct{
int food_x;
int food_y;
bool isfood;
}food;
bool startFlag;
... //省略
}
// Operations
BOOL CSnakeDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
snake[0].body_x = 10;
snake[0].body_y = 10;
snake[1].body_x = 11;
snake[1].body_y = 10;
snake[2].body_x = 12;
snake[2].body_y = 10;
snake[0].direction = 3;
snake[0].length = 3;
food.isfood = true;
startFlag = false;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
return TRUE;
}

蛇和食物用两个结构体来表示,snake[]结构体中包含了蛇的坐标和蛇身的长度(蛇是用方块组成,简单来说length表示构成蛇的方块数)。food结构体中包含了食物的坐标和食物是否出现的状态。

视图类中的代码是这样的:

void CSnakeView::OnDraw(CDC* pDC)
{
    CSnakeDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    pDC->TextOut(20,20,"Hsmouc");
    pDC->TextOut(20,50,"Ocean University of China");
    CBrush DrawBrush=(RGB(130,130,0));
    CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);
    for(int i = 0 ; i <= pDoc->snake[0].length-1 ; i++){
        pDC->Rectangle(pDoc->snake[i].body_x*20,pDoc->snake[i].body_y*20,(pDoc->snake[i].body_x+1)*20,(pDoc->snake[i].body_y+1)*20);

    }
    pDC->SelectObject(DrawBrush);
    // TODO: add draw code for native data here
}
void CSnakeView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    // TODO: Add your message handler code here and/or call default
    CSnakeDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    switch(nChar){
        case VK_UP:
            if(pDoc->snake[0].direction != 2){
                pDoc->snake[0].direction = 1;
            }
            break;
        case VK_DOWN:
            if(pDoc->snake[0].direction != 1){
                pDoc->snake[0].direction = 2;
            }
            break;
        case VK_LEFT:
            if(pDoc->snake[0].direction != 4){
                pDoc->snake[0].direction = 3;
            }
            break;
        case VK_RIGHT:
            if(pDoc->snake[0].direction != 3){
                pDoc->snake[0].direction = 4;
            }
            break;
    }
    CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CSnakeView::OnTimer(UINT nIDEvent) 
{
    // TODO: Add your message handler code here and/or call default
    CDC *pDC = GetDC();
    CSnakeDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    CRect rectClient;
    GetClientRect(&rectClient);
    if(pDoc->snake[0].body_x*20 <= rectClient.left || pDoc->snake[0].body_x*20 >= rectClient.right || pDoc->snake[0].body_y*20 <= rectClient.top || pDoc->snake[0].body_y*20 >= rectClient.bottom){
        KillTimer(1);
        AfxMessageBox("233");
        InvalidateRect(rectClient);
        pDoc->gameReset();
    }
    if(pDoc->snake[0].length > 3){
        for(int temp = pDoc->snake[0].length-1 ; temp > 0 ; temp--){
            if(pDoc->snake[0].body_x*20==pDoc->snake[temp].body_x*20 && pDoc->snake[0].body_y*20 == pDoc->snake[temp].body_y*20){
                KillTimer(1);
                AfxMessageBox("233");
                InvalidateRect(rectClient);
                pDoc->gameReset();
            }
        }
    }
    pDC->SelectStockObject(WHITE_PEN);
    pDC->Rectangle(pDoc->snake[pDoc->snake[0].length-1].body_x*20,pDoc->snake[pDoc->snake[0].length-1].body_y*20,(pDoc->snake[pDoc->snake[0].length-1].body_x+1)*20,(pDoc->snake[pDoc->snake[0].length-1].body_y+1)*20);
    for(int i=pDoc->snake[0].length-1 ; i > 0 ; i--){
        pDoc->snake[i].body_x = pDoc->snake[i-1].body_x;
        pDoc->snake[i].body_y = pDoc->snake[i-1].body_y;
    }
    if(pDoc->snake[0].direction==1){
        pDoc->snake[0].body_y--;
    }
    if(pDoc->snake[0].direction==2){
        pDoc->snake[0].body_y++;
    }
    if(pDoc->snake[0].direction==3){
        pDoc->snake[0].body_x--;
    }
    if(pDoc->snake[0].direction==4){
        pDoc->snake[0].body_x++;
    }
    pDC->SelectStockObject(BLACK_PEN);

    CBrush DrawBrush = (RGB(130,130,0));
    CBrush *Drawbrush = pDC->SelectObject(&DrawBrush);
    pDC->Rectangle(pDoc->snake[0].body_x*20,pDoc->snake[0].body_y*20,(pDoc->snake[0].body_x+1)*20,(pDoc->snake[0].body_y+1)*20);
    pDC->SelectObject(DrawBrush);
    if(pDoc->snake[0].body_x*20 == pDoc->food.food_x*20 && pDoc->snake[0].body_y*20 == pDoc->food.food_y*20)
    {
        pDoc->snake[0].length++;
        pDoc->food.isfood = true;
        pDoc->snake[pDoc->snake[0].length-1].body_x = pDoc->snake[pDoc->snake[0].length-2].body_x;
        pDoc->snake[pDoc->snake[0].length-1].body_y = pDoc->snake[pDoc->snake[0].length-2].body_y;
    }
    if(pDoc->food.isfood == true)
    {
        srand((unsigned)time(NULL));
        do{
            for(int temp=pDoc->snake[0].length-1 ; temp >= 0 ; temp--)
                if(pDoc->snake[0].body_x*20 == pDoc->snake[temp].body_x*20 && pDoc->snake[0].body_y*20 == pDoc->snake[temp].body_y*20){
                    pDoc->food.food_x=rand()%25;
                    pDoc->food.food_y=rand()%25;
                    for(temp=pDoc->snake[0].length-1 ; temp >= 0 ; temp--){
                        if(pDoc->food.food_x*20 == pDoc->snake[temp].body_x*20 && pDoc->food.food_y*20 == pDoc->snake[temp].body_y*20){
                            temp = pDoc->snake[0].length-1;
                            pDoc->food.food_x=rand()%25;
                            pDoc->food.food_y=rand()%25;
                        }
                    }
                }
        }while(pDoc->food.food_x*20 < 50 || pDoc->food.food_y*20 < 50 || pDoc->food.food_x*20 > rectClient.right-100 || pDoc->food.food_y*20 > rectClient.right-100);
        pDC->Rectangle(pDoc->food.food_x*20,pDoc->food.food_y*20,(pDoc->food.food_x+1)*20,(pDoc->food.food_y+1)*20);
        pDoc->food.isfood = false;
    }
    CView::OnTimer(nIDEvent);
}
void CSnakeView::OnLButtonDown(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default 
    CView::OnLButtonDown(nFlags, point);
}

void CSnakeView::OnEasy() 
{
    // TODO: Add your command handler code here
    CSnakeDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if(pDoc->startFlag == true){
        SetTimer(1,400,NULL);
    }
}

void CSnakeView::OnCrazy() 
{
    // TODO: Add your command handler code here
    CSnakeDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if(pDoc->startFlag == true){
        SetTimer(1,100,NULL);
    }
}

void CSnakeView::OnHard() 
{
    // TODO: Add your command handler code here
    CSnakeDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if(pDoc->startFlag == true){
        SetTimer(1,200,NULL);
    }
}

void CSnakeView::OnNormol() 
{
    // TODO: Add your command handler code here
    CSnakeDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if(pDoc->startFlag == true){
        SetTimer(1,300,NULL);
    }
}

void CSnakeView::OnStart() 
{
    // TODO: Add your command handler code here
    CSnakeDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    pDoc->startFlag = true;
    SetTimer(1,300,NULL); //normal  
}



void CSnakeView::OnPause() 
{
    // TODO: Add your command handler code here
    KillTimer(1);   
}



void CSnakeView::OnStop() 
{
    // TODO: Add your command handler code here
    AfxMessageBox("Have a nice day!");
    ASSERT(AfxGetApp()->m_pMainWnd != NULL);
    AfxGetApp()->m_pMainWnd->SendMessage(WM_CLOSE);
    
}

让蛇动起来的方式是使用定时器,和以前我做过的卫星和小车大同小异。在这个程序中,蛇前进的方式是通过定时器计时,每过相同的时间,将蛇身部分的位置依次向前传递,用白色的画笔擦去蛇的最后一节。吃食物的时候把食物当成蛇新的头,同时数组长度增加1。
### 小结 这个程序比较繁琐……