C++ 开发贪吃蛇游戏总结

一、思考贪吃蛇的思路:

1.在一个窗口绘制贪吃蛇,肯定少不了绘制的API,所以必不可少需要引入graphics.h这个图形界面库(里面封装了WIN 32大部分绘制API,如果没有的话,可以用GDI)。

2.贪吃蛇吃掉食物就会增长一点,可以把它看成是一节一节的。

3.蛇要移动,肯定会有坐标的变化。

4.既然蛇会移动,那么蛇吃掉食物的时候食物当然也是随机变化的,所以食物也有坐标。

5.蛇的碰撞检测(蛇头碰到边界或者碰到自己的身体某部分,就死亡(game over))。

6.蛇要有方向才可以

7.需要读取键盘操作,改变蛇的方向

二、补充graphics.h图形库的基础知识

1.创建一个绘制窗口

API:

void initgraph(int width,int height)    //创建宽x,高y的图形窗口 

 2.清除窗口屏幕所有绘制

API:

void cleardevice()     //清空整个窗口的绘制

 3.在指定位置输出文字

API:

void settextxy(int x,int y, LPCTSTR str//在坐标为(x,y)处输出文字str

void outtextxy( int x, int y, TCHAR c )     // 两种字符串类型

 4.绘制矩形

API:

fillrectangle(int left, int top, int right, int bottom)  //画填充矩形,从起点(left,top) 到终点(right,bottom)

5.绘制圆角矩形

API:

fillroundrect(int left, int top, int right, int bottom, int ellipsewidth, int ellipseheight) //画填充矩形,从起点(left,top) 到终点(right,bottom)  ellipsewidth构成圆角矩形的圆角的椭圆的宽度。ellipseheight构成圆角矩形的圆角的椭圆的高度。若后两个相当相当于绘制一个圆形 不过不是以圆心的 是按照矩形的。

6.设置当前文字填充色

API:

settextcolor(COLOR color)   //设置当前文字也就是下一行文字的颜色 宏定义RED,BLUE,YELLOW等

7.设置当前文字风格

API:

settextstyle(int nHeight, int nWidth, LPCTSTR lpszFace, int nEscapement, int nOrientation, int nWeight, bool bItalic, bool bUnderline, bool bStrikeOut)  //设置文字宽高 如果 width = 0就是自适应大小,LPCTSTR输出的文字后面可以省略掉

 三、如何创建蛇和食物

蛇和食物有一个共同的特点就是都有坐标

所以

//创建坐标
struct Coor {		// x y 坐标结构体
	int x;
	int y;
};

其次创建蛇和食物结构(其实可以见蛇和食物的对象都可以的)

//蛇
struct Snake {		//蛇结构
	int n;	//当前的节数
	Coor szb[SNAKELENGTH];	//蛇坐标 最大长度是500
	Ch ch;	//蛇的方向
}snake;		//结构体变量

			
//食物
struct Food
{
	Coor fzb;	//食物的坐标 只有一个
	int flag;		//是否被吃的标志
}food;

当然,少不了方向 方向无非就是上下左右 可以使会用枚举类型定义

enum Ch
{
	up = 72,
	down = 80,
	left = 75,
	right = 77
};

四、编写思路(过程)

定义完这些结构体后 我们知道 这个蛇也就是snake.szb[0].x 和snake.szb[0].y就是蛇头

所以只需要移动蛇头就可以。

为什么?because 首先蛇是一节一节的也就是绘制的一个一个小矩形,并且这个矩形是根据上面定义的snake结构的坐标结构数组存放的x,y坐标来绘制的,当蛇移动的时候 只需要将坐标加一个步长就可以了。

C++ 开发贪吃蛇游戏总结_第1张图片

这样就真的对了吗?每次移动就在那个放下加或者减?

其实不对,为什么?because 如果是蛇变了很多次方向就像这样

C++ 开发贪吃蛇游戏总结_第2张图片

应该使用怎么样的思路来进行所有蛇节方向改变呢?

可以让蛇的最后一节n的x,y坐标每次绘制的时候等于蛇的n-1的坐标 这样不就可以改变方向并且按蛇头走的路原封不动的走一遍吗? 对吧

所以 移动蛇的时候应该有一个循环,根据蛇节数来遍历的

	for (int i = snake.n - 1; i > 0; i--)
	{
		snake.szb[i].x = snake.szb[i - 1].x;
		snake.szb[i].y = snake.szb[i - 1].y;
	}

创建蛇(这里一共创建了4个蛇节 初始化的时候就是四个蛇节):

	snake.szb[3].x = 50;		//当前节数的X坐标
	snake.szb[3].y = 0;		//当前节数的Y坐标
	snake.szb[2].x = 40;
	snake.szb[2].y = 0;
	snake.szb[1].x = 30;
	snake.szb[1].y = 0;
	snake.szb[0].x = 20;
	snake.szb[0].y = 0;

那么蛇的如何判断方向呢(你需要引入头文件 )?

	//*****这是判断按键 来改变方向
    int move = _getch();
	switch (move)
	{
	case up:	//不能向下走
		if(snake.ch!=down)
			snake.ch = up;
		break;
	case down:
		if (snake.ch != up)
			snake.ch = down;
		break;
	case right:
		if (snake.ch != left)
			snake.ch = right;
		break;
	case left:
		if (snake.ch != right)
			snake.ch = left;
		break;
	}
//***********/
//********根据方向变化来移动蛇头
    //先将后一节的坐标等于前一节的坐标
    for (int i = snake.n - 1; i > 0; i--)
	{
		snake.szb[i].x = snake.szb[i - 1].x;
		snake.szb[i].y = snake.szb[i - 1].y;
	}
    switch (snake.ch)
	{
	case up:
		snake.szb[0].y -= NUM;
		break;
	case down:
		snake.szb[0].y += NUM;
		break;
	case right:
		snake.szb[0].x += NUM;
		break;
	case left:
		snake.szb[0].x -= NUM;
		break;
	}
//********/

这里的_getch 用于获取按键ASCII 比如 _getch 获取键盘按键   _kbhit() 检测是否按下某个键无则返回0;

 接下来就是每次移动绘制蛇:

//绘制蛇  (根据存储的蛇节n)来
for (int i =0; i  < snake.n ; i++)
	{
		if (i == 0)
		{
			setfillcolor(YELLOW);		//填充颜色
		}
		else {
			setfillcolor(RED);		//填充颜色
		}
		fillrectangle(snake.szb[i].x, snake.szb[i].y, snake.szb[i].x + size, snake.szb[i].y + size);
	}

接下来应该是食物了

食物是随机变化的 并且必须是蛇头吃掉食物才能再次随机生成一个食物。

所以开始的时候应该封装一个函数进行初始化食物


food.fzb.x = (rand() % WINDOW_WIDTH / 10) * 10; // 0 1 2 3 4 .. 639
	food.fzb.y = (rand() % WINDOW_HEIGHT / 10) * 10;	// 0 1 2 3 ... 479;
	//蛇的坐标是10 20 30 40 有规律的 10的倍数
	food.flag = 1;	//没有吃掉 1 随机出现

为什么要 *10?因为设置的窗口是640 * 480 食物的坐标必须和蛇节大到小和每次的步长一致,这样当蛇头与食物重合就可以判断吃掉了食物

//绘制食物
void DrawFood()
{
	setfillcolor(GREEN);
	//绘制圆形食物
	
	fillroundrect(food.fzb.x, food.fzb.y, food.fzb.x + 10, food.fzb.y + 10,10,10);
}

有了上面说的碰撞的思路

部分代码:

if (snake.szb[0].x < 0 || snake.szb[0].x >= WINDOW_WIDTH || snake.szb[0].y >= WINDOW_HEIGHT || snake.szb[0].y < 0)
	{
		return true;
	}

。下面是main


	//初始化随机种子
	srand((unsigned int)time(NULL));
	//初始化后会有一个像素矩阵,可以使用坐标系去表示它
	InitSnake();
	/*
		改变方向.按键之后
		按键之前程序在继续移动和绘图
	*/
	 char buffer[100];
	while (1)
	{
		while (!_kbhit())		//检测是否按下某个键 没有按下就返回0
		{
			if (food.flag==0)
			{
				CoorFood();
			}
		
			cleardevice();		//清除重绘
			if (isCrash())		//如果碰撞了
			{
				
				NUM = 0;	//表示撞墙 每次移动的步伐10 变成0 不走了
				
			}
			if (NUM != 0)		//
			{
				rectangle(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
				settextstyle(30, 0, TEXT("微软雅黑"));
				outtextxy(WINDOW_WIDTH + ((WINDOW_WIDTH + 200) - WINDOW_WIDTH) / 2 - 30, 50, TEXT("贪吃蛇"));
				settextstyle(25, 0, TEXT("微软雅黑"));
				settextcolor(YELLOW);
				outtextxy(WINDOW_WIDTH + ((WINDOW_WIDTH + 200) - WINDOW_WIDTH) / 2 - 60, 80, TEXT("分数:"));
				settextstyle(60, 0, TEXT("微软雅黑"));
				settextcolor(WHITE);
				_itoa_s(FenShu, buffer, _countof(buffer), 10);
				outtextxy(WINDOW_WIDTH + ((WINDOW_WIDTH + 200) - WINDOW_WIDTH) / 2 - 60, 120, LPCTSTR(buffer));
				MoveSnake();
				DrawFood();
				DrawSnake(10);
				EatFood();
				Sleep(100);
			}
			else {
				break;
			}
		}
		if (NUM == 0)
		{
			break;
		}
		ChangeSnakeCh();	//按下后执行方向改变函数
		
	}
	settextstyle(65, 0, TEXT("仿宋"));
	outtextxy((WINDOW_WIDTH + 200) / 2 - 120, WINDOW_HEIGHT / 2 - 50, TEXT("你挂了!"));
	settextstyle(33, 0, TEXT("仿宋"));
	settextcolor(YELLOW);
	outtextxy((WINDOW_WIDTH + 200) / 2 - 120, WINDOW_HEIGHT / 2 + 20, TEXT("分数:"));
	outtextxy((WINDOW_WIDTH + 200) / 2 , WINDOW_HEIGHT / 2 + 20, LPCTSTR(buffer));

我的贪吃蛇的源代码已经在github上面

github地址:https://github.com/IAmWilling/SnakeGame

源码里面有详细的注释,可以对照代码根据这篇总结,来读。

你可能感兴趣的:(C++,贪吃蛇)