上几天看了人家用c语言在控制台上面写俄罗斯方块,觉得自己应该能在SDK上写个贪吃蛇出来。毕竟贪吃蛇比俄罗斯方块简单一点吧。
运用知识
1.GUI绘图
2.计时器
3.键盘控制
思路
(一)蛇的产生机制
1.蛇身体的绘制(n个正方形,创建结构体存放4个点坐标,创建结构体数组)
2.运用键盘改变蛇的前进方向(其实改变的只有头尾,并递推改变数组)
(二)食物的产生机制
1.获取当前窗口大小
2.利用随机函数产生x,y为食物(用正方形或圆形函数绘制)的左上角坐标
3.绘制食物
(三)吃食物机制
1.必须验证是蛇头吃到食物
2.吃到食物后食物消失并且重新产生食物(重新产生的食物最好不与蛇的身体重合)
3.蛇的身体的增长(与当前蛇前进的方向有关,数组的扩增)
(四)死亡机制
1.蛇头碰到自己的身体就算死亡
2.蛇头超过窗口就算死亡
因为以前写过模拟钢琴游戏和随机数生成器,所以写起来还是比较顺手的,下面是代码:
#include <windows.h> #include <time.h> #define ID_TIMER 1 struct own_rectangle{ //蛇身体的结构体 int xLeft ; int xRight ; int yTop ; int yBottom ; }rect[255]; struct Snake_Food{ //食物结构体 int x; int y; }Food; int Keyboard_Flag = 1;//键盘标志 int NUM = 3; // 蛇身体长度,默认从3开始 RECT Rect_Window;//获得窗口客户区大小 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("GreedySnake") ; 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, TEXT ("GreedySnake"), 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 ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; int j; switch (message) { case WM_CREATE: SetTimer (hwnd, ID_TIMER, 200, NULL) ;//设置时间 GetClientRect(hwnd, &Rect_Window); //获取窗口大小 rect[0].xLeft = 400 ; //初始化蛇身体 rect[0].xRight = 420 ; rect[0].yTop = 200 ; rect[0].yBottom = 220 ; for (j = 1; j < NUM; j++) { rect[j].xLeft = rect[j-1].xLeft + 20 ; rect[j].xRight = rect[j-1].xRight + 20 ; rect[j].yTop = rect[0].yTop ; rect[j].yBottom = rect[0].yBottom ; } srand(time(0)); Food.x = (rand() % Rect_Window.right) / 20 * 20 ; //保证产生的食物与蛇可以连接 Food.y = (rand() % Rect_Window.bottom) / 20 * 20; return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps); for (j = 0; j < NUM-1; j++) //画蛇 Rectangle(hdc, rect[j].xLeft, rect[j].yTop, rect[j].xRight, rect[j].yBottom); SelectObject(hdc, (HBRUSH)GetStockObject (GRAY_BRUSH)); Rectangle(hdc, rect[NUM-1].xLeft, rect[NUM-1].yTop, rect[NUM-1].xRight, rect[NUM-1].yBottom); Ellipse(hdc, Food.x, Food.y, Food.x+20, Food.y+20);//画食物 EndPaint(hwnd, &ps); return 0; case WM_TIMER: //键盘操作对应改变蛇的形状 //1对应处理向右 if (1 == Keyboard_Flag) { for (j = 0; j < NUM-1; j++) { rect[j].xLeft = rect[j+1].xLeft ; rect[j].xRight = rect[j+1].xRight ; rect[j].yBottom = rect[j+1].yBottom ; rect[j].yTop = rect[j+1].yTop ; } rect[NUM-1].xLeft += 20; rect[NUM-1].xRight += 20; } //2对应处理向下 else if (2 == Keyboard_Flag) { for (j = 0; j < NUM-1; j++) { rect[j].xLeft = rect[j+1].xLeft ; rect[j].xRight = rect[j+1].xRight ; rect[j].yTop = rect[j+1].yTop ; rect[j].yBottom = rect[j+1].yBottom ; } rect[NUM-1].yBottom += 20; rect[NUM-1].yTop += 20; } //3对应处理向左 else if (3 == Keyboard_Flag) { for (j = 0; j < NUM-1; j++) { rect[j].xLeft = rect[j+1].xLeft ; rect[j].xRight = rect[j+1].xRight ; rect[j].yTop = rect[j+1].yTop ; rect[j].yBottom = rect[j+1].yBottom ; } rect[NUM-1].xLeft -= 20; rect[NUM-1].xRight -= 20; } //4对应处理向上 else if (4 == Keyboard_Flag) { for (j = 0; j < NUM-1; j++) { rect[j].xLeft = rect[j+1].xLeft ; rect[j].xRight = rect[j+1].xRight ; rect[j].yTop = rect[j+1].yTop ; rect[j].yBottom = rect[j+1].yBottom ; } rect[NUM-1].yTop -= 20; rect[NUM-1].yBottom -= 20; } //判断蛇头是否吃到食物 if (rect[NUM-1].xLeft == Food.x && rect[NUM-1].yTop == Food.y) { if (1 == Keyboard_Flag)//向右吃到食物 { NUM++; for (j = NUM-1; j > 0 ; j--) { rect[j].xLeft = rect[j-1].xLeft ; rect[j].xRight = rect[j-1].xRight ; rect[j].yTop = rect[j-1].yTop ; rect[j].yBottom = rect[j-1].yBottom ; } rect[0].xLeft -= 20; rect[0].xRight -= 20; Food.x = (rand() % Rect_Window.right) / 20 * 20 ; //蛇吃到后食物坐标改变 Food.y = (rand() % Rect_Window.bottom) / 20 * 20; } else if (2 == Keyboard_Flag)//向下吃到食物 { NUM++; for (j = NUM-1; j > 0 ; j--) { rect[j].xLeft = rect[j-1].xLeft ; rect[j].xRight = rect[j-1].xRight ; rect[j].yTop = rect[j-1].yTop ; rect[j].yBottom = rect[j-1].yBottom ; } rect[0].yTop -= 20; rect[0].yBottom -= 20; Food.x = (rand() % Rect_Window.right) / 20 * 20 ; //蛇吃到后食物坐标改变 Food.y = (rand() % Rect_Window.bottom) / 20 * 20; } else if (3 == Keyboard_Flag)//向左吃到食物 { NUM++; for (j = NUM-1; j > 0 ; j--) { rect[j].xLeft = rect[j-1].xLeft ; rect[j].xRight = rect[j-1].xRight ; rect[j].yTop = rect[j-1].yTop ; rect[j].yBottom = rect[j-1].yBottom ; } rect[0].xLeft += 20; rect[0].xRight += 20; Food.x = (rand() % Rect_Window.right) / 20 * 20 ; //蛇吃到后食物坐标改变 Food.y = (rand() % Rect_Window.bottom) / 20 * 20; } else if (4 == Keyboard_Flag)//向上吃到食物 { NUM++; for (j = NUM-1; j > 0 ; j--) { rect[j].xLeft = rect[j-1].xLeft ; rect[j].xRight = rect[j-1].xRight ; rect[j].yTop = rect[j-1].yTop ; rect[j].yBottom = rect[j-1].yBottom ; } rect[0].yTop += 20; rect[0].yBottom += 20; Food.x = (rand() % Rect_Window.right) / 20 * 20 ; //蛇吃到后食物坐标改变 Food.y = (rand() % Rect_Window.bottom) / 20 * 20; } } //SnakeBody_Longer(&rect, NUM); //死亡机制 //触碰边界而死 if (rect[NUM-1].xLeft < 0 || rect[NUM-1].xRight > Rect_Window.right || rect[NUM-1].yTop < 0 ||rect[NUM-1].yBottom > Rect_Window.bottom ) { KillTimer(hwnd, ID_TIMER); MessageBox(hwnd, TEXT("Game Over!"), TEXT("Sorry,You Died, Never Give Up!"), MB_OK); return 0; } //触碰自己而死 for (j = 0; j < NUM-1; j++) { if (rect[NUM-1].xLeft == rect[j].xLeft && rect[NUM-1].xRight == rect[j].xRight && rect[NUM-1].yTop == rect[j].yTop && rect[NUM-1].yBottom == rect[j].yBottom) { KillTimer(hwnd, ID_TIMER); MessageBox(hwnd, TEXT("Game Over!"), TEXT("Sorry,You Died, Never Give Up!"), MB_OK); return 0; } } InvalidateRect(hwnd, NULL, TRUE); return 0; case WM_KEYDOWN: //处理键盘消息 switch(wParam) { case VK_LEFT: Keyboard_Flag = 3; break; case VK_RIGHT: Keyboard_Flag = 1; break; case VK_UP: Keyboard_Flag = 4; break; case VK_DOWN: Keyboard_Flag = 2; break; } return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }这里我用 NUM 表示蛇身体的长度
rect[num-1]表示的是蛇头,很多处理都用到了这个。
写的时候可能代码有点乱,是按照顺序写下来的,关键代码都放在VM_TIMER里面了
也没有写函数,所以看起来确实有点乱的。
具体的话写了点注释,应该不难理解吧。
还有不完善的地方可以修改:
1.身体增长机制
我是根据方向在尾部增长的,有时候看起来比较突兀,这里可以改进
2食物产生机制
产生食物的时候食物可能会落在窗口外面,这个时候蛇无论如何也吃不到食物了,这里有个BUG
3.难度控制
可以写个菜单,调节蛇的速度
4.窗口应该设置为固定的
5.结束机制
结束后选择重新玩,或者退出游戏
不过这个只是实现了基本功能,还不能称为游戏。我觉得应该把它称作 "移动和增长方块"比较合适吧
如果想称之为游戏,那必须还得添加一些可玩因素,比如
1.积分机制
2.过关机制
3.蛇的移动速度调节
4.食物的种类,不同类型食物可以增加身体长度不同,增加分数不同
5.背景音乐
6.把蛇画的好看点
7.其他的创意因子
暂时想到的可玩因素就只有这些了。大家有兴趣的话也可以去写个玩玩。最后说明,偶只是个新手,高手勿喷,呵呵