使用C++实现贪吃蛇

    本博客中部分代码并非原创,浅谈对于代码的理解,由于水平所限,在此作为个人回忆整理所用.

原创地址:https://blog.csdn.net/shawn_hou/article/details/27734217

    根据以前玩的贪吃蛇游戏,它的核心在于实现动态的蛇:首先为了将蛇动态的描述出来,通过不断的输出,清屏,修改,再输出这样的循环来实现动画的效果,动画本身就是由一张张图片表现出来的。

    下一步需要一个类,它能存储要输出的数据,游戏界面包括四周的墙壁以及蛇的展示,采用一个二维字符数组来完整表示,设定蛇头为‘#’,蛇身为‘*’,想一想,当要描述蛇在不断前进时,如何实现蛇下一步所在位置字符的变化?根据当前的方向值,以蛇向右直行为例,计算出下一步的蛇头坐标,x方向加1,y不变,更新新蛇头(变为'#'),旧蛇头('#'变为'*'),旧蛇尾(‘*’变为‘‘)所在位置的字符。在这更新的过程中,需要得到旧蛇头旧蛇尾的坐标,新蛇尾仍然还是'*',保持不变,但下一次又需要新蛇尾的坐标(变为空字符),以此类推,需要将蛇的所有坐标都存储起来。

    创建一个只包含坐标x和坐标y的简单类,用这个类的数组来保存蛇身上每个点的坐标值,并不关心当前坐标上的字符是'#'还是'*',只起到存储作用。蛇头和蛇尾是这两个类之间的纽带。按照数组序号依次递增,记录了蛇不断爬行的坐标,成为蛇爬行的痕迹。


    注意:

1,全局变量的声明应放在other.cpp(存放类方法定义的文件)中,放在.h文件中会造成多重定义,因为main.cpp和other.cpp中均包含头文件。

2,使用getch()函数(在vs中强制使用_getch(),效果不变)来读取用户的输入,如果要使用getchar()函数,那就需要在每一次使用之前清除缓冲区。因为每次从键盘输入一个字符时,换行符残留在缓冲区中,会干扰下一次的读取。

3,在up_data方法中,为什么采用两次_getch()来读取键盘上的方向键,因为根据键盘扫描码,实际键盘的一次按键并非是想象中如ASC||码中的8位,而是16位。 第一次_getch()接受低8位的数值,第二次_getch()接受高8位的值,例如:字符'a'  0x1e61,     'b' 0x3062,    'c'  0x2e63          普通字符按键的低8位值和其ASC||码相同。但是四个方向键的值和ASC码值不同 ↑ 0x4800,    ↓ 0x5000,  ← 0x4b00,  →  0x4d00  ,因此主要获取第二次的_getch()返回值。

4,友情提示:按一次空格键暂停,按两次空格键恢复。

5,可以与原创文章结合在一起进行理解,欢迎留言和讨论~~,

头文件:

#ifndef HEADER_H_
#define HEADER_H_
#define N 22
#include<iostream>
#include<cstdlib>//食物的随机分配rand()
#include<ctime>//蛇的速度,等级clock()
#include<conio.h>//控制台_getch(),_kbhit()函数的使用
class positions//保存坐标,有一点要清楚,走的是留下的痕迹
{
public:
	int x, y;
	void initialize(int&);//存储蛇最初的位置坐标
};
  
class Snake//可以再进行添加别的元素以使得游戏更加有趣
{
private:
	char s[N][N];//用于保存游戏界面信息
	char direction;
	int head, tail;
	int gamespeed,grade;
public:
	Snake(int h = 4, int t = 1, char d = 77, int g = 400) :
		head(h), tail(t), direction(d), gamespeed(400){}
	void initialize();
	void setfood();
	void getgrade();
	void show_game();
	bool updata_game();
};
#endif

other.cpp文件

#include"header.h"
using std::cout;
using std::endl;
using std::cin;
bool gameover; 
char key;
int xh, yh;//蛇头位置坐标
int xf, yf;//食物坐标点
positions snakexy[(N - 2)*(N - 2)];//建立一个position类的数组
void positions::initialize(int& a)
{
	x = 1;
	y = a;
}
void Snake::initialize()
{
	int i, j;
	for (i = 0; i < N; i++)
		for (j = 0; j < N; j++)
		{
			if (i == 0 || i == N - 1)
				s[i][j] = '-';
			else
				s[i][j] = ' ';
		}
	for (i = 1; i < N - 1; i++)
	{
		s[i][0] = '|';
		s[i][N - 1] = '|';
	}
	for (j = 1; j < 4; j++)
		s[1][j] = '*';
	s[1][4] = '#';
}
void Snake::setfood()
{
	srand(time(0));
	while (1)
	{
		xf = rand() % N;
		yf = rand() % N;
		if (s[xf][yf] == ' ')
		{
			s[xf][yf] = '*';
			break;
		}
	}
}
void Snake::getgrade()
{
	cout << "\n\n\n\n\t\t\t1:Grade  speed 500"
		<< "\n\n\t\t\t1:Grade  speed 250" << "\n\n\t\t\t3:Grade  speed 125" << "\n\n\t\t\t4:Grade  speed 50";
	cout << "\n\n\n\t\tPlease choose the grade(1 or 2 or 3 or4)";
	while (cin >> grade)
	{
		if (grade == 1 || grade == 2 || grade == 3 || grade == 4)
			break;
		else
		{
			cout << "Input error!and input again..\n";
		}
	}
	switch (grade)
	{
	case 1:
		gamespeed = 500;
		break;
	case 2:
		gamespeed = 250;
		break;
	case 3:
		gamespeed = 125;
		break;
	case 4:
		gamespeed = 50;
	}
}
void Snake::show_game()
{
	system("CLS");
	cout << endl;
	cout << "\t\tHello,snake is waiting for you!!\n\n";
	int i, j;
	for (i = 0; i < N; i++)
	{
		cout << '\t';
		for (j = 0; j < N; j++)
		{
			cout << s[i][j]<< ' ';
		}
		if (i == 6)
			cout << "\t Speed: " << gamespeed;
		if (i == 10)
			cout << "\t Pause: press the space key!";
		if (i == 14)
			cout << "\t Recover: press the space key twice!!";
		cout << endl;
	}
}
bool Snake::updata_game()
{
	gameover = 1;
	key = direction;
	long tt = clock();
	while ((gameover=(clock() - tt) <= gamespeed) && !_kbhit());
	//gamespeed实际上是作为每次蛇自动前进的时间间隔,gamespeed越小,速度越快。
	//在while语句中,若存在击键,此时产生的时间之差肯定是小于设定的时间间隔的,gameover仍为1(真)继而读取键盘输入;
	//否则等待时间之差越来越大,便会跳出while循环,gameover=0,使用上一次的方向;
	if (gameover)
	{
		_getch();
		key = _getch();//获取击键的值
	}
	if (key == ' ')//空格暂停
	{
		while (_getch() != ' ')
			continue;
	}
	else
		direction = key;
	switch (direction)//计算下一步要显示的蛇头的坐标
	{
	case 72:xh = snakexy[head].x - 1;
		yh = snakexy[head].y;
		break;
	case 80:xh = snakexy[head].x + 1;
		yh = snakexy[head].y;
		break;
	case 77:xh = snakexy[head].x;
		yh = snakexy[head].y + 1;
		break;
	case 75: xh = snakexy[head].x;
		yh = snakexy[head].y - 1;
	}
	if (direction != 72 && direction != 80 && direction != 75 && direction != 77)
		gameover = 0;
	else if (xh == 0 || xh == N - 1 || yh == 0 || yh == N - 1)//碰到墙壁
		gameover = 0;
	else if (s[xh][yh] != ' '&&xh != xf&&yh != yf)//碰到蛇自己
		gameover = 0;     
	else if (xh == xf&&yh == yf)//吃到食物
	{
		s[xh][yh] = '#';
		s[snakexy[head].x][snakexy[head].y] = '*';
		head = (head + 1) % ((N - 1)*(N - 2));
		snakexy[head].x = xh;
		snakexy[head].y = yh;
		setfood();
		gameover = 1;
	}
	else
	{
		//snakexy[head].x = xh;
		//snakexy[head].y = yh;
		//s[snakexy[head].x][snakexy[head].y] = '#';
		s[xh][yh] = '#';
		s[snakexy[head].x][snakexy[head].y] = '*';
		head = (head + 1) % ((N - 1)*(N - 2));
		snakexy[head].x = xh;
		snakexy[head].y = yh;
		s[snakexy[tail].x][snakexy[tail].y] = ' ';
		tail = (tail + 1) % ((N - 1)*(N - 2));
		gameover = 1;
	}
	return gameover;
}

main.cpp文件:

#include"header.h"
int main()
{
	using namespace std;
	extern positions snakexy[(N - 2)*(N - 2)];
	char input = 'y';
	bool live = 1;
	while (input == 'y')
	{	
		system("CLS");
		cout << "\n\n\n\n\n\n\n\n\n\t\t\tWelcome to the snakes.\n";
		cout << "\n\n\n\t\tAny key you push and the game will go...\n";
		_getch();
		system("CLS");
		Snake snake;
		snake.getgrade();
		snake.initialize();
		for (int i = 1; i <= 4; i++)
			snakexy[i].initialize(i);//存储最初蛇的坐标。
		snake.setfood();
		while (live)
		{
			snake.show_game();
			live = snake.updata_game();
		}

		system("CLS");

		cout << "\n\n\n\t\t\tGameover!\n\n" << endl;
		cout << "\n\n\n\t\t Want to continue ? y or n.\n\t\t\t\t";
		cin>> input;
		live = 1;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/ccedricz/article/details/80203900