【C++学习笔记】三、控制台草图贪吃蛇工程

1贪吃蛇逻辑声明

1.1 创建蛇对象

在创建蛇对象前应该现demo一个打印地图,毕竟是打算用控制台打印。

const int N = 30;  	//地图的宽
const int M = 81;  	//地图的长
char map[N][M];		//地图数组

Map生成地图

void Map()
{
	int i, j;
	for (i = 0; i < N; i++)
	{
		for (j = 0; j < M - 1; j++)
		{
			if (i == 0 || i == N - 1 || j == 0 || j == M - 2)
				map[i][j] = '*';
			else
				map[i][j] = ' ';
		}
		map[i][j] = '\n';
	}
	map[i][j] = '\0';
}

常见的控制台下的dos命令:
system(“cls”);用于清屏
system(“pause”);暂停
Sleep函数需要添加相关的头文件,快捷键Alt + Enter ()
某些编译器下的Windows.h头文件

void Show_map()
{
	system("cls");		
	printf("%s", map);
	Sleep(100);
}

创建蛇节点,用于存储蛇头和蛇身的连接

struct Node
{
	char x;
	char y;
	struct Node* next;
};

当然关于蛇对象的做法很多,直接拿函数打出来,结构体都行。这里举例的只是一种蛇类的实现方式。

/*
功能: 	- 创建蛇:
		@
描述	:
示例	:s.Create_snake(s1);
*/
void Snake::Create_snake(Node* snake)
{
	srand((unsigned)time(NULL));

	snake->x = rand() % 78 + 1;
	snake->y = rand() % 26 + 1;
	//snake->x = 40;
	//snake->y = 16;
	char _x = snake->x;
	char _y = snake->y;
	while (snake_length--[--snakeNum])
	{
		//snake->next = (Node*)malloc(sizeof(Node));
		snake->next = new Node;
		if (!(snake->next))
		{
			cerr << "申请蛇节点失败" << endl;
		}
		else
		{
			//cerr << "申请蛇节点成功" << endl;
			snake = snake->next;
			snake->x = _x;
			snake->y = ++_y;
		}
	}
	snake->next = NULL;
	delete (snake->next);
	snakeNum++;
	//free(snake->next);	
}

1.2 蛇移动

代码比较简单,就不解释了,这里的移动不是指控制的移动
注:局部变量,私有成员变量,经常在前加’_'或者’m__'表示访问权限和作用空间

/*
功能: 	- 蛇移动:
		@
描述	:
示例	:s.moveUp(s1);;
*/
void Snake::Move_snake(Node* snake)
{
	static char  _x, _y, _x1, _y1;
	_x = snake->x;
	_y = snake->y;
	while (snake = snake->next)
	{
		_x1 = snake->x;
		_y1 = snake->y;
		snake->x = _x;
		snake->y = _y;
		_x = _x1;
		_y = _y1;
	}
}

这个才是真的控制移动,其实做的时候没有要分离出来,直接放到switch中就行。继续传递蛇节点

//向上移动 'w'
void Snake::moveUp(Node* snake)
{
	snake->y--;
}
//向下移动 's'
void Snake::moveDown(Node* snake)
{
	snake->y++;
}
//向左移动 'a'
void Snake::moveLeft(Node* snake)
{
	snake->x--;
}
//向右移动 'd'
void Snake::moveRight(Node* snake)
{
	snake->x++;
}

1.3 蛇身体的定位

这里理解下蛇身体的增长是怎么一回事,屁股跟着脑壳动

/*
功能: 	- 蛇长出的身体定位:
		@
描述	:
示例	:
*/
void Snake::Growth(char ch, Node* snake)
{
	char _x = snake->x;
	char _y = snake->y;
	snake = snake->next;
	switch (ch)
	{
	case 'w':
		snake->x = _x;
		snake->y = ++_y;
		break;
	case 's':
		snake->x = _x;
		snake->y = --_y;
		break;
	case 'a':
		snake->x = ++_x;
		snake->y = _y;
		break;
	case 'd':
		snake->x = --_x;
		snake->y = _y;
		break;
	default:
		break;
	}
	snake->next = NULL;
}

1.4 录用蛇

以上撸清蛇的逻辑,需要录用蛇打印到地图上

/*
功能: 	- 录用蛇:
		@
描述	:
示例	:
*/
void Snake::Entry_snake(Node* snake)
{
	Map();
	map[snake->y][snake->x] = '@';
	while (snake = snake->next)
		map[snake->y][snake->x] = '+';
	map[food.y][food.x] = '$';
}

1.5 测试蛇对象的正常工作

现测试蛇是否正常运动,随便跑跑意思下,确认蛇有没有问题

void SnakeTest()
{
	Snake s;
	Node* s1 = new Node;
	//Node* s2 = new Node;
	s.Map();
	s.Create_snake(s1);

	//s.Create_snake(s2);
	//s.Entry_snake(s2);
	s.Refresh_Food(s1);
	for (int i = 0; i < 3; i++)
	{
		s.Move_snake(s1);
		s.moveUp(s1);
		s.Entry_snake(s1);
		Sleep(100);
		s.Show_map();
	}
	for (int i = 0; i < 3; i++)
	{
		s.Move_snake(s1);
		s.moveDown(s1);
		s.Entry_snake(s1);
		Sleep(100);
		s.Show_map();
	}
	for (int i = 0; i < 3; i++)
	{
		s.Move_snake(s1);
		s.moveLeft(s1);
		s.Entry_snake(s1);
		Sleep(100);
		s.Show_map();
	}
	for (int i = 0; i < 3; i++)
	{
		s.Move_snake(s1);
		s.moveRight(s1);
		s.Entry_snake(s1);
		Sleep(100);
		s.Show_map();
	}
	delete s1;
}

因为还在demo,所以暂时全塞到public上

#ifndef __SNAKE_H
#define __SNAKE_H

const int N = 30;  //地图的宽
const int M = 81;  //地图的长
//const int N = 20;  //地图的宽
//const int M = 61;  //地图的长
//蛇节点的结构
struct Node
{
	char x;
	char y;
	struct Node* next;
};

//食物的结构
struct Food
{
	char x;
	char y;
};

class Snake
{
public:
	Snake();
	Snake(int);
	~Snake();

	void Create_snake(Node*);	//创建蛇
	void Move_snake(Node*);		//移动蛇
	void Growth(char, Node*);	//给新长出来的身子定位

	void moveUp(Node*);			//向上移动
	void moveDown(Node*);		//向下移动
	void moveLeft(Node*);		//向左移动
	void moveRight(Node*);		//向右移动

	void Entry_snake(Node*);	//录入蛇

	void Refresh_Food(Node*);	//刷新食物

	void Map();					//贪吃蛇的地图
	void Show_map();			//输出函数

	int* snake_length;			//蛇的长度
	int snakeNum = 1;			//蛇的数量 //暂时没做两条蛇

	Food food;					//定义食物的结构

private:

};
#endif // !__SNAKE_H

2 食物

2.1食物结构

食物比较简单就两坐标

//食物的结构
struct Food
{
	char x;
	char y;
};

2.2 更新食物

蛇吃到食物就更新

/*
功能: 	- 更新食物:
		@
描述	:
示例	:
*/
void Snake::Refresh_Food(Node* snake)
{
	food.x = rand() % 78 + 1;
	food.y = rand() % 28 + 1;
	while (snake)
	{
		if (food.x == snake->x && food.y == snake->y)
		{
			Refresh_Food(snake);
		}	
		snake = snake->next;
	}
}

3 Game

我怕自己乱,直接拉出一个游戏类,在里面就只有
1): 蛇的初始化
2) :控制蛇
3) : 判蛇是否死亡

3.1 蛇的初始化

初始化要做的就是把蛇创建出来,开始刷新一个食物,并刷新地图

/*
功能: 	- 构造:Game()
		@
描述	:
示例	:
*/
Game::Game()
{
	snake = new Node;
	s.Map();
	s.Create_snake(snake);

	s.Refresh_Food(snake);

	s.Entry_snake(snake);
	Sleep(100);
	s.Show_map();
}

3.2 控制蛇

获取键盘的键值,进行判断处理。
别用goto尽量,这个只是方便,代码不多就直接用上了

/*
功能: 	- 控制蛇:
		@
描述	:
示例	:
*/
void Game::Control()
{
	while (!_kbhit());
	char c = _getch();
	char nextC;
z:	s.Move_snake(snake);
	switch (c)
	{
	case 'w':	s.moveUp(snake);		break;	//向上移动
	case 's':	s.moveDown(snake);		break;	//向下移动
	case 'a':	s.moveLeft(snake);		break;	//向左移动
	case 'd':	s.moveRight(snake);		break;	//向右移动
	default:							break;
	}
	died(c);
	s.Entry_snake(snake);	//录用蛇
	s.Show_map();			//更新地图
	if (_kbhit())
	{
		nextC = _getch();
		if (nextC == 'w' || nextC == 'a' || nextC == 'd' || nextC == 's')
		{
			if ((nextC == 'w' && c == 's') || (nextC == 's' && c == 'w') || (nextC == 'a' && c == 'd') || (nextC == 'd' && c == 'a'))
				nextC = c;
			else
				c = nextC;
		}
	}
	else
		nextC = c;

	//回咬会死
	goto z;
}

3.3 判断蛇是否还活着

蛇怎么死的?
自己咬自己?
撞墙死?
还有别的死法吗?
这是单人的,别想太多,双人感觉还是拿引擎做来的快。unity处理比这个简单多了。
直接贴代码

/*
功能: 	- 判断蛇存活:
		@
描述	:
示例	:
*/
void Game::died(char c)
{
	Node* _snake = snake;
	if ((snake->x == s.food.x) && (snake->y == s.food.y))
	{
		while (_snake->next)
			_snake = _snake->next;
		_snake->next = (Node*)malloc(sizeof(Node));
		s.Growth(c, _snake);
		s.snake_length++;
		if (s.snake_length[s.snakeNum - 1] == (N - 2) * (M - 3))
		{
			fprintf(stderr, "恭喜你成功通过,你太牛了!!!");
			exit(EXIT_FAILURE);
		}
		s.Refresh_Food(snake);
		return;
	}
	if ((snake->x == 0) || (snake->x == (M - 2)) || (snake->y == 0) || (snake->y == (N - 1)))
	{
		fprintf(stderr, "你干嘛撞墙啊");
			exit(EXIT_FAILURE);
	}
	while (_snake = _snake->next)
	{
		if (snake->x == _snake->x && snake->y == _snake->y)
		{
			fprintf(stderr, "你TMD自己撞自己干嘛,不能往回退");
				exit(EXIT_FAILURE);
		}
	}
}

Game类的头文件

#ifndef __GAME_H
#define __GAME_H

#include "Snake.h"

class Game
{
public:
	Game();
	~Game();
	void Control();
	void died(char c);

private:
	Snake s;
	Node* snake;
};
#endif // !__GAME_H

最后就是在主函数中添加以下相关的内容

void GameTest();

int main()
{
	GameTest();
	//cout << "time = " << (double)(clock()) / CLOCKS_PER_SEC << "s" << endl;
	//_CrtDumpMemoryLeaks();
	system("pause");
	return 0;
}

void GameTest()
{
	Game g1;
	while (1)
	{
		g1.Control();
	}
}

运行结果如下:

wsad:控制上下左右在这里插入图片描述
运行成功收工,不要慌,我这里整理了两份自己的源码供参考,都能正常运行
以下给出贪吃蛇源码
贪吃蛇源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高启强不卖鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值