贪吃蛇游戏简单实现
1. 实现的情况:
1) 蛇能够在游戏窗口区自由移动,吃到食物后,身体增长。
2) 窗口区,可随机出现一个食物。
3) 当蛇吃掉食物后,食物消失,随后窗口另外随机一个食物。
4) 上,下,左, 右,分别由键盘上的↑,↓,←,→键控制。
5) 食物随机出现,但不会出现在蛇的身体上,且食物出现在蛇可行的轨道上。
6) 加了两个字母键控制。‘s’键表示开始,‘x’键表示退出。
7) 两个定时器,分别是1,2, 当按下s键后,启动定时器1,然后由窗口函数处理WM_TIMER消息,游戏开始,并开启定时器2。
8) 开始游戏,初始化蛇头,并默认蛇头向下运动。关闭定时器1。
9) 由定时器2发出的WM_TIMER消息根据蛇的运行方向,处理相应的情况。
10) 处理完移动之后情况后,获得移动之前的坐标,并处理掉。
11) 改变蛇移动之后的坐标并保存,其后画出来。
12) 判断蛇是否撞到了窗口边界或者是自身,如果是,提示GAME OVER ,游戏结束。如果不是,判断蛇是否吃到了食物,如果吃到了,把吃到的食物清除,然后另外随机食物到游戏区里,并增长蛇的身体。
2. 没解决的问题:
1) 蛇移动后,会留下边框,当然窗口背景为黑色的除外(因为边框是黑色的).
2) 蛇的身体增长到一定程度时,蛇体颜色发生变化或者游戏死机。
3) 暂停键没有实现。
4) 墙没有实现,游戏区的边界是窗口的边界,当蛇撞到边界时,总是超出一部分。
5) 蛇身是由一个个小方块组成(比较丑)。
6) 键盘上只用了6(s, x,↑,↓,←,→)个键, 其它的都没有使用。
7) 缺少菜单栏,蛇缺少速度控制键。只有程序里设置的那个速度值,这个程序里是200ms移动一下。
8) 得分牌没有实现。
3. 程序文字流程图:
游戏开始--à初始化蛇头和食物,并默认蛇头的运动方向为向下--à控制上,下,左,右键控制蛇的运动-à判断蛇是否撞到边界或者撞到自己--à①如果是,显示GAME OVER,并退出游戏。②如果不是,判断蛇是否吃到食物--à①如果是,去掉吃掉的食物,另外随机食物到游戏区,并且增加蛇的身体。②如果不是,继续控制蛇运动,向前几步一样,循环,直到游戏结束。
4. 代码实现:
本程序是基于VC++6.0实现的,工程是Win32 Application。
加源文件:Snake.cpp
程序:
/**************************************************************
//File Name : Snake.cpp
//Function :
//Date : 25, 12, 2011
//Version : 0.1
//History :
// Version Author Memo
// 0.1 HZG Programming start
***************************************************************/
#include <windows.h>
#include <time.h>
/**********************************************************/
//Function : WinMain
//Parameter :
//Returned Values : msg.wParam
//Memo : main function
/***********************************************************/
// 程序主函数
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // pointer to command line
int nCmdShow // show state of window
)
{
MSG msg;
MyRegisterClass(hInstance);
if (!Initialize(hInstance))
{
return FALSE;
}
srand((unsigned)time(NULL));
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//======================================================
// Function : MyRegisterClass
//
// Parameter : hInst -- windows instance
// Returned Values : RegisterClass(&wndcls)---ATOM style
// Memo : register windows class
//======================================================
// 窗口类注册函数
ATOM MyRegisterClass(HINSTANCE hInst)
{
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndcls.hInstance = hInst;
wndcls.lpfnWndProc = Wnd_Snake_Proc;
wndcls.lpszClassName = "Snake";
wndcls.lpszMenuName = NULL;
wndcls.style = CS_HREDRAW | CS_VREDRAW;
return RegisterClass(&wndcls);
}
//======================================================
// Function : Initialize
//
// Parameter : hInst -- windows instance
// Returned Values : TURE---initialize success
// Memo : Initialize
//======================================================
// 创建并初始化窗口
BOOL Initialize(HINSTANCE hInst)
{
HWND hwnd;
hwnd = CreateWindow("Snake", "Snake", WS_OVERLAPPEDWINDOW,
0, 0, 400, 500, NULL, NULL, hInst, NULL);
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
return TRUE;
}
int key = 1;
POINT PreSnake[100];
POINT Snake[100];
int SnakeNum = 0;
POINT Food;
void StartGame(HWND hwnd); // 游戏开始,初始化蛇头并关闭定时器1
void DrawSnake(HWND hwnd, POINT Snake); // 画蛇
void GetSnake(HWND hwnd, POINT PreSnake[]); // 得到移动之前的蛇
void DrawPreSnake(HWND hwnd, POINT PreSnake); // 去掉移动之前的蛇
void ChangeSnake(HWND hwnd, POINT Snake[]); // 画出移动之后的蛇
void DrawFood(HWND hwnd, POINT Snake[]); // 游戏区随机出现食物
void AddBody(HWND hwnd, int Num); // 吃到一个食物, 身体就增一节
BOOL JuageFood(HWND hwnd, POINT Snake[]); // 判断蛇头是否与蛇身相撞或出游戏边界
void ClearFood(HWND hwnd, POINT Food); // 食物吃了之后清理食物
//=============================================================================
// Function : Wnd_Snake_Proc
//
// Parameter :
// Returned Values : 0---return success
// Memo : CallBack Function ---- process windows messages and come true some methods;
//=============================================================================
// 窗口函数
LRESULT CALLBACK Wnd_Snake_Proc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
switch(uMsg)
{
case WM_CHAR:
switch(wParam)
{
case 's':
SetTimer(hwnd, 1, 200, NULL);
break;
case 't':
KillTimer(hwnd, 2);
break;
case 'x':
exit(0);
break;
}
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_UP: // 0为上, 1为下, 2为左, 3为右
if (key != 1)
{
key = 0;
}
break;
case VK_DOWN:
if (key != 0)
{
key = 1;
}
break;
case VK_LEFT:
if (key != 3)
{
key = 2;
}
break;
case VK_RIGHT:
if (key != 2)
{
key = 3;
}
break;
}
break;
case WM_TIMER:
switch(wParam)
{
case 1:
StartGame(hwnd);
SetTimer(hwnd, 2, 200, NULL);
break;
case 2:
if (key == 0)
{
Snake[0].y -= 20;
}
if (key == 1)
{
Snake[0].y += 20;
}
if (key == 2)
{
Snake[0].x -= 20;
}
if (key == 3)
{
Snake[0].x += 20;
}
GetSnake(hwnd, &PreSnake[0]); //得到蛇移动之前的坐标
ChangeSnake(hwnd, &Snake[0]); //画出移动之后的蛇
if (JuageSnake (hwnd, &Snake[0]) == FALSE)//判断蛇是否撞到边界或者撞到自己
{
KillTimer(hwnd, 2);
if (IDYES == MessageBox(hwnd, "GAME OVER", "GAME OVER", MB_YESNO))
{
DestroyWindow(hwnd);
}
}
else
{
if (Snake[0].x == Food.x && Snake[0].y == Food.y)//判断蛇是否吃到食物
{
ClearFood(hwnd, Food); //清除吃到的食物
DrawFood(hwnd, &Snake[0]); // 另个随机食物到游戏区内
SnakeNum++; //蛇的身体增加
AddBody(hwnd, SnakeNum); //画出蛇增加的一部分
}
}
break;
}
break;
case WM_CLOSE:
if (IDYES == MessageBox(hwnd, "是否真的结束游戏?", "close", MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
//=========================================================
// Function : StartGame
//
// Parameter :
// Returned Values : none
// Memo : start game
//=========================================================
// 游戏开始,并初始蛇头和食物
void StartGame(HWND hwnd)
{
PreSnake[0].x = 0;
PreSnake[0].y = 0;
Snake[0].x = 0;
Snake[0].y = 0;
DrawSnake(hwnd, Snake[0]);
DrawFood(hwnd, &Snake[0]);
KillTimer(hwnd, 1);
}
//=================================================
// Function : DrawSnake()
//
// Parameter :
// Returned Values : none
// Memo : draw a snake in the client area
//==================================================
//得到蛇节点(蛇是由一节节构成的,这里是蛇的最小单位)
void DrawSnake(HWND hwnd, POINT Snake)
{
HDC hdc;
HBRUSH hbrush;
hdc = GetDC(hwnd);
hbrush = CreateSolidBrush(RGB(255, 0, 0));
SelectObject(hdc, hbrush);
Rectangle(hdc, Snake.x, Snake.y, Snake.x + 20, Snake.y + 20);
ReleaseDC(hwnd, hdc);
}
//========================================================
// Function : DrawFood()
//
// Parameter :
// Returned Values : none
// Memo : draw food in the client area
//========================================================
// 随机食物到游戏区
void DrawFood(HWND hwnd, POINT Snake[])
{
RECT rt;
HDC hdc;
HBRUSH hbrush;
BOOL fit = TRUE;
GetClientRect(hwnd, &rt);
hdc = GetDC(hwnd);
hbrush = CreateSolidBrush(RGB(0, 255, 0));
SelectObject(hdc, hbrush);
while (fit)
{
Food.x = rand() % (rt.right - rt.left);
Food.y = rand() % (rt.bottom - rt.top);
if ((Food.x >= rt.left) && ((Food.y + 20) <= rt.bottom) && ((Food.x + 20) <= rt.right) && (Food.y >= rt.top))
{
if ((Food.x % 20 == 0) && (Food.y % 20 == 0))
{
fit = 0;
}// 使食物随机在蛇运动的轨道上
for (int i = 0; i <= SnakeNum; i++)
{
if (Food.x == Snake[i].x && Food.y == Snake[i].y)
{
fit = 1;
}
}// 防止出现食物再现在蛇身上
}
}
Rectangle(hdc, Food.x, Food.y, Food.x + 20, Food.y + 20);
ReleaseDC(hwnd, hdc);
}
//=================================================
// Function : GetSnake()
//
// Parameter :
// Returned Values : none
// Memo : get all of the snake's coordinate after snake motion
//==================================================
//得到蛇运动之前的身体,并除去(实际是把之前的蛇节点都用和背景一样的颜色填充)
void GetSnake(HWND hwnd, POINT PreSnake[])
{
for (int i = 0; i <= SnakeNum; i++)
{
DrawPreSnake(hwnd, PreSnake[i]);
}
}
//=================================================
// Function : GetSnake()
//
// Parameter :
// Returned Values : none
// Memo : draw snake node
//==================================================
// 把蛇移动之前的蛇节点画出来
void DrawPreSnake(HWND hwnd, POINT PreSnake)
{
HDC hdc;
HBRUSH hbrush;
hdc = GetDC(hwnd);
hbrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
// hbrush = CreateSolidBrush(RGB(0, 0, 0));
SelectObject(hdc, hbrush);
Rectangle(hdc, PreSnake.x, PreSnake.y, PreSnake.x + 20, PreSnake.y + 20);
ReleaseDC(hwnd, hdc);
}
//============================================================
// Function : ChangeSnake(HWND hwnd, POINT Snake[])
//
// Parameter :
// Returned Values : none
// Memo : come true the snake's move, save and draw
//=============================================================
// 得到蛇动之后的坐标,并保存各画出蛇移动之后的身体
void ChangeSnake(HWND hwnd, POINT Snake[])
{
// move
for (int i = SnakeNum; i > 0; i--)
{
Snake[i].x = PreSnake[i-1].x;
Snake[i].y = PreSnake[i-1].y;
}
// save
for (i = 0; i <= SnakeNum; i++)
{
PreSnake[i].x = Snake[i].x;
PreSnake[i].y = Snake[i].y;
}
// draw
for (i = 0; i <= SnakeNum; i++)
{
DrawSnake(hwnd, Snake[i]);
}
}
//========================================================
// Function : AddBody
//
// Parameter :
// Returned Values : none
// Memo : eat a food ----add a node body
//========================================================
// 蛇吃了食物之后,蛇身增长的实现
void AddBody(HWND hwnd, int Num)
{
if (key == 0)
{
Snake[Num].x = Snake[Num-1].x;
Snake[Num].y = Snake[Num-1].y + 20;
}
if (key == 1)
{
Snake[Num].x = Snake[Num-1].x;
Snake[Num].y = Snake[Num-1].y - 20;
}
if (key == 2)
{
Snake[Num].x = Snake[Num-1].x + 20;
Snake[Num].y = Snake[Num-1].y;
}
if (key == 3)
{
Snake[Num].x = Snake[Num-1].x - 20;
Snake[Num].y = Snake[Num-1].y;
}
}
//========================================================
// Function : JuageSnake
//
// Parameter :
// Returned Values : none
// Memo : juage snake whether hit itself or window border
//========================================================
//判断蛇是不是撞到自己或者是窗口边界
BOOL JuageSnake(HWND hwnd, POINT Snake[])
{
RECT rt;
GetClientRect(hwnd, &rt);
if (Snake[0].x < rt.left || Snake[0].y < rt.top || Snake[0].x > rt.right || Snake[0].y >rt.bottom)
{
KillTimer(hwnd, 2);
return FALSE;
}
else
{
for (int i = 1; i <= SnakeNum; i++)
{
if (Snake[0].x == Snake[i].x && Snake[0].y == Snake[i].y)
{
return FALSE;
}
}
}
return TRUE;
}
//========================================================
// Function : ClearFood
//
// Parameter :
// Returned Values : none
// Memo : clear food while the snake eat a food
//========================================================
void ClearFood(HWND hwnd, POINT Food)
{
HDC hdc;
HBRUSH hbrush;
hdc = GetDC(hwnd);
hbrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
SelectObject(hdc, hbrush);
Rectangle(hdc, Food.x, Food.y, Food.x + 20, Food.y + 20);
ReleaseDC(hwnd, hdc);
}
// 难点1:蛇怎么移动 -----------------------------------------OK
// 难点2:如何把食物放在蛇移动的轨道上------------------------OK
// 难点3:如何防止食物随机出现在蛇身上------------------------OK
// 难点4:吃到了食物之后,怎么身体变长------------------------OK
// 难点5:随着蛇变长之后, 蛇身上的颜色变白(正常为红色), 然后程序不动(程序死//机了)-----未解决
// 难点6:暂停的实现----没有实现