#include <windows.h> #include <time.h> #include <stdlib.h> //定义4个方向 #define UP 1 //向上,蛇头y坐标不断减小 #define DOWN 2 //向下,蛇头y坐标不断增大 #define LEFT 3 //向左,蛇头x坐标不断减小 #define RIGHT 4 //向右,蛇头x坐标不断增大 #define SNAKEWIDTH 10 //单节蛇的大小 #define XWIDTH 20 //游戏区的宽度 #define YHEIGHT 20 //游戏区的高度 #define GETSCORE 10 //吃一个食物的得分 #define ID_TIMER 1 //定时器ID #define TIMERSET 600 //定时间隔 static int direct = RIGHT; //方向(注:方向由蛇头决定) static int Score; //吃到的食物数量 bool IsOver = false, bEat = false; struct Snake { Snake():next(NULL), before(NULL) { pt.x = 0; pt.y = 0; } Snake(const POINT &npt, Snake *, Snake *); POINT pt; Snake * next, * before; } snake; //定义蛇头 Snake::Snake(const POINT &npt, Snake * nnext, Snake * nbefore): pt(npt), next(nnext), before(nbefore){} Snake * psLast = NULL, * food = NULL; bool GameClient[XWIDTH][YHEIGHT]; //定义游戏区坐标布尔值,表示相应点的蛇身以及是否有食物。 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { TCHAR szAppName[] = TEXT("Snake"), szWindowName[] = TEXT("贪吃蛇 by 阿卡(QQ:8596801)"); WNDCLASS wndcls; wndcls.cbClsExtra = 0; wndcls.cbWndExtra = 0; wndcls.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wndcls.hCursor = LoadCursor(hInstance, IDC_ARROW); wndcls.hIcon = LoadIcon(hInstance, IDI_APPLICATION); wndcls.hInstance = hInstance; wndcls.lpfnWndProc = WndProc; wndcls.lpszClassName = szAppName; wndcls.lpszMenuName = NULL; wndcls.style = CS_HREDRAW | CS_VREDRAW; RegisterClass(&wndcls); HWND hwnd = CreateWindow(szAppName, szWindowName, WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, (XWIDTH + 10) * SNAKEWIDTH, (YHEIGHT + 5) * SNAKEWIDTH, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); MSG msg; while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //新食物刷新 void NewFood(HWND hwnd) { food = new Snake(); SYSTEMTIME st; GetLocalTime(&st); srand(st.wMilliseconds); food->pt.x = rand() % XWIDTH, food->pt.y = rand() % YHEIGHT; while(GameClient[food->pt.x][food->pt.y]) { GetLocalTime(&st); srand(st.wMilliseconds); food->pt.x = rand() % XWIDTH, food->pt.y = rand() % YHEIGHT; } GameClient[food->pt.x][food->pt.y] = true; InvalidateRect(hwnd, NULL, TRUE); } //判断前方是否是食物 inline bool IsFood() { if(snake.pt.x == food->pt.x && snake.pt.y == food->pt.y) return true; else return false; } //判断是否撞墙或者撞到自己身体 bool TouchWall() { Snake * temp = psLast; //超越边界判断 if(snake.pt.x >= XWIDTH || snake.pt.x < 0 || snake.pt.y < 0 || snake.pt.y >= YHEIGHT) return true; //碰撞自身身体判断 while(temp != &snake) { if(snake.pt.x == temp->pt.x && snake.pt.y == temp->pt.y) return true; temp = temp->before; } return false; } void Move(HWND hwnd) { int x, y; //用于记录蛇头的当前位置(未前进时) //标记前方是否为食物,默认为不是食物,表示系通常的前进 Snake * temp = psLast; //记录蛇头当前坐标 x = snake.pt.x; y = snake.pt.y; //测试前进的地方是否是食物,同时将蛇头前进 switch(direct) { case UP: --snake.pt.y; break; case DOWN: ++snake.pt.y; break; case LEFT: --snake.pt.x; break; case RIGHT: ++snake.pt.x; break; default: break; } //如果不是食物,作如下处理 if(!IsFood()) { //最后一节不是蛇头 if(temp != &snake) { GameClient[temp->pt.x][temp->pt.y] = false; while(temp != snake.next) { temp->pt.x = temp->before->pt.x; temp->pt.y = temp->before->pt.y; temp = temp->before; } temp->pt.x = x; temp->pt.y = y; GameClient[temp->pt.x][temp->pt.y] = true; } else //psLast == &snake,即蛇只有蛇头 GameClient[x][y] = false; if(TouchWall()) IsOver = true; //则标记游戏结束 else GameClient[snake.pt.x][snake.pt.y] = true; //不然移动后的蛇头位置标记为有方块了 InvalidateRect(hwnd, NULL, TRUE); //刷新游戏区 } //如果是食物,作如下处理 else { ++Score; food->before = psLast; psLast->next = food; if(temp != &snake) { food->pt.x = psLast->pt.x; food->pt.y = psLast->pt.y; while(temp != snake.next) { temp->pt.x = temp->before->pt.x; temp->pt.y = temp->before->pt.y; temp = temp->before; } temp->pt.x = x; temp->pt.y = y; } else //psLast == &snake,即蛇只有蛇头 { food->pt.x = x; food->pt.y = y; } psLast = food; NewFood(hwnd); } } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; TEXTMETRIC tm; static int cxChar, cyChar; TCHAR szScore[] = TEXT("得分:"), szGameOver[] = TEXT("游戏结束!"), szPause[] = TEXT("游戏暂停!"), szBuffer[20], *szText = NULL; int x, y; static bool pause = false; switch(message) { case WM_CREATE: direct = RIGHT; //游戏开局,默认方向向右 snake.pt.x = XWIDTH / 2; //默认显示在游戏区中间位置 snake.pt.y = YHEIGHT / 2; psLast = &snake; GameClient[snake.pt.x][snake.pt.y] = true; hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); cyChar = tm.tmExternalLeading + tm.tmHeight; ReleaseDC(hwnd, hdc); NewFood(hwnd); SetTimer(hwnd, ID_TIMER, TIMERSET, NULL); return 0; case WM_TIMER: if(pause) return 0; Move(hwnd); if(IsOver) { KillTimer(hwnd, ID_TIMER); InvalidateRect(hwnd, NULL, TRUE); } return 0; case WM_KEYDOWN: if(IsOver || pause) return 0; switch(wParam) { case VK_UP: if(direct != DOWN) { direct = UP; Move(hwnd); } break; case VK_DOWN: if(direct != UP) { direct = DOWN; Move(hwnd); } break; case VK_LEFT: if(direct != RIGHT) { direct = LEFT; Move(hwnd); } break; case VK_RIGHT: if(direct != LEFT) { direct = RIGHT; Move(hwnd); } break; default: break; } return 0; case WM_CHAR: if(IsOver && wParam != 'r') return 0; switch(wParam) { case 'p': { pause = !pause; InvalidateRect(hwnd, NULL, TRUE); } break; case 'r': { for(int x = 0; x < XWIDTH; ++x) for(int y = 0; y < YHEIGHT; ++y) GameClient[x][y] = false; } SetTimer(hwnd, ID_TIMER, TIMERSET, NULL); direct = RIGHT; //游戏开局,默认方向向右 snake.pt.x = XWIDTH / 2; //默认显示在游戏区中间位置 snake.pt.y = YHEIGHT / 2; psLast = &snake; IsOver = false; pause = false; GameClient[snake.pt.x][snake.pt.y] = true; NewFood(hwnd); break; default: break; } return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); SetViewportOrgEx(hdc, SNAKEWIDTH, SNAKEWIDTH, NULL); //画游戏区的边框 MoveToEx(hdc, -1, -1, NULL); LineTo(hdc, XWIDTH * SNAKEWIDTH + 1, -1); LineTo(hdc, XWIDTH * SNAKEWIDTH + 1, YHEIGHT * SNAKEWIDTH + 1); LineTo(hdc, -1, YHEIGHT * SNAKEWIDTH + 1); LineTo(hdc, -1, -1); TextOut(hdc, (XWIDTH + 1) * SNAKEWIDTH, 0, szScore, lstrlen(szScore)); TextOut(hdc, (XWIDTH + 1) * SNAKEWIDTH, cyChar, szBuffer, wsprintf(szBuffer, TEXT("%4d"), Score)); if(pause) TextOut(hdc, (XWIDTH + 1) * SNAKEWIDTH, 3 * cyChar, szPause, lstrlen(szPause)); else if(IsOver) TextOut(hdc, (XWIDTH + 1) * SNAKEWIDTH, 3 * cyChar, szGameOver, lstrlen(szGameOver)); SelectObject(hdc, GetStockObject(BLACK_BRUSH)); for(x = 0; x < XWIDTH; ++x) { for(y = 0; y < YHEIGHT; ++y) { if(GameClient[x][y]) Rectangle(hdc, x * SNAKEWIDTH, y * SNAKEWIDTH, (x + 1) * SNAKEWIDTH, (y + 1) * SNAKEWIDTH); } } return 0; case WM_DESTROY: KillTimer(hwnd, ID_TIMER); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }