赞
踩
贪吃蛇是一款经典的小游戏,一条小蛇在一个区域里自由游走,目的是吃掉随机出现的食物,每吃掉一个食物蛇的身体会变长一点,如果在移动过程中蛇头碰到墙壁或者自己的身体游戏结束。所以吃掉的食物越多,游戏将变得更难**。**
功能模块介绍
程序入口为WinMain函数,
函数中实现创建Windows
窗口功能,获取并分发消息。
处理流程如图。
功能设计
功能设计
功能设计
功能设计
#include <windows.h>
#include <time.h>
#include <stdlib.h>
#include <conio.h>
/*定义4个方向*/
#define UP 1 /*向上,蛇头y坐标不断减小*/
#define DOWN 2 /*向下,蛇头y坐标不断增大*/
#define LEFT 3 /*向左,蛇头x坐标不断减小*/
#define RIGHT 4 /*向右,蛇头x坐标不断增大*/
#define SNAKE_WIDTH 20 /*单节蛇的大小*/
#define X_WIDTH 20 /*游戏区的宽度*/
#define Y_HEIGHT 20 /*游戏区的高度*/
#define GETSCORE 10 /*吃一个食物的得分*/
#define ID_TIMER 1 /*定时器ID*/
#define TIMERSET 600 /*定时间隔*/
#define FALSE 0
#define TRUE 1
#define APP_NAME "Snake"
#define APP_TITLE TEXT("贪吃蛇")
#define P 80
#define R 82
static int iDirect = RIGHT; /*方向(注:方向由蛇头决定)*/
static int iScore; /*吃到的食物数量*/
int iIsOver = FALSE;
struct Snake
{
POINT point;
struct Snake *pstNext, *pstBefore;
} stSnake; /*定义蛇头*/
struct Snake * pstLast = NULL, *pstFood = NULL;
int aiPointExist[X_WIDTH][Y_HEIGHT]; /*定义游戏区坐标布尔值,表示相应点的蛇身以及是否有食物。*/
int aiFood[X_WIDTH][Y_HEIGHT]; /*定义了食物的坐标*/
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASS wndcls; /*WNDCLASS结构变量,包含窗口类全部信息*/
/*设置窗口样式*/
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 = APP_NAME; /*定义窗口类名*/
wndcls.lpszMenuName = NULL; /*窗口无菜单*/
wndcls.style = CS_HREDRAW | CS_VREDRAW; /*当窗口水平或垂直宽度变化时重绘窗口*/
RegisterClass(&wndcls); /*注册窗口类*/
/*创建Windows窗口*/
HWND hwnd = CreateWindow(APP_NAME, APP_TITLE,
WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT, (X_WIDTH + 15) * SNAKE_WIDTH, (Y_HEIGHT + 5) * SNAKE_WIDTH,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow); /*显示窗口*/
UpdateWindow(hwnd); /*绘制用户区*/
/*判断是否有Windows消息*/
while (GetMessage(&msg, NULL, 0, 0)) /*从调用线程的消息队列中取出一条消息*/
{
TranslateMessage(&msg); /*对"消息对"的转化*/
DispatchMessage(&msg); /*将取出的消息传到窗口的回调函数去处理*/
}
return msg.wParam;
}
/*新食物刷新*/
void NewFood(HWND hwnd)
{
pstFood = (struct Snake *)malloc(sizeof(struct Snake));
SYSTEMTIME st;
/*在窗口位置内随机产生新的食物*/
GetLocalTime(&st); /*获取当地的当前系统日期和时间*/
srand(st.wMilliseconds);
pstFood->point.x = rand() % X_WIDTH;
pstFood->point.y = rand() % Y_HEIGHT;
/*判断新食物位置是否是蛇身位置*/
while (aiPointExist[pstFood->point.x][pstFood->point.y])
{
GetLocalTime(&st);
srand(st.wMilliseconds);
pstFood->point.x = rand() % X_WIDTH;
pstFood->point.y = rand() % Y_HEIGHT;
}
aiPointExist[pstFood->point.x][pstFood->point.y] = TRUE;
InvalidateRect(hwnd, NULL, TRUE); /*重绘窗口区域*/
}
/*判断前方是否是食物*/
int IsFood()
{
if (stSnake.point.x == pstFood->point.x && stSnake.point.y == pstFood->point.y)
return TRUE;
else
return FALSE;
}
/*判断是否撞墙或者撞到自己身体*/
int TouchWall()
{
struct Snake *pstTemp = pstLast;
/*超越边界判断*/
if (stSnake.point.x >= X_WIDTH ||
stSnake.point.x < 0 ||
stSnake.point.y < 0 ||
stSnake.point.y >= Y_HEIGHT)
return TRUE;
/*碰撞自身身体判断*/
while (pstTemp != &stSnake)
{
if (stSnake.point.x == pstTemp->point.x &&
stSnake.point.y == pstTemp->point.y)
return TRUE;
pstTemp = pstTemp->pstBefore;
}
return FALSE;
}
void Move(HWND hwnd)
{
int iPosition_x, iPosition_y; /*用于记录蛇头的当前位置(未前进时)*/
/*标记前方是否为食物,默认为不是食物,表示系通常的前进*/
struct Snake * pstTemp = pstLast;
/*记录蛇头当前坐标*/
iPosition_x = stSnake.point.x;
iPosition_y = stSnake.point.y;
/*测试前进的地方是否是食物,同时将蛇头前进*/
switch (iDirect)
{
case UP:
--stSnake.point.y;
break;
case DOWN:
++stSnake.point.y;
break;
case LEFT:
--stSnake.point.x;
break;
case RIGHT:
++stSnake.point.x;
break;
default:
break;
}
if (!IsFood())/*如果不是食物,作如下处理*/
{
if (pstTemp != &stSnake)/*最后一节不是蛇头*/
{
aiPointExist[pstTemp->point.x][pstTemp->point.y] = FALSE;/*由于蛇要前进,原蛇尾位置标记为FALSE*/
/*蛇尾到蛇头位置节点对应位置分别标记为TRUE*/
while (pstTemp != stSnake.pstNext)
{
pstTemp->point.x = pstTemp->pstBefore->point.x;
pstTemp->point.y = pstTemp->pstBefore->point.y;
pstTemp = pstTemp->pstBefore;
}
pstTemp->point.x = iPosition_x;
pstTemp->point.y = iPosition_y;
aiPointExist[pstTemp->point.x][pstTemp->point.y] = TRUE;
}
else /*pstLast == &snake,即蛇只有蛇头*/
aiPointExist[iPosition_x][iPosition_y] = FALSE;
if (TouchWall())
iIsOver = TRUE; /*则标记游戏结束*/
else
aiPointExist[stSnake.point.x][stSnake.point.y] = TRUE; /*不然移动后的蛇头位置标记为有方块了*/
InvalidateRect(hwnd, NULL, TRUE); /*刷新游戏区*/
}
/*如果是食物,作如下处理*/
else
{
++iScore;
pstFood->pstBefore = pstLast;
pstLast->pstNext = pstFood;
if (pstTemp != &stSnake)
{
pstFood->point.x = pstLast->point.x;
pstFood->point.y = pstLast->point.y;
while (pstTemp != stSnake.pstNext)
{
pstTemp->point.x = pstTemp->pstBefore->point.x;
pstTemp->point.y = pstTemp->pstBefore->point.y;
pstTemp = pstTemp->pstBefore;
}
pstTemp->point.x = iPosition_x;
pstTemp->point.y = iPosition_y;
}
else /*pstLast == &snake,即蛇只有蛇头*/
{
pstFood->point.x = iPosition_x; pstFood->point.y = iPosition_y;
}
pstLast = pstFood;
NewFood(hwnd);
}
}
void startGame(HWND hwnd)
{
int x, y;
for (x = 0; x < X_WIDTH; ++x)
for (y = 0; y < Y_HEIGHT; ++y)
aiPointExist[x][y] = FALSE;/*初始化变量为FALSE,表示没有食物和蛇*/
aiFood[x][y] = FALSE;
iDirect = RIGHT; /*游戏开局,设定蛇的默认方向向右*/
/*在游戏区中间位置生成长度为1的蛇*/
stSnake.point.x = X_WIDTH / 2;
stSnake.point.y = Y_HEIGHT / 2;
pstLast = &stSnake;
iIsOver = FALSE;/*将游戏结束变量设为FALSE*/
aiPointExist[stSnake.point.x][stSnake.point.y] = TRUE;/*将新生成蛇的位置的aiPointExist变量设为TRUE*/
NewFood(hwnd);/*随机生成一个食物*/
SetTimer(hwnd, ID_TIMER, TIMERSET, NULL);/*设置计数器*/
}
void keyDown(HWND hwnd, WPARAM wParam, int iPause)
{
if (!iPause&&!iIsOver)/*游戏运行时,才可以进行运行控制*/
{
switch (wParam)
{
case VK_UP:
if (iDirect != DOWN)/*更改的移动方向不能与原方向相反*/
{
iDirect = UP;/*改变当前移动方向*/
Move(hwnd);/*移动*/
}
break;
case VK_DOWN:
if (iDirect != UP)
{
iDirect = DOWN;
Move(hwnd);
}
break;
case VK_LEFT:
if (iDirect != RIGHT)
{
iDirect = LEFT;
Move(hwnd);
}
break;
case VK_RIGHT:
if (iDirect != LEFT)
{
iDirect = RIGHT;
Move(hwnd);
}
break;
default:
break;
}
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
HBRUSH hBrush;
static int cxChar, cyChar;
TCHAR szScore[] = TEXT("得分:"),
szInfor_1[] = TEXT("========游戏说明========"),
szInfor_2[] = TEXT("1.按方向键移动小蛇;"),
szInfor_3[] = TEXT("2.按P暂停游戏;"),
szInfor_4[] = TEXT("2.按R重新开始游戏;"),
szInfor_5[] = TEXT("3.蛇头撞到墙,结束游戏;"),
szInfor_6[] = TEXT("4.蛇头碰到蛇身,游戏结束;"),
szInfor_7[] = TEXT("5.每吃一个米粒得1分"),
szInfor_8[] = TEXT("========================"),
szInfor_9[] = TEXT("========================"),
szInfor_10[] = TEXT(" 您的得分是:"),
szInfor_11[] = TEXT("======================="),
szGameOver[] = TEXT("游戏结束!"),
szPause[] = TEXT("游戏暂停!"),
szBuffer[X_WIDTH],
*szText = NULL;
int x, y;
static int iPause = FALSE;
switch (message)
{
case WM_CREATE:
hdc = GetDC(hwnd);/*返回窗口客户区的设备上下文环境*/
GetTextMetrics(hdc, &tm);/*该函数把程序当前的字体信息,存放到TEXTMETRIC*/
cyChar = tm.tmExternalLeading + tm.tmHeight;
ReleaseDC(hwnd, hdc);
startGame(hwnd);
return 0;
case WM_TIMER:
if (iPause) return 0;
Move(hwnd);
if (iIsOver)
{
KillTimer(hwnd, ID_TIMER);
InvalidateRect(hwnd, NULL, TRUE);
}
return 0;
case WM_KEYDOWN:
keyDown(hwnd, wParam, iPause);
if (wParam == P)
{
iPause = !iPause;
InvalidateRect(hwnd, NULL, TRUE);
return 0;
}
if (wParam == R)
{
startGame(hwnd);
return 0;
}
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hBrush = (HBRUSH)GetStockObject(NULL_BRUSH);
SetViewportOrgEx(hdc, SNAKE_WIDTH, SNAKE_WIDTH, NULL);
/*画游戏区的边框*/
MoveToEx(hdc, -1, -1, NULL);
LineTo(hdc, X_WIDTH * SNAKE_WIDTH + 1, -1);
LineTo(hdc, X_WIDTH * SNAKE_WIDTH + 1, Y_HEIGHT * SNAKE_WIDTH + 1);
LineTo(hdc, -1, Y_HEIGHT * SNAKE_WIDTH + 1);
LineTo(hdc, -1, -1);
/*显示屏幕右侧的游戏说明*/
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 0, szInfor_1, lstrlen(szInfor_1));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 20, szInfor_2, lstrlen(szInfor_2));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 50, szInfor_3, lstrlen(szInfor_3));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 80, szInfor_4, lstrlen(szInfor_4));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 110, szInfor_5, lstrlen(szInfor_5));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 140, szInfor_6, lstrlen(szInfor_6));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 170, szInfor_7, lstrlen(szInfor_7));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 200, szInfor_8, lstrlen(szInfor_8));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 300, szInfor_9, lstrlen(szInfor_9));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 320, szInfor_10, lstrlen(szInfor_10));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 340, szInfor_11, lstrlen(szInfor_11));
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH + 100, 320, szBuffer, wsprintf(szBuffer, TEXT("%4d"), iScore));
if (iPause)
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 380, szPause, lstrlen(szPause));
else if (iIsOver)
TextOut(hdc, (X_WIDTH + 1) * SNAKE_WIDTH, 380, szGameOver, lstrlen(szGameOver));
hBrush = CreateSolidBrush(RGB(0, 0, 255)); /*定义画刷为蓝色*/
SelectObject(hdc, hBrush);
for (x = 0; x < X_WIDTH; ++x)
{
for (y = 0; y < Y_HEIGHT; ++y)
{
if (aiPointExist[x][y])
Rectangle(hdc, x * SNAKE_WIDTH, y * SNAKE_WIDTH,
(x + 1) * SNAKE_WIDTH, (y + 1) * SNAKE_WIDTH); /*绘制蛇身和食物的矩形*/
}
}
return 0;
case WM_DESTROY:
KillTimer(hwnd, ID_TIMER);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。