贪吃蛇详解Windows编程(四)

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


控制区的代码就不详细讲解了,自己看看就能明白,并没有什么难点。如果有兴趣的画还可以把界面做好一点,把功能做丰富一点。但是我觉得点到为止就行了。

我用的是Win32空项目写的,你也可以用自动生成的代码写,但是我觉得那个太乱代码太多。
接下来是全部的代码:


#include <windows.h>
#include <vector>
#include <time.h>

//游戏区和控制区宽度高度
#define  GAME_WIDE             70     //游戏区宽度70*10
#define  GAME_HEIGHT           60     //游戏区高度60*10
#define  CTRL_WIDE             30     //控制区宽度30*10
#define  CTRL_HEIGHT           60     //控制区高度60*10
#define  BOUNDSIZE             10     //边界厚度10   
//蛇的信息
#define   SNAKESIZE            10     //蛇的宽高都为10的小方块
#define   SNAKEMAX            100     //蛇身长度最大值为100
#define   SNAKESPEED          500     //初始化蛇的速度为500ms
#define   TIMERID_1             1     //定时器ID
#define   PAUSEID               1     //暂停按钮ID
#define   STARTID               2     //开始按钮ID 
#define   SPEEDUPID             3     //加速按钮ID
#define   SPEEDDOWNID           4     //减速按钮ID
std::vector<POINT> vSnakePoint;       //声明一个蛇的坐标

UCHAR ucSnakeHead = 4;                //初始化蛇头为4,实际上是vSnakePoint[3];
UCHAR ucSnakeTail = 0;                //初始化蛇尾为0
UCHAR ucSnakekLen = 4;                //初始化蛇身长度为4
UINT uiSnakeSpeed = SNAKESPEED;       //初始化蛇的速度为500ms
UINT uiScore = 0;                     //初始化分数为0

POINT ptDirectHead = { 1,0 };         //蛇头的方向,初始化为向右
POINT ptFood = { 20,20 };             //食物坐标

BOOL bIsLoose = TRUE;                 //判断是否输了
BOOL bIsWin = FALSE;                  //判断是否赢了
BOOL bIsPause = FALSE;                //判断是否暂停了
BOOL bIsStart = FALSE;                //判断是否开始了
BOOL bFlagStart = FALSE;              //判断是否能开始

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

TCHAR szAppName[] = TEXT("Snake by _acme_");
HINSTANCE hInst = nullptr;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	hInst = hInstance;
	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, szAppName,
		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;
}

//初始化蛇
VOID InitSnake()
{
	//先清空坐标
	vSnakePoint.clear();
	//给蛇坐标分配空间
	vSnakePoint.resize(SNAKEMAX);
	//游戏结束后再次初始化
	ucSnakeHead = 4;//初始化蛇头为4,实际上是vSnakePoint[3];
	ucSnakeTail = 0;//初始化蛇尾为0
	ucSnakekLen = 4;//初始化蛇身长度为4
	uiSnakeSpeed = SNAKESPEED;//初始化蛇的速度为200ms
	ptDirectHead = { 1,0 };//初始化蛇头方向
	ptFood = { 20,20 };//初始化食物坐标
	uiScore = 0;//初始化分数
				//初始化每一节的蛇身坐标
	for (int i = 0; i < ucSnakekLen; i++)
	{
		vSnakePoint[i].x = i;
		vSnakePoint[i].y = 0;
	}
}

//获取蛇结点位置
//index是一个偏移蛇尾的偏移量
POINT &GetSnakeNode(int index)
{
	int i = ucSnakeTail + index;//蛇身的长度
	if (i >= SNAKEMAX)
	{
		i -= SNAKEMAX;
	}
	return vSnakePoint[i];
}

//画蛇身
VOID DrawSnake(HDC hdc)
{
	static HBRUSH hBrush = CreateSolidBrush(RGB(0, 243, 222));
	SelectObject(hdc, hBrush);
	//当前蛇结点
	POINT ptCurrentNode = { 0 };

	for (int i = 0; i < ucSnakekLen; i++)
	{
		ptCurrentNode = GetSnakeNode(i);
		Rectangle(hdc, ptCurrentNode.x*SNAKESIZE + BOUNDSIZE, ptCurrentNode.y*SNAKESIZE + BOUNDSIZE,
			(ptCurrentNode.x + 1)*SNAKESIZE + BOUNDSIZE, (ptCurrentNode.y + 1)*SNAKESIZE + BOUNDSIZE);
	}

}

//设置食物坐标
void SetFood()
{
	POINT ptNow = { 0 };
	int x = 0, y = 0, i = 0;
	while (true)
	{
		srand(time(0));//随机器种子
		x = rand() % GAME_WIDE; //设置食物随机坐标
		y = rand() % GAME_HEIGHT;
		for (i = 0; i < ucSnakekLen; i++)
		{
			ptNow = GetSnakeNode(i);
			if (ptNow.x == x && ptNow.y == y)
				break;//如果食物出现在蛇身,则重新设置食物
		}//如果食物不在蛇身,就退出循环
		if (ucSnakekLen == i) break;
	}
	ptFood.x = x;
	ptFood.y = y;
}

//判断蛇是否撞墙
BOOL IsStrikeWall(POINT ptHead)
{
	if (ptHead.x < 0 || ptHead.x >= GAME_WIDE
		|| ptHead.y < 0 || ptHead.y >= GAME_HEIGHT)
	{
		bIsLoose = FALSE;//撞墙输了
		return FALSE;
	}
	return TRUE;
}

//判断是否撞到自己
BOOL IsStrikeSelf(POINT ptHead)
{
	POINT ptNow = { 0 };
	for (int i = 0; i < ucSnakekLen - 1; i++)
	{
		ptNow = GetSnakeNode(i);
		if (ptHead.x == ptNow.x && ptHead.y == ptNow.y)
		{
			bIsLoose = FALSE;//撞自己输了
			return FALSE;
		}

	}
	return TRUE;
}

//判断是否吃到食物
BOOL IsEat(POINT ptHead)
{
	return (ptHead.x == ptFood.x && ptHead.y == ptFood.y) ? TRUE : FALSE;
}

//显示分数
void ShowScore(HWND hwnd)
{
	HDC hdc = GetDC(hwnd);
	HPEN hPen = CreatePen(PS_SOLID, 4, RGB(255, 255, 0));
	SelectObject(hdc, hPen);
	int iLength = 0;
	TCHAR szBuffer[20] = { 0 };
	iLength = wsprintf(szBuffer, L"Score: %u", uiScore);
	TextOut(hdc, BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE + 100,
		BOUNDSIZE + 200, szBuffer, iLength);
	ReleaseDC(hwnd, hdc);
	DeleteObject(hPen);
}

//判断是否超出范围
void IsOverTake()
{
	if (SNAKEMAX == ucSnakeHead) ucSnakeHead = 0;	//超出范围往返循环	
	if (SNAKEMAX == ucSnakeTail)    ucSnakeTail = 0;
	if (SNAKEMAX == ucSnakekLen)   bIsWin = TRUE;//赢了
}

//增加蛇的长度
void AddSnakeLen(POINT ptHead)
{
	vSnakePoint[ucSnakeHead++] = ptHead;//蛇头增长
	ucSnakekLen++;//长度+1
	IsOverTake();//判断是否超出范围
}

//让蛇动起来
BOOL  SnakeMove()
{
	BOOL bRet = FALSE;
	do
	{
		POINT ptNewNode = { 0 };//新的蛇头
		ptNewNode.x = GetSnakeNode(ucSnakekLen - 1).x + ptDirectHead.x;
		ptNewNode.y = GetSnakeNode(ucSnakekLen - 1).y + ptDirectHead.y;
		if (!IsStrikeWall(ptNewNode))//判断新的蛇头是否撞墙
			break;
		if (!IsStrikeSelf(ptNewNode))//判断蛇是否撞到自己
			break;
		if (IsEat(ptNewNode))//判断是否吃到食物
		{
			SetFood();//重新设置食物坐标
			uiScore += 10;//吃到一个食物加10分
			AddSnakeLen(ptNewNode);//增加蛇的长度
			break;
		}
		vSnakePoint[ucSnakeHead].x = ptNewNode.x;//蛇头移动
		vSnakePoint[ucSnakeHead++].y = ptNewNode.y;
		ucSnakeTail++;
		IsOverTake();
		bRet = TRUE;
	} while (FALSE);
	return bRet;
}

//画食物
void DrawFood(HWND hwnd)
{
	HDC hdc = GetDC(hwnd);
	HBRUSH hBrush = CreateSolidBrush(RGB(255, 0, 0));
	SelectObject(hdc, hBrush);
	//椭圆食物
	Ellipse(hdc, BOUNDSIZE + ptFood.x*SNAKESIZE, BOUNDSIZE + ptFood.y*SNAKESIZE
		, BOUNDSIZE + (ptFood.x + 1)*SNAKESIZE, BOUNDSIZE + (ptFood.y + 1)*SNAKESIZE);
	ReleaseDC(hwnd, hdc);
	DeleteObject(hBrush);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	RECT rect = { 0 };
	static int  ixWind = 0, iyWind = 0;//窗口大小参数
	static int ixClient = 0, iyClient = 0;//客户区大小参数
	static HWND  hPause = nullptr;//暂停按钮句柄
	static HWND  hStart = nullptr;//开始按钮句柄
	static HWND  hSpeedUp = nullptr;//加速按钮句柄
	static HWND  hSpeedDown = nullptr;//减速按钮句柄
	DWORD dwError = 0;//错误信息

	switch (message)
	{
	   case WM_CREATE:
	   {

		  //获取窗口大小
		  GetWindowRect(hwnd, &rect);
		  ixWind = rect.right - rect.left;
		  iyWind = rect.bottom - rect.top;
		  //获取客户区大小
		  GetClientRect(hwnd, &rect);
		  ixClient = rect.right - rect.left;
		  iyClient = rect.bottom - rect.top;
		  //修改窗口大小
		  int nWideth = BOUNDSIZE * 3 + (GAME_WIDE + CTRL_WIDE)*SNAKESIZE + (ixWind - ixClient);
		  int nHeight = BOUNDSIZE * 2 + GAME_HEIGHT*SNAKESIZE + (iyWind - iyClient);
		  MoveWindow(hwnd, 50, 50, nWideth, nHeight, TRUE);
		  //初始化蛇身
		  InitSnake();
		  //创建一个暂停按钮,宽100高50
		  hPause = CreateWindowW(L"button", L"Pause", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
			BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE + 30, BOUNDSIZE + 20,
			100, 50, hwnd, (HMENU)PAUSEID, hInst, 0);
		  //创建一个开始按钮,宽100高50
		  hStart = CreateWindowW(L"button", L"Start", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
			BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE + 170, BOUNDSIZE + 20,
			100, 50, hwnd, (HMENU)STARTID, hInst, 0);
		  //创建一个加速按钮,宽100高50
		  hSpeedUp = CreateWindowW(L"button", L"SpeedUp", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
			BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE + 30, BOUNDSIZE + 90,
			100, 50, hwnd, (HMENU)SPEEDUPID, hInst, 0);
		  //创建一个减速按钮,宽100高50
		  hSpeedDown = CreateWindowW(L"button", L"SpeedDown", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
			BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE + 170, BOUNDSIZE + 90,
			100, 50, hwnd, (HMENU)SPEEDDOWNID, hInst, 0);
	   }

	    break;

	   case WM_TIMER:

		   switch (wParam)
		   {
		    case TIMERID_1:
		    {	//移动蛇
			   SnakeMove();
			   //判断是否输了
			   if (FALSE == bIsLoose)
			   {
				   KillTimer(hwnd, TIMERID_1);
				   MessageBox(hwnd, L"you loose!", L"", MB_OK);
				   InitSnake();//初始化蛇
				   bIsLoose = TRUE;
				   SetWindowTextW(hStart, L"Start"); //如果输了就把ReStart变为Start
				   bIsStart = FALSE;//重置开始
				   bFlagStart = FALSE;
			   }
			   if (TRUE == bIsWin)//判断是否赢了
			   {
				   KillTimer(hwnd, TIMERID_1);
				   MessageBox(hwnd, L"you Win!", L"", MB_OK);
				   InitSnake();//初始化蛇
				   bIsWin = FALSE;
			   }
			   //重新画蛇
			   RECT rect = { 0 };
			   SetRect(&rect, 0, 0, BOUNDSIZE + GAME_WIDE*SNAKESIZE,
				   BOUNDSIZE + GAME_HEIGHT*SNAKESIZE);
			   InvalidateRect(hwnd, &rect, TRUE);
		    }
		    break;
		    default:
			   break;
		   }

		   break;
	   case WM_KEYDOWN://控制方向

		   if (FALSE == bIsLoose || TRUE == bIsPause)
			   return 0;

		   switch (wParam)
		   {
		   case VK_UP:
			   if (ptDirectHead.x != 0 && ptDirectHead.y != 1)
			   {
				   ptDirectHead.x = 0;
				   ptDirectHead.y = -1;
			   }
			   break;
		   case VK_DOWN:
			   if (ptDirectHead.x != 0 && ptDirectHead.y != -1)
			   {
				   ptDirectHead.x = 0;
				   ptDirectHead.y = 1;
			   }
			   break;
		   case VK_LEFT:
			   if (ptDirectHead.y != 0 && ptDirectHead.x != 1)
			   {
				   ptDirectHead.x = -1;
				   ptDirectHead.y = 0;
			   }
			   break;
		   case VK_RIGHT:
			   if (ptDirectHead.y != 0 && ptDirectHead.x != -1)
			   {
				   ptDirectHead.x = 1;
				   ptDirectHead.y = 0;
			   }
			   break;
		   default:
			   break;
		   }

		   break;

	case WM_COMMAND:
	{
		int wmId = LOWORD(wParam);
		// 分析菜单选择: 
		switch (wmId)
		{
		case SPEEDUPID://速度加
			if (uiSnakeSpeed >= 0 && bIsStart == TRUE)
			{
				if (100 >= uiSnakeSpeed)
					uiSnakeSpeed -= 10;
				else
					uiSnakeSpeed -= 100;
				SetTimer(hwnd, TIMERID_1, uiSnakeSpeed, nullptr);//重新设置				
			}
			SendMessage(hwnd, WM_SETFOCUS, 0, 0);
			break;
		case SPEEDDOWNID://速度减
		{

			if (uiSnakeSpeed <= 1000 && bIsStart == TRUE)
			{
				if (100 > uiSnakeSpeed)
					uiSnakeSpeed += 10;
				else
					uiSnakeSpeed += 100;
				SetTimer(hwnd, TIMERID_1, uiSnakeSpeed, nullptr);//重新设置				
			}
			SendMessage(hwnd, WM_SETFOCUS, 0, 0);
		}
		break;
		case STARTID://开始
		{
			if (FALSE == bIsStart && FALSE == bFlagStart)
			{
				SetTimer(hwnd, TIMERID_1, uiSnakeSpeed, nullptr);//设置定时器
				bIsStart = TRUE;//设置为已经开始
				bFlagStart = TRUE;//第一次开始后就设为TREU 表示不再进入这个函数
				SetWindowTextW(hStart, L"ReStart");
				SendMessage(hwnd, WM_SETFOCUS, 0, 0);//把输入焦点还给游戏区
			}
			else
			{
				if (TRUE == bIsPause)
				{//如果已经暂停了,按下重新开始要记得把暂停按钮恢复原样
					if (!SetWindowTextW(hPause, L"Pause"))
						dwError = GetLastError();
					bIsPause = FALSE;
				}
				InitSnake();//初始化,重新开始
				SetTimer(hwnd, TIMERID_1, uiSnakeSpeed, nullptr);
				SendMessage(hwnd, WM_SETFOCUS, 0, 0);//把焦点还给游戏区
			}
		}
		break;
		case PAUSEID://暂停
			if (TRUE == bFlagStart)//游戏进行的时候才能暂停
			{
				if (FALSE == bIsPause)//如果没有暂停则将它暂停
				{
					KillTimer(hwnd, TIMERID_1);//关掉定时器就是暂停了
					bIsPause = TRUE;          //把状态设置为暂停
					if (!SetWindowText(hPause, L"Go on"))//更改按钮标题
						dwError = GetLastError();

				}
				else
				{//按下继续按钮就重新创建定时器
					SetTimer(hwnd, TIMERID_1, uiSnakeSpeed, nullptr);
					bIsPause = FALSE;//把状态设置为没有被暂停
					if (!SetWindowText(hPause, L"Pause"))//更改按钮标题
						dwError = GetLastError();
					SendMessage(hwnd, WM_SETFOCUS, 0, 0);//把焦点还给游戏区
				}
			}

			break;
		default:
			return DefWindowProc(hwnd, message, wParam, lParam);
		}
	}
	    break;
	case WM_SETFOCUS:
		SetFocus(hwnd);
		break;
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hwnd, &ps);
		static HBRUSH hBrush1 = CreateSolidBrush(RGB(58, 206, 66));
		static HBRUSH hBrush2 = CreateSolidBrush(RGB(80, 87, 196));

		//画游戏区
		Rectangle(hdc, BOUNDSIZE, BOUNDSIZE, BOUNDSIZE + GAME_WIDE*SNAKESIZE,
			BOUNDSIZE + GAME_HEIGHT*SNAKESIZE);
		//填充游戏区颜色
		SetRect(&rect, BOUNDSIZE, BOUNDSIZE, BOUNDSIZE + GAME_WIDE*SNAKESIZE,
			BOUNDSIZE + GAME_HEIGHT*SNAKESIZE);
		FillRect(hdc, &rect, hBrush1);
		//画控制区
		Rectangle(hdc, BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE, BOUNDSIZE,
			BOUNDSIZE * 2 + (GAME_WIDE + CTRL_WIDE)*SNAKESIZE, BOUNDSIZE + CTRL_HEIGHT*SNAKESIZE);
		//填充控制区颜色
		SetRect(&rect, BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE, BOUNDSIZE,
			BOUNDSIZE * 2 + (GAME_WIDE + CTRL_WIDE)*SNAKESIZE,
			BOUNDSIZE + CTRL_HEIGHT*SNAKESIZE);
		FillRect(hdc, &rect, hBrush2);
		//画蛇身
		DrawSnake(hdc);
		//画食物
		DrawFood(hwnd);
		//显示分数
		ShowScore(hwnd);
		EndPaint(hwnd, &ps);
	}
	break;
	case WM_DESTROY:
		KillTimer(hwnd, TIMERID_1);
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值