贪吃蛇详解Windows编程(四)

控制区的代码就不详细讲解了,自己看看就能明白,并没有什么难点。如果有兴趣的画还可以把界面做好一点,把功能做丰富一点。但是我觉得点到为止就行了。

我用的是Win32空项目写的,你也可以用自动生成的代码写,但是我觉得那个太乱代码太多。
接下来是全部的代码:


#include <windows.h>
#include <vector>
#include <time.h>

//游戏区和控制区宽度高度
#define GAME_WIDE 70 //游戏区宽度70*10
#define GAME_HEIGHT 60 //游戏区高度60*10
#define CTRL_WIDE 30 //控制区宽度30*10
#define CTRL_HEIGHT 60 //控制区高度60*10
#define BOUNDSIZE 10 //边界厚度10 
//蛇的信息
#define SNAKESIZE 10 //蛇的宽高都为10的小方块
#define SNAKEMAX 100 //蛇身长度最大值为100
#define SNAKESPEED 500 //初始化蛇的速度为500ms
#define TIMERID_1 1 //定时器ID
#define PAUSEID 1 //暂停按钮ID
#define STARTID 2 //开始按钮ID 
#define SPEEDUPID 3 //加速按钮ID
#define SPEEDDOWNID 4 //减速按钮ID
std::vector<POINT> vSnakePoint;       //声明一个蛇的坐标

UCHAR ucSnakeHead = 4;                //初始化蛇头为4,实际上是vSnakePoint[3];
UCHAR ucSnakeTail = 0;                //初始化蛇尾为0
UCHAR ucSnakekLen = 4;                //初始化蛇身长度为4
UINT uiSnakeSpeed = SNAKESPEED;       //初始化蛇的速度为500ms
UINT uiScore = 0;                     //初始化分数为0

POINT ptDirectHead = { 1,0 };         //蛇头的方向,初始化为向右
POINT ptFood = { 20,20 };             //食物坐标

BOOL bIsLoose = TRUE;                 //判断是否输了
BOOL bIsWin = FALSE;                  //判断是否赢了
BOOL bIsPause = FALSE;                //判断是否暂停了
BOOL bIsStart = FALSE;                //判断是否开始了
BOOL bFlagStart = FALSE;              //判断是否能开始

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

TCHAR szAppName[] = TEXT("Snake by _acme_");
HINSTANCE hInst = nullptr;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    hInst = hInstance;
    HWND     hwnd;
    MSG      msg;
    WNDCLASS wndclass;

    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName, szAppName,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

//初始化蛇
VOID InitSnake()
{
    //先清空坐标
    vSnakePoint.clear();
    //给蛇坐标分配空间
    vSnakePoint.resize(SNAKEMAX);
    //游戏结束后再次初始化
    ucSnakeHead = 4;//初始化蛇头为4,实际上是vSnakePoint[3];
    ucSnakeTail = 0;//初始化蛇尾为0
    ucSnakekLen = 4;//初始化蛇身长度为4
    uiSnakeSpeed = SNAKESPEED;//初始化蛇的速度为200ms
    ptDirectHead = { 1,0 };//初始化蛇头方向
    ptFood = { 20,20 };//初始化食物坐标
    uiScore = 0;//初始化分数
                //初始化每一节的蛇身坐标
    for (int i = 0; i < ucSnakekLen; i++)
    {
        vSnakePoint[i].x = i;
        vSnakePoint[i].y = 0;
    }
}

//获取蛇结点位置
//index是一个偏移蛇尾的偏移量
POINT &GetSnakeNode(int index)
{
    int i = ucSnakeTail + index;//蛇身的长度
    if (i >= SNAKEMAX)
    {
        i -= SNAKEMAX;
    }
    return vSnakePoint[i];
}

//画蛇身
VOID DrawSnake(HDC hdc)
{
    static HBRUSH hBrush = CreateSolidBrush(RGB(0, 243, 222));
    SelectObject(hdc, hBrush);
    //当前蛇结点
    POINT ptCurrentNode = { 0 };

    for (int i = 0; i < ucSnakekLen; i++)
    {
        ptCurrentNode = GetSnakeNode(i);
        Rectangle(hdc, ptCurrentNode.x*SNAKESIZE + BOUNDSIZE, ptCurrentNode.y*SNAKESIZE + BOUNDSIZE,
            (ptCurrentNode.x + 1)*SNAKESIZE + BOUNDSIZE, (ptCurrentNode.y + 1)*SNAKESIZE + BOUNDSIZE);
    }

}

//设置食物坐标
void SetFood()
{
    POINT ptNow = { 0 };
    int x = 0, y = 0, i = 0;
    while (true)
    {
        srand(time(0));//随机器种子
        x = rand() % GAME_WIDE; //设置食物随机坐标
        y = rand() % GAME_HEIGHT;
        for (i = 0; i < ucSnakekLen; i++)
        {
            ptNow = GetSnakeNode(i);
            if (ptNow.x == x && ptNow.y == y)
                break;//如果食物出现在蛇身,则重新设置食物
        }//如果食物不在蛇身,就退出循环
        if (ucSnakekLen == i) break;
    }
    ptFood.x = x;
    ptFood.y = y;
}

//判断蛇是否撞墙
BOOL IsStrikeWall(POINT ptHead)
{
    if (ptHead.x < 0 || ptHead.x >= GAME_WIDE
        || ptHead.y < 0 || ptHead.y >= GAME_HEIGHT)
    {
        bIsLoose = FALSE;//撞墙输了
        return FALSE;
    }
    return TRUE;
}

//判断是否撞到自己
BOOL IsStrikeSelf(POINT ptHead)
{
    POINT ptNow = { 0 };
    for (int i = 0; i < ucSnakekLen - 1; i++)
    {
        ptNow = GetSnakeNode(i);
        if (ptHead.x == ptNow.x && ptHead.y == ptNow.y)
        {
            bIsLoose = FALSE;//撞自己输了
            return FALSE;
        }

    }
    return TRUE;
}

//判断是否吃到食物
BOOL IsEat(POINT ptHead)
{
    return (ptHead.x == ptFood.x && ptHead.y == ptFood.y) ? TRUE : FALSE;
}

//显示分数
void ShowScore(HWND hwnd)
{
    HDC hdc = GetDC(hwnd);
    HPEN hPen = CreatePen(PS_SOLID, 4, RGB(255, 255, 0));
    SelectObject(hdc, hPen);
    int iLength = 0;
    TCHAR szBuffer[20] = { 0 };
    iLength = wsprintf(szBuffer, L"Score: %u", uiScore);
    TextOut(hdc, BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE + 100,
        BOUNDSIZE + 200, szBuffer, iLength);
    ReleaseDC(hwnd, hdc);
    DeleteObject(hPen);
}

//判断是否超出范围
void IsOverTake()
{
    if (SNAKEMAX == ucSnakeHead) ucSnakeHead = 0;   //超出范围往返循环 
    if (SNAKEMAX == ucSnakeTail)    ucSnakeTail = 0;
    if (SNAKEMAX == ucSnakekLen)   bIsWin = TRUE;//赢了
}

//增加蛇的长度
void AddSnakeLen(POINT ptHead)
{
    vSnakePoint[ucSnakeHead++] = ptHead;//蛇头增长
    ucSnakekLen++;//长度+1
    IsOverTake();//判断是否超出范围
}

//让蛇动起来
BOOL  SnakeMove()
{
    BOOL bRet = FALSE;
    do
    {
        POINT ptNewNode = { 0 };//新的蛇头
        ptNewNode.x = GetSnakeNode(ucSnakekLen - 1).x + ptDirectHead.x;
        ptNewNode.y = GetSnakeNode(ucSnakekLen - 1).y + ptDirectHead.y;
        if (!IsStrikeWall(ptNewNode))//判断新的蛇头是否撞墙
            break;
        if (!IsStrikeSelf(ptNewNode))//判断蛇是否撞到自己
            break;
        if (IsEat(ptNewNode))//判断是否吃到食物
        {
            SetFood();//重新设置食物坐标
            uiScore += 10;//吃到一个食物加10分
            AddSnakeLen(ptNewNode);//增加蛇的长度
            break;
        }
        vSnakePoint[ucSnakeHead].x = ptNewNode.x;//蛇头移动
        vSnakePoint[ucSnakeHead++].y = ptNewNode.y;
        ucSnakeTail++;
        IsOverTake();
        bRet = TRUE;
    } while (FALSE);
    return bRet;
}

//画食物
void DrawFood(HWND hwnd)
{
    HDC hdc = GetDC(hwnd);
    HBRUSH hBrush = CreateSolidBrush(RGB(255, 0, 0));
    SelectObject(hdc, hBrush);
    //椭圆食物
    Ellipse(hdc, BOUNDSIZE + ptFood.x*SNAKESIZE, BOUNDSIZE + ptFood.y*SNAKESIZE
        , BOUNDSIZE + (ptFood.x + 1)*SNAKESIZE, BOUNDSIZE + (ptFood.y + 1)*SNAKESIZE);
    ReleaseDC(hwnd, hdc);
    DeleteObject(hBrush);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    RECT rect = { 0 };
    static int  ixWind = 0, iyWind = 0;//窗口大小参数
    static int ixClient = 0, iyClient = 0;//客户区大小参数
    static HWND  hPause = nullptr;//暂停按钮句柄
    static HWND  hStart = nullptr;//开始按钮句柄
    static HWND  hSpeedUp = nullptr;//加速按钮句柄
    static HWND  hSpeedDown = nullptr;//减速按钮句柄
    DWORD dwError = 0;//错误信息

    switch (message)
    {
       case WM_CREATE:
       {

          //获取窗口大小
          GetWindowRect(hwnd, &rect);
          ixWind = rect.right - rect.left;
          iyWind = rect.bottom - rect.top;
          //获取客户区大小
          GetClientRect(hwnd, &rect);
          ixClient = rect.right - rect.left;
          iyClient = rect.bottom - rect.top;
          //修改窗口大小
          int nWideth = BOUNDSIZE * 3 + (GAME_WIDE + CTRL_WIDE)*SNAKESIZE + (ixWind - ixClient);
          int nHeight = BOUNDSIZE * 2 + GAME_HEIGHT*SNAKESIZE + (iyWind - iyClient);
          MoveWindow(hwnd, 50, 50, nWideth, nHeight, TRUE);
          //初始化蛇身
          InitSnake();
          //创建一个暂停按钮,宽100高50
          hPause = CreateWindowW(L"button", L"Pause", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE + 30, BOUNDSIZE + 20,
            100, 50, hwnd, (HMENU)PAUSEID, hInst, 0);
          //创建一个开始按钮,宽100高50
          hStart = CreateWindowW(L"button", L"Start", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE + 170, BOUNDSIZE + 20,
            100, 50, hwnd, (HMENU)STARTID, hInst, 0);
          //创建一个加速按钮,宽100高50
          hSpeedUp = CreateWindowW(L"button", L"SpeedUp", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE + 30, BOUNDSIZE + 90,
            100, 50, hwnd, (HMENU)SPEEDUPID, hInst, 0);
          //创建一个减速按钮,宽100高50
          hSpeedDown = CreateWindowW(L"button", L"SpeedDown", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE + 170, BOUNDSIZE + 90,
            100, 50, hwnd, (HMENU)SPEEDDOWNID, hInst, 0);
       }

        break;

       case WM_TIMER:

           switch (wParam)
           {
            case TIMERID_1:
            {   //移动蛇
               SnakeMove();
               //判断是否输了
               if (FALSE == bIsLoose)
               {
                   KillTimer(hwnd, TIMERID_1);
                   MessageBox(hwnd, L"you loose!", L"", MB_OK);
                   InitSnake();//初始化蛇
                   bIsLoose = TRUE;
                   SetWindowTextW(hStart, L"Start"); //如果输了就把ReStart变为Start
                   bIsStart = FALSE;//重置开始
                   bFlagStart = FALSE;
               }
               if (TRUE == bIsWin)//判断是否赢了
               {
                   KillTimer(hwnd, TIMERID_1);
                   MessageBox(hwnd, L"you Win!", L"", MB_OK);
                   InitSnake();//初始化蛇
                   bIsWin = FALSE;
               }
               //重新画蛇
               RECT rect = { 0 };
               SetRect(&rect, 0, 0, BOUNDSIZE + GAME_WIDE*SNAKESIZE,
                   BOUNDSIZE + GAME_HEIGHT*SNAKESIZE);
               InvalidateRect(hwnd, &rect, TRUE);
            }
            break;
            default:
               break;
           }

           break;
       case WM_KEYDOWN://控制方向

           if (FALSE == bIsLoose || TRUE == bIsPause)
               return 0;

           switch (wParam)
           {
           case VK_UP:
               if (ptDirectHead.x != 0 && ptDirectHead.y != 1)
               {
                   ptDirectHead.x = 0;
                   ptDirectHead.y = -1;
               }
               break;
           case VK_DOWN:
               if (ptDirectHead.x != 0 && ptDirectHead.y != -1)
               {
                   ptDirectHead.x = 0;
                   ptDirectHead.y = 1;
               }
               break;
           case VK_LEFT:
               if (ptDirectHead.y != 0 && ptDirectHead.x != 1)
               {
                   ptDirectHead.x = -1;
                   ptDirectHead.y = 0;
               }
               break;
           case VK_RIGHT:
               if (ptDirectHead.y != 0 && ptDirectHead.x != -1)
               {
                   ptDirectHead.x = 1;
                   ptDirectHead.y = 0;
               }
               break;
           default:
               break;
           }

           break;

    case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        // 分析菜单选择: 
        switch (wmId)
        {
        case SPEEDUPID://速度加
            if (uiSnakeSpeed >= 0 && bIsStart == TRUE)
            {
                if (100 >= uiSnakeSpeed)
                    uiSnakeSpeed -= 10;
                else
                    uiSnakeSpeed -= 100;
                SetTimer(hwnd, TIMERID_1, uiSnakeSpeed, nullptr);//重新设置 
            }
            SendMessage(hwnd, WM_SETFOCUS, 0, 0);
            break;
        case SPEEDDOWNID://速度减
        {

            if (uiSnakeSpeed <= 1000 && bIsStart == TRUE)
            {
                if (100 > uiSnakeSpeed)
                    uiSnakeSpeed += 10;
                else
                    uiSnakeSpeed += 100;
                SetTimer(hwnd, TIMERID_1, uiSnakeSpeed, nullptr);//重新设置 
            }
            SendMessage(hwnd, WM_SETFOCUS, 0, 0);
        }
        break;
        case STARTID://开始
        {
            if (FALSE == bIsStart && FALSE == bFlagStart)
            {
                SetTimer(hwnd, TIMERID_1, uiSnakeSpeed, nullptr);//设置定时器
                bIsStart = TRUE;//设置为已经开始
                bFlagStart = TRUE;//第一次开始后就设为TREU 表示不再进入这个函数
                SetWindowTextW(hStart, L"ReStart");
                SendMessage(hwnd, WM_SETFOCUS, 0, 0);//把输入焦点还给游戏区
            }
            else
            {
                if (TRUE == bIsPause)
                {//如果已经暂停了,按下重新开始要记得把暂停按钮恢复原样
                    if (!SetWindowTextW(hPause, L"Pause"))
                        dwError = GetLastError();
                    bIsPause = FALSE;
                }
                InitSnake();//初始化,重新开始
                SetTimer(hwnd, TIMERID_1, uiSnakeSpeed, nullptr);
                SendMessage(hwnd, WM_SETFOCUS, 0, 0);//把焦点还给游戏区
            }
        }
        break;
        case PAUSEID://暂停
            if (TRUE == bFlagStart)//游戏进行的时候才能暂停
            {
                if (FALSE == bIsPause)//如果没有暂停则将它暂停
                {
                    KillTimer(hwnd, TIMERID_1);//关掉定时器就是暂停了
                    bIsPause = TRUE;          //把状态设置为暂停
                    if (!SetWindowText(hPause, L"Go on"))//更改按钮标题
                        dwError = GetLastError();

                }
                else
                {//按下继续按钮就重新创建定时器
                    SetTimer(hwnd, TIMERID_1, uiSnakeSpeed, nullptr);
                    bIsPause = FALSE;//把状态设置为没有被暂停
                    if (!SetWindowText(hPause, L"Pause"))//更改按钮标题
                        dwError = GetLastError();
                    SendMessage(hwnd, WM_SETFOCUS, 0, 0);//把焦点还给游戏区
                }
            }

            break;
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
        }
    }
        break;
    case WM_SETFOCUS:
        SetFocus(hwnd);
        break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        static HBRUSH hBrush1 = CreateSolidBrush(RGB(58, 206, 66));
        static HBRUSH hBrush2 = CreateSolidBrush(RGB(80, 87, 196));

        //画游戏区
        Rectangle(hdc, BOUNDSIZE, BOUNDSIZE, BOUNDSIZE + GAME_WIDE*SNAKESIZE,
            BOUNDSIZE + GAME_HEIGHT*SNAKESIZE);
        //填充游戏区颜色
        SetRect(&rect, BOUNDSIZE, BOUNDSIZE, BOUNDSIZE + GAME_WIDE*SNAKESIZE,
            BOUNDSIZE + GAME_HEIGHT*SNAKESIZE);
        FillRect(hdc, &rect, hBrush1);
        //画控制区
        Rectangle(hdc, BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE, BOUNDSIZE,
            BOUNDSIZE * 2 + (GAME_WIDE + CTRL_WIDE)*SNAKESIZE, BOUNDSIZE + CTRL_HEIGHT*SNAKESIZE);
        //填充控制区颜色
        SetRect(&rect, BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE, BOUNDSIZE,
            BOUNDSIZE * 2 + (GAME_WIDE + CTRL_WIDE)*SNAKESIZE,
            BOUNDSIZE + CTRL_HEIGHT*SNAKESIZE);
        FillRect(hdc, &rect, hBrush2);
        //画蛇身
        DrawSnake(hdc);
        //画食物
        DrawFood(hwnd);
        //显示分数
        ShowScore(hwnd);
        EndPaint(hwnd, &ps);
    }
    break;
    case WM_DESTROY:
        KillTimer(hwnd, TIMERID_1);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

你可能感兴趣的:(游戏,编程,windows,Win32,界面)