【Chapter 3】基于Chp2的贪吃蛇的例子

来源:互联网 发布:机房 蓝屏 网络攻击 编辑:程序博客网 时间:2024/05/16 00:38

稍微看了下

还是好难啊啊啊啊啊QAQ

Please请对我好一点啊啊啊啊啊TAT

 

放代码慢慢分析

代码依旧是有俱乐部培训的师兄提供

什么时候能自己写啊[望天]

代码渣画画渣读书废

都不知道怎么有勇气活到现在的是么嗯哼?

 

会写注释的程序员都是好男人/女人

#include <windows.h>#include <list>#include <stdlib.h>#include <time.h>#define LEFT 0#define RIGHT 1#define UP 2#define DOWN 3#define COL_NUM 20#define ROW_NUM 20#define BLOCK_SIZE 25void Game_Init(HWND hwmd);void SetFood();void Game_Exit();void Game_Render(HDC hdc);bool Game_Update(HWND hwmd);struct Block//For every little block{int col;int row;bool operator== (Block& b) {return col == b.col && row == b.row;}bool operator!= (Block& b) {return !((*this) == b);}};std::list<Block> snake;Block food;int direction;HBRUSH redBrush, orangeBrush, whiteBrush;inline void Fill_Block(HDC hdc, Block& b);/*  Declare Windows procedure  */LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);/*  Make the class name into a global variable  */int WINAPI WinMain (HINSTANCE hInstance,                    HINSTANCE hPrevInstance,                    LPSTR lpszArgument,                    int iCmdShow){    HWND hwnd;               /* This is the handle for our window */    MSG msg;            /* Here messages to the application are saved */    WNDCLASS wndclass;        /* Data structure for the windowclass */static char szClassName[ ] = "WindowsApp";    /* 第一步:注册窗口类 */    wndclass.hInstance = hInstance;    wndclass.lpszClassName = szClassName;    wndclass.lpfnWndProc = WindowProcedure;      /* This function is called by windows */    wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;                 /* Catch double-clicks */    wndclass.cbWndExtra = 0;wndclass.cbClsExtra = 0;wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);wndclass.lpszMenuName = NULL;    /* Register the window class, and if it fails quit the program */    if (!RegisterClass (&wndclass))        return 0;    /* 第二步:创建窗口 */hwnd = CreateWindow(szClassName, TEXT("MyApp"), WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,// initial x positionCW_USEDEFAULT,// initial y position       600,// initial x size       600,// initial y sizeNULL,NULL,hInstance,NULL);    /* 第三步:显示窗口 */    ShowWindow (hwnd, iCmdShow);UpdateWindow(hwnd);    /* 第四步:消息循环 */    while (GetMessage (&msg, NULL, 0, 0))    {        /* Translate virtual-key messages into character messages */        TranslateMessage(&msg);        /* Send message to WindowProcedure */        DispatchMessage(&msg);    }    /* The program return-value is 0 - The value that PostQuitMessage() gave */    return msg.wParam;}/*  This function is called by the Windows function DispatchMessage()  */LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){HDC hdc;PAINTSTRUCT ps;    switch (message)                  /* handle the messages */    {case WM_CREATE:Game_Init(hwnd);return 0;case WM_DESTROY:Game_Exit();PostQuitMessage (0);       /* send a WM_QUIT to the message queue */break;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);Game_Render(hdc);EndPaint(hwnd, &ps);return 0;case WM_TIMER:if(wParam == 1) {if(!Game_Update(hwnd)) {KillTimer(hwnd, 1);MessageBox(hwnd, "Game Over!", NULL, 0);DestroyWindow(hwnd);}}return 0;case WM_KEYDOWN:if(wParam == VK_LEFT && direction != RIGHT) direction = LEFT;if(wParam == VK_RIGHT && direction != LEFT) direction = RIGHT;if(wParam == VK_UP && direction != DOWN) direction = UP;if(wParam == VK_DOWN && direction != UP) direction = DOWN;return 0;default:                      /* for messages that we don't deal with */return DefWindowProc (hwnd, message, wParam, lParam);    }    return 0;}void Game_Init(HWND hwmd){srand(time(NULL));direction = LEFT;Block b;b.row = 9;b.col = 9;int i;for (i = 0; i < 7; i++){snake.push_back(b);b.col++;}SetFood();//create some brushesHDC hdc = GetDC(hwmd);redBrush = (HBRUSH)CreateSolidBrush(RGB(255, 0, 0));orangeBrush = (HBRUSH)CreateSolidBrush(RGB(255, 102, 0));whiteBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);ReleaseDC(hwmd, hdc);SetTimer(hwmd, 1, 200, NULL);}void SetFood(){while(true) {food.row = rand() % ROW_NUM;food.col = rand() % COL_NUM;//if food is not in snake position, quit loopstd::list<Block>::iterator it = snake.begin();for (; it != snake.end(); it++){if(food == (*it)) {break;}}if(it == snake.end()) break;}}void Game_Exit(){DeleteObject(redBrush);DeleteObject(orangeBrush);}void Game_Render(HDC hdc){SelectObject(hdc, (HPEN)GetStockObject(NULL_PEN));//draw white areaSelectObject(hdc, whiteBrush);Rectangle(hdc, 0, 0, COL_NUM * BLOCK_SIZE, ROW_NUM * BLOCK_SIZE);//draw snakeSelectObject(hdc, redBrush);std::list<Block>::iterator it = snake.begin();for (; it != snake.end(); it++){Fill_Block(hdc, *it);}//draw foodSelectObject(hdc, orangeBrush);Fill_Block(hdc, food);}void Fill_Block(HDC hdc, Block& b){Rectangle(hdc, b.col * BLOCK_SIZE, b.row * BLOCK_SIZE, (b.col + 1) * BLOCK_SIZE, (b.row + 1) * BLOCK_SIZE);}bool Game_Update(HWND hwmd){Block head = snake.front();switch(direction){case LEFT:head.col--;if(head.col < 0) return false;break;case RIGHT:head.col++;if(head.col >= COL_NUM) return false;break;case UP:head.row--;if(head.row < 0) return false;break;case DOWN:head.row++;if(head.row >= ROW_NUM) return false;break;}std::list<Block>::iterator it = snake.begin();for (; it != snake.end(); it++){if(head == *it)return false;}snake.push_front(head);HDC hdc = GetDC(hwmd);SelectObject(hdc, (HPEN)GetStockObject(NULL_PEN));SelectObject(hdc, redBrush);Fill_Block(hdc, head);if(head == food) {SetFood();SelectObject(hdc, orangeBrush);Fill_Block(hdc, food);}else {SelectObject(hdc, whiteBrush);Block tail = snake.back();snake.pop_back();Fill_Block(hdc, tail);}ReleaseDC(hwmd, hdc);return true;}


有一个类block

定义了每个小block的位置(row和col)

下面又为snake定义了<block>list,为food定义了一个block

 

WinMain函数里面没有什么大变动

主要看窗口过程函数里面的操作。

 

首先理解几个函数的意思:

void Game_Init(HWND hwmd){srand(time(NULL));direction = LEFT;Block b;b.row = 9;b.col = 9;int i;for (i = 0; i < 7; i++){snake.push_back(b);b.col++;}SetFood();//create some brushesHDC hdc = GetDC(hwmd);redBrush = (HBRUSH)CreateSolidBrush(RGB(255, 0, 0));orangeBrush = (HBRUSH)CreateSolidBrush(RGB(255, 102, 0));whiteBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);ReleaseDC(hwmd, hdc);SetTimer(hwmd, 1, 200, NULL);}


 

大抵上是游戏刚开始的时候定义了一条在位置坐标(9,9)的方向向左的一条长度为7个格子的snake

和setfood放置食物(另一个函数)

还定义了各种刷子brush的RGB颜色值

 

ReleaseDC是释放窗口

使用GetDC或者GetWindowDC等API时,会向系统检索设备上下文环境,换句话说,就是系统动态分配了资源让你可以拥有对这个设备(这个设备可以是屏幕、窗口、客户区域等)一定的控制权,比如绘图。

使用完毕后,这个动态分配的资源应该还给系统,于是要ReleaseDC。有借有还,再借不难。有借无还,系统玩完。

函数原型:int ReleaseDC(HWND hWnd, HDC hdc);
参数:
hWnd:指向要释放的设备上下文环境所在的窗口的句柄。
hDC:指向要释放的设备上下文环境的句柄。
返回值:返回值说明了设备上下文环境是否释放;如果释放成功,则返回值为1;如果没有释放成功,则返回值为0。

 

还设置了一个时钟

 

SetTimer函数用于创建一个计时器,KillTimer函数用于销毁一个计时器。计时器属于系统资源,使用完应及时销毁。

 

SetTimer的函数原型如下:
UINT_PTR SetTimer( HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc ) ;


其中:
hWnd是和timer关联的窗口句柄,此窗口必须为调用SetTimer的线程所有;如果hWnd为NULL,没有窗口和timer相关联并且nIDEvent参数被忽略


nIDEvent是timer的标识,为非零值;如果hWnd为NULL则被忽略;如果hWnd非NULL而且与timer相关联的窗口已经存在一个为此标识的timer,则此次SetTimer调用将用新的timer代替原来的timer。timer标识和窗口相关,两个不同的窗口可以拥有nIDEvent相同的tiemr


uElapse是以毫秒指定的计时间隔值,范围为1毫秒到4,294,967,295毫秒(将近50天),这个值指示Windows每隔多久时间给程序发送WM_TIMER消息。

 
lpTimerFunc是一个回调函数的指针,俗称TimerFunc;如果lpTimerFunc为NULL,系统将向应用程序队列发送WM_TIMER消息;如果lpTimerFunc指定了一个值,DefWindowProc将在处理WM_TIMER消息时调用这个lpTimerFunc所指向的回调函数,因此即使使用TimerProc代替处理WM_TIMER也需要向窗口分发消息。

关于SetTimer的返回值:如果hWnd为NULL,返回值为新建立的timer的ID,如果hWnd非NULL,返回一个非0整数,如果SetTimer调用失败则返回0

 

KillTimer的函数原型为:BOOL KillTimer( HWND hWnd, UINT_PTR uIDEvent ) ; 参数意义同SetTimer。

 

void SetFood(){while(true) {food.row = rand() % ROW_NUM;food.col = rand() % COL_NUM;//if food is not in snake position, quit loopstd::list<Block>::iterator it = snake.begin();for (; it != snake.end(); it++){if(food == (*it)) {break;}}if(it == snake.end()) break;}}


 

一起来看SetFood()

即随机找个位置个food,但是要满足不在snake体内的条件。

若在snake体内则跳出这个循环。

 

void Game_Exit()即为游戏退出

销毁格子

 

void Game_Render(HDC hdc){SelectObject(hdc, (HPEN)GetStockObject(NULL_PEN));//draw white areaSelectObject(hdc, whiteBrush);Rectangle(hdc, 0, 0, COL_NUM * BLOCK_SIZE, ROW_NUM * BLOCK_SIZE);//draw snakeSelectObject(hdc, redBrush);std::list<Block>::iterator it = snake.begin();for (; it != snake.end(); it++){Fill_Block(hdc, *it);}//draw foodSelectObject(hdc, orangeBrush);Fill_Block(hdc, food);}


 

这个看名字看不出来干嘛的- -

看备注是构造这个游戏世界的意思么

刚刚是构造了snake和food

这个是把snake和food都用特定颜色填充到世界里

 

void Fill_Block(HDC hdc, Block& b){Rectangle(hdc, b.col * BLOCK_SIZE, b.row * BLOCK_SIZE, (b.col + 1) * BLOCK_SIZE, (b.row + 1) * BLOCK_SIZE);}


 

Rectangle是画矩形函数

函数名: rectangle
功 能: 画一个矩形
用 法: void far rectangle(int left, int top, int right, int bottom);
参数说明:(left ,top )为矩形的左上坐标,(right,bottom)为矩形的右下坐标,两者可确定一个矩形的大小

 

bool Game_Update(HWND hwmd){Block head = snake.front();switch(direction){case LEFT:head.col--;if(head.col < 0) return false;break;case RIGHT:head.col++;if(head.col >= COL_NUM) return false;break;case UP:head.row--;if(head.row < 0) return false;break;case DOWN:head.row++;if(head.row >= ROW_NUM) return false;break;}std::list<Block>::iterator it = snake.begin();for (; it != snake.end(); it++){if(head == *it)return false;}snake.push_front(head);HDC hdc = GetDC(hwmd);SelectObject(hdc, (HPEN)GetStockObject(NULL_PEN));SelectObject(hdc, redBrush);Fill_Block(hdc, head);if(head == food) {SetFood();SelectObject(hdc, orangeBrush);Fill_Block(hdc, food);}else {SelectObject(hdc, whiteBrush);Block tail = snake.back();snake.pop_back();Fill_Block(hdc, tail);}ReleaseDC(hwmd, hdc);return true;}


 

Game_Update应该是贪吃蛇的核心。即snake的移动。

键盘上的上下左右定义成0,1,2,3来识别方向。

当吃到food的时候,尾巴的那一格red格子不变成白色的格子。

 

===============================================

 

分析完了

但是自己写还是不会啊怎么破!!!!

一个上午了分析了一章ppt

还有许许多多的ppt

今早7点半起~

果然比睡到12点然后再捶胸敦子心情舒畅一些

刚宿舍还合力杀死了一只超级大的蟑螂QAQ吓死

 

总之今天可以奖励自己一下么

肚子还疼着TAT