小型项目:贪吃蛇中学习win32 API

版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/leolinsheng/article/details/22899911

贪吃蛇

用C++和少量win32 Api实现一个经典小游戏:贪吃蛇,主要是为了学习之用。

一.首先构思贪吃蛇类应该具有哪些属性和功能,并且需要用什么样的数据结构来存储数据。我们把贪吃蛇和它将要吃的贪物看成一个整体来考虑。

属性有:

1.贪吃蛇的蛇头Head

2.贪吃蛇的身长Length

3.是否存在食物isExistFood

4.食物所在的坐标(Food_x,Food_y)

5.蛇头的方向direction ={(-1,0)(1,0)(0,1)(0,-1)}

6.游戏的速度Speed

应该具有的功能:

1.根据方向进行挪动Move()

2.重绘贪吃蛇redraw(),因为需要不停地移动,由此需要经常重绘

3.判断游戏是否结束,isGameOver()程序终止条件

扫描二维码关注公众号,回复: 4708398 查看本文章

4.随机在地图中生成食物createfood()

5.游戏进程函数onGame()

6.初始游戏界面Init()

7.删除蛇身结点函数,避免内存泄露。del()

因此,贪吃蛇类的大体框架就可以出来了。

class Snake
{
public:
	node *head;								//蛇头指针
	int Length;								//蛇长
	int level;								//速度(等级)
	bool isFoodExist;						
	int Food_x,Food_y;						//食物存在标记
	int dir[2];								//蛇头方向
public:
	void del();						//删除结点,避免内存泄露
	void Init();					//初始化界面
	void ReDraw();					//重绘蛇身函数
	void CreateFood();				//随机生成食物
	bool isGameOver();				//判断游戏是否结束
	void onGame();					//游戏进程函数
	void Move();					//移动
};

我们在初始化的时候,贪吃蛇的身长应该有三个结点,因此可以添加

Snake():Length(3),isFoodExist(false),level(1)		//构造函数
	{ 	  
		dir[0] = 1,dir[1] = 0;
		head = new node(4,2);				//申请起始蛇身3节
		node *temp1 = new node(3,2);
		node *temp2 = new node(2,2);
		head->next = temp1;
		temp1->next = temp2;
		temp2->next = NULL;
	}

接下来就实现各个部分的功能代码:

最主要的部分是游戏进程函数onGame(),它的主要功能有

1.贪吃蛇的移动,Move

2.贪吃蛇的重绘

3.处理上下左右的方向

4.处理吃到食物和不吃到食物的处理方法。吃到食物,则置食物标示为false,如果没吃到食物,则删除蛇尾结点。

完整代码

#include <iostream>
#include <windows.h>
#include <conio.h>
#include <time.h>

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

using namespace std;

#define RIGHT 40
#define LEFT 1
#define TOP 1
#define BOTTOM 25

struct node										//蛇身(链表结构)
{
	int x,y;									//蛇身结点坐标
	node *next;									//下一个结点
	node(){}
	node(int _x,int _y):x(_x),y(_y),next(NULL){}
	node(const node&){}
	~node(){}
};

void SetPos(int i,int j)							//设定光标位置,win32 API
{
	COORD pos = {i-1,j-1};							//坐标变量
	HANDLE Out = GetStdHandle(STD_OUTPUT_HANDLE);	//获取输出句柄
	SetConsoleCursorPosition(Out,pos);				//设定控制台光标位置
}

class Snake
{
public:
	node *head;								//蛇头指针
	int Length;								//蛇长
	int level;								//速度(等级)
	bool isFoodExist;						
	int Food_x,Food_y;						//食物存在标记
	int dir[2];								//蛇头方向
public:
	Snake():Length(3),isFoodExist(false),level(1)		//构造函数
	{ 	  
		dir[0] = 1,dir[1] = 0;
		head = new node(4,2);				//申请起始蛇身3节
		node *temp1 = new node(3,2);
		node *temp2 = new node(2,2);
		head->next = temp1;
		temp1->next = temp2;
		temp2->next = NULL;
	}
	void del();						//删除结点,避免内存泄露
	void Init();					//初始化界面
	void ReDraw();					//重绘蛇身函数
	void CreateFood();				//随机生成食物
	bool isGameOver();				//判断游戏是否结束
	void onGame();					//游戏进程函数
	void Move();					//移动
};

void Snake::del()
{
	node *p = head;
	while (p != NULL)
	{
		p = p->next;
		delete head;
		head = p;
	}
}

void Snake::Init()						//初始化界面
{
	int i;
	for (i=1; i <= RIGHT; ++i)
	{
		SetPos(i,1);
		cout << "-";
		SetPos(i,25);
		cout << "-";
	}
	for (i=2; i <= BOTTOM; ++i)
	{
		SetPos(1,i);
		cout << "|";
		SetPos(RIGHT,i);
		cout << "|";
	}
	SetPos(54,3);
	cout << "贪吃蛇";
	SetPos(54,5);
	cout << "长度:" << Length;
	SetPos(54,7);
	cout << "等级:" << level;
}

void Snake::CreateFood()						//随机出食物的坐标并判断坐标是否合法
{
	node *p = NULL;
	srand((unsigned int)time(0));			    //用系统时间来做随机数种子
	while (true) 
	{
		Food_x = rand() % 38 + 2;				//随机出食物的坐标
		Food_y = rand() % 23 + 2;
		p = head;
		while (p!=NULL)							//判断食物是否产生在蛇体内
		{
			if (p->x==Food_x && p->y==Food_y)	//有冲突
				break;
			p = p->next;
		}
		if (p==NULL)							//所有结点都不冲突。生成成功
			break;
	}
}

bool Snake::isGameOver()
{
	if (head->x >= RIGHT || head->x <=LEFT ||
		head->y <= TOP || head->y >= BOTTOM)	//是否撞到边缘
		return true;
	node *p = head->next;
	while (p!=NULL)
	{
		if ((head->x==p->x) && (head->y==p->y))	//撞到蛇身
			return true;
		p = p->next;
	}
	return false;
}

void Snake::ReDraw()							//重绘蛇身
{
	node *p = head;
	while (p!=NULL)
	{
		SetPos(p->x,p->y);
		cout << "*";
		p = p->next;
	}
	SetPos(Food_x,Food_y);
	cout << "*";
}

void Snake::Move()
{
	node *New;
	New = new node(head->x+dir[0],head->y+dir[1]);		//新的蛇身结点
	New->next = head;
	head = New;
}

void Snake::onGame()
{
	system("cls");										//刷新屏幕
	node *p = NULL;
	Init();												//初始化界面
	ReDraw();											//重绘蛇身
	char x;
	while (true)
	{
		if (_kbhit())							    	   	  //_kbhit()判断是否有键盘操作
		{
			x = _getch();									  //重缓冲区读出一个字符赋给x
			if ((x=='W'||x=='w') && !(dir[0]==0&&dir[1]==1))  //改变蛇的方向(不可以是反方向)下
				dir[0] = 0, dir[1] = -1;
			if ((x=='S'||x=='s') && !(dir[0]==0&&dir[1]==-1)) //上
				dir[0] = 0, dir[1] = 1;
			if ((x=='A'||x=='a') && !(dir[0]==1&&dir[1]==0))  //右
				dir[0] = -1, dir[1] = 0;
			if ((x=='D'||x=='d') && !(dir[0]==-1&&dir[1]==0)) //左
				dir[0] = 1, dir[1] = 0;
			while (_kbhit())								  //读掉这之后所有的键盘输入
				_getch();
		}
		if (!isFoodExist)	   							 	 //如果视图中没有食物,则随机生成食物
		{
			CreateFood();
			isFoodExist = true;
		}
		Move();												//移动蛇
		if (head->x==Food_x && head->y==Food_y)				//如果蛇吃到了食物
		{
			isFoodExist = false;
			++Length;										//蛇身+1
			SetPos(60,5);
			cout << "长度 : " << Length;					//改变界面信息
			if (Length%10==0)								//每十个蛇身升一级
			{
				++level;
				SetPos(60,7);
				cout << "等级 : " << level;
			}
			if (level==10)									//最高等级达成。退出游戏
				break;
		}
		ReDraw();											//重绘蛇身
		if (isFoodExist)									//如果没有吃到食物,需要删除蛇尾。
		{	
			p=head;
			while ((p->next)->next!=NULL)
				p=p->next;
			SetPos(p->next->x,p->next->y);
			cout << " ";
			delete(p->next);
			p->next=NULL;
		}
		if(isGameOver())								//判断是否游戏结束
			break;
		Sleep(500-level*50);							//等待,具体时间和等级有关
	}
	system("cls");
	if (level==10)										//通关
	{
		SetPos(10,5);
		cout << "成功通关!" << endl;
	}
	else
	{
		SetPos(10,5);
		cout << "游戏结束!你的成绩为:" << Length << endl;
	}
}
int main()
{
	Snake game;
	system("cls");
	cout<<"*                      贪吃蛇                       *"<<endl;
	cout<<"*		    	  W,A,S,D控制移动                  *"<<endl;
	cout<<"*       每10节蛇身升一级,并提高速度,10级通关      *"<<endl;
	_getch();
	game.onGame();
	game.del();
	_CrtDumpMemoryLeaks();										//内存泄露检测!
	return 0;
}

2.将上面的程序用SDK程序改写成图形化界面,while(true){}逻辑需要用SetTimer来改写.其它大体逻辑不变.

#include <Windows.h>
#include <time.h>

#define UNIT 20

struct NODE										//蛇身(链表结构)
{
	int x, y;
	NODE *next;
	NODE(int _x,int _y):x(_x),y(_y),next(NULL){}
};

RECT RectWindow;
NODE *head = NULL;
int Length = 3;
int level = 1;
bool isFoodExist = FALSE;
int Food_x,Food_y;
int dir[2];

bool isGameOver()
{
	if (head->x >= RectWindow.right || head->x <= RectWindow.left 
		|| head->y <= RectWindow.top || head->y >= RectWindow.bottom)	//是否撞到边缘
		return true;
	NODE *p = head->next;
	while (p)
	{
		if ((head->x==p->x) && (head->y==p->y))	//撞到蛇身
			return true;
		p = p->next;
	}
	return false;
}
void AddNewHead()
{
	NODE *New = new NODE(head->x+dir[0],head->y+dir[1]);		//新的蛇身结点
	New->next = head;
	head = New;
}

void CreateFood()							//随机出食物的坐标并判断坐标是否合法
{
	NODE *p = NULL;
	srand((unsigned int)time(0));			 //用系统时间来做随机数种子
	while (true) 
	{
		Food_x = (rand() % (RectWindow.right-60)) / UNIT * UNIT + UNIT ;  //保证产生的食物与蛇可以连接
		Food_y = (rand() % (RectWindow.bottom-60)) / UNIT * UNIT + UNIT;

		p = head;
		while (p)							//判断食物是否产生在蛇体内
		{
			if (p->x==Food_x && p->y==Food_y)	//有冲突
				break;
			p = p->next;
		}
		if (p==NULL)							//所有结点都不冲突。生成成功
			break;
	}
}

void DrawSnake(HWND hwnd)							//重绘蛇身
{
	NODE *p = head;
	HDC hdc = GetDC(hwnd);
//	HBRUSH brush = (HBRUSH)SelectObject(hdc,GetStockObject(WHITE_BRUSH));//把黑色画刷选进hdc
	while (p)
	{
		Rectangle(hdc,p->x,p->y,p->x+UNIT,p->y+UNIT);
		p = p->next;
	}
	Rectangle(hdc,Food_x,Food_y,Food_x+UNIT,Food_y+UNIT);
//	SelectObject(hdc,brush);	//恢复handle
	ReleaseDC(hwnd,hdc);
}

LRESULT CALLBACK WndProc(HWND hwnd,      // handle to window
							UINT uMsg,      // message identifier
							WPARAM wParam,  // first message parameter
							LPARAM lParam);   // second message parameter);
int WINAPI WinMain(
				   HINSTANCE hInstance,      // handle to current instance
				   HINSTANCE hPrevInstance,  // handle to previous instance
				   LPSTR lpCmdLine,          // command line
				   int nCmdShow              // show state
				   )
{
	WNDCLASS wc;

	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
	wc.hCursor = LoadCursor(NULL,IDC_ARROW);
	wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
	wc.hInstance = hInstance;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = "ls";
	wc.lpszMenuName = "ls";
	wc.style = CS_VREDRAW | CS_HREDRAW;

	RegisterClass(&wc);

	HWND hwnd = CreateWindow("ls","ls",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
		CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
	if (!hwnd)
	{
		return FALSE;
	}
	ShowWindow(hwnd,nCmdShow);
	UpdateWindow(hwnd);

	MSG msg;
	while (GetMessage(&msg,NULL,0,0) > 0)
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return TRUE;
}

LRESULT CALLBACK WndProc(HWND hwnd,      // handle to window
							UINT uMsg,      // message identifier
							WPARAM wParam,  // first message parameter
							LPARAM lParam)   // second message parameter);
{
	PAINTSTRUCT ps ;
	HDC hdc;
	switch (uMsg)
	{
	case WM_CREATE:
		{
			isFoodExist = FALSE;
			dir[0] = -UNIT,dir[1] = 0;		//debug
			head = new NODE(400,200);				//申请起始蛇身3节
			NODE *temp1 = new NODE(420,200);
			NODE *temp2 = new NODE(440,200);
			head->next = temp1;
			temp1->next = temp2;
			temp2->next = NULL;
			GetClientRect(hwnd, &RectWindow);	  //获取窗口大小

			SetTimer (hwnd, 1, 200, NULL) ;		//设置时间
			break;
		}
		
	case WM_PAINT:
		{
			hdc = BeginPaint (hwnd, &ps);
			if (!isFoodExist)	 							 	 //如果视图中没有食物,则随机生成食物
			{
				CreateFood();
				isFoodExist = true;
			}
			DrawSnake(hwnd);
			EndPaint(hwnd,&ps);
			break;
		}
	case WM_KEYDOWN:
		{
			if ((wParam=='W'||wParam=='w') && !(dir[0]==0&&dir[1]==UNIT)) //改变蛇的方向(不可以是反方向)下
				dir[0] = 0, dir[1] = -UNIT;
			if ((wParam=='S'||wParam=='s') && !(dir[0]==0&&dir[1]==-UNIT)) //上
				dir[0] = 0, dir[1] = UNIT;
			if ((wParam=='A'||wParam=='a') && !(dir[0]==UNIT&&dir[1]==0)) //右
				dir[0] = -UNIT, dir[1] = 0;
			if ((wParam=='D'||wParam=='d') && !(dir[0]==-UNIT&&dir[1]==0)) //左
				dir[0] = UNIT, dir[1] = 0;
		}
	case WM_TIMER:
		{
			if (isGameOver())
			{
				KillTimer(hwnd,1);
				return 0;
			}
			AddNewHead();
			if (head->x==Food_x && head->y==Food_y)				//如果蛇吃到了食物
			{
				isFoodExist = false;
			}
			if (isFoodExist)									//如果没有吃到食物,需要删除蛇尾。
			{
				NODE *p = head;
				while ((p->next)->next!=NULL)
					p=p->next;

				delete(p->next);
				p->next=NULL;
			}
			InvalidateRect(hwnd, NULL, TRUE);
			break;
		}
	case WM_DESTROY:
		PostQuitMessage(0) ;
		break;
	default:
		return DefWindowProc(hwnd,uMsg,wParam,lParam);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/leolinsheng/article/details/22899911
今日推荐