# 贪吃蛇开发日志(装模作样)-<1.2>-(已完结)

191 阅读5分钟

贪吃蛇开发日志(装模作样)

1. 2020/4/13 14:55 写完墙壁生成代码后测试时遇到第一个问题

问题: 1>------ 已启动全部重新生成: 项目: 贪吃蛇(霁), 配置: Debug Win32 ------ 1>game.cpp

1>game.obj : error LNK2019: 无法解析的外部符号 "public: void __thiscall Wall::initWall(void)" (?initWall@Wall@@QAEXXZ),该符号在函数 _main 中被引用 1>game.obj : error LNK2019: 无法解析的外部符号 "public: void __thiscall Wall::printWall(void)" (?printWall@Wall@@QAEXXZ),该符号在函数 _main 中被引用 1>C:\Users\Young\source\repos\贪吃蛇(霁)\Debug\贪吃蛇(霁).exe : fatal error LNK1120: 2 个无法解析的外部命令

问题原因:经过几个测试发现,原来定义类中函数忘记声明所属

2. 2020/4/13 15:17 解决了问题1后成功运行代码,但运行结果出现问题

问题:生成墙壁总是半边

问题原因:数组 s[10][10]中其实是0-9,而不是0-10.

3.2020/4/13 16:03 先是发现打印墙的同时也就打印了蛇,于是将蛇和墙的打印统一为printMap,后发现墙类生成可以合并到map生成,于是想将蛇并入map类下来

减少代码,但发现无法继承部分属性

问题:想使用继承语法,让子类class snake 继承基类class map中的数组 mapArray[][],但未能实现,最终还是将mapArray[][]变为全局变量,以此来解决。

2020/4/13 16:33,耗时2小时30分钟,完成地图和蛇的打印。

4.2020/4/13 18:25 碰到关键问题,没法实现蛇的运动

我将地图设置为30*30 的数组,蛇设为[n]的数组,通过定义其中3个连续数组的位置来显示蛇,再通过改变蛇[0]的位置,不停刷新地图,再讲蛇身体的数组坐标往上一个数组挪动来移动蛇,但却未能实现,不太清楚问题出在哪里,我将目前的代码贴在下面

#include <iostream>
#include <deque>
#include <windows.h>                
#include <conio.h>                  //包含 _getch() 和 _kbhit()
#include <ctime>
using namespace std;

#define N 200  //给蛇的节数取N,并初步定位200节
#define row 30
#define col 30
 //方向键对应的的ASCII为 72 80 75 77

enum direction { Up = 72, Down = 80, Left = 75, Right = 77 };
char MapArray[row][col];//地图的数组,将地图化为30*30的数组来定位
struct Coor //坐标的结构体,方便于定位每一节蛇的坐标
{
public:
	int x, y;
};
class Snake
{
public:
	int n ;//蛇有几节
	struct Coor szb[N];//蛇的坐标
	direction dire_Snake;
	void initSnake();//初始化蛇
	void printSnake();
	void SnakeMove();//蛇移动的函数
};
class Map
{
public:
	void initWall();//初始化墙,产生边缘地图和中间空白
	void printWall();//打印出墙(地图)
	void printMap();
};
class Food
{
public:
	int x, y;
	void setFood();
	bool isEat;
};
//设置坐标类
Snake snake_temp;
Food food_temp;

//墙类

//初始化地图 ,对上下左右边生成*
void Map::initWall()
{
	for (int i = 0; i < row; i++)
	{
		MapArray[i][0] = MapArray[i][row-1] = '*';
	}
	for (int j = 0; j < col; j++)
	{
		MapArray[0][j] = MapArray[col - 1][j] = '*';
	}
	for (int i = 1; i < row - 2; i++)
	{
		for (int j = 1; j < col -2; j++)
		{
			MapArray[i][j] = ' ';
		}
	}
}
//打印出地图,包括了蛇和墙和食物,main()中可用于刷新
void Map::printMap()
{
	system("cls");
	for (int i = 1; i < snake_temp.n; i++)
	{
		MapArray[snake_temp.szb[i].x][snake_temp.szb[i].y] = '#';
	}
	MapArray[snake_temp.szb[0].x][snake_temp.szb[0].y] = '@';
	
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			cout << MapArray[i][j]<<" ";
		}
		cout << endl;
	}
	MapArray[food_temp.x][food_temp.y] = '$';
}
//蛇类

void Snake::initSnake()//初始化蛇类,给蛇定位3节,并定出3节的位置
{
	snake_temp.n = 3;
	snake_temp.dire_Snake = Down;
	snake_temp.szb[0].x = snake_temp.szb[1].x= snake_temp.szb[2].x=10;
	snake_temp.szb[0].y = 10;
	snake_temp.szb[1].y =  9;
	snake_temp.szb[2].y = 8;
}
void Snake::SnakeMove()//实现蛇的移动,原理是将每一节蛇的坐标赋值给下一节,这样控制第一节蛇(蛇头)即可
{
	MapArray[snake_temp.szb[snake_temp.n - 1].x][snake_temp.szb[snake_temp.n - 1].y] = ' ';
	for (int i = snake_temp.n - 1; i > 0; i--)
	{
		snake_temp.szb[i].x = snake_temp.szb[i - 1].x;
		snake_temp.szb[i].y = snake_temp.szb[i - 1].y;
	}
	
	switch (snake_temp.dire_Snake)
	{
	case Up:snake_temp.szb[0].x -= 1; break;
	case Down:snake_temp.szb[0].x += 1; break;
	case Right:snake_temp.szb[0].y += 1; break;
	case Left: snake_temp.szb[0].y -= 1; break;
	}
}
void Food::setFood()//设置食物,有2种方法
{
	do
	{
		food_temp.x = rand() % 28 + 1;
		food_temp.y = rand() % 28 + 1;
		food_temp.isEat = false;
	} while (MapArray[food_temp.x][food_temp.y] != ' ');
	//下面的是产生食物的第二种写法
	//int flag = 0;     
	//while (1)
	//{
	//	food_temp.x = rand() % 28 + 1;
	//	food_temp.y = rand() % 28 + 1;
	//	for (int i = 0; i < n; i++)
	//	{
	//		if (food_temp.x == snake_temp.szb[i].x&&food_temp.y == snake_temp.szb[i].y)
	//		{
	//			flag = 0;
	//			break;
	//		}
	//		else
	//		{
	//			flag = 1;
	//		}
	//		if (flag)
	//		{
	//			continue;
	//		}
	//		else
	//		{
	//			break;
	//		}
	//	}
	//}
	//MapArray[food_temp.x][food_temp.y] = '$';
}
//改变方向
void changeDire()//改变方向,通过判断键盘输入信号的函数_getch()和方向键的ASCII码值来改变蛇头移动方向
	char key;
    key = _getch();
	switch (key)
	{
	case Up:
		if (snake_temp.dire_Snake != Down)
		{
			snake_temp.dire_Snake = Up;
		}
		break;
	case Down:
		if (snake_temp.dire_Snake != Up)
		{
			snake_temp.dire_Snake = Down;
		}
		break;
	case Right:
		if (snake_temp.dire_Snake != Left)
		{
			snake_temp.dire_Snake = Right;
		}
		break;
	case Left:
		if (snake_temp.dire_Snake != Right)
		{
			snake_temp.dire_Snake = Left;
		}
		break;
	}
}
void Eatfood()//判断是否吃到,吃到则增加一节,并给出信号需要重新产生食物——food_temp.isEat = true
{
	if (food_temp.x == snake_temp.szb[0].x && food_temp.y == snake_temp.szb[0].y)
	{
		snake_temp.n ++;
		food_temp.isEat = true;
	}
}
int Gameover()
{
	if (snake_temp.szb[0].x == 0 || snake_temp.szb[0].x == row || snake_temp.szb[0].y == col || snake_temp.szb[0].y == 0)
	{
		return 1;
	}
	for (int i = snake_temp.n; i > 0; i--)
	{
		if (snake_temp.szb[0].x == snake_temp.szb[i].x && snake_temp.szb[0].y == snake_temp.szb[i].y)
		{
			return 1;
		}
	}
}
int main()
{
	srand((unsigned)time(NULL));
	Map map;
	map.initWall();//生成墙壁和地图
	snake_temp.initSnake();//生成蛇
	food_temp.isEat = true;
	while (1)
	{
		if (food_temp.isEat == true)
		{
			food_temp.setFood();
		}
		map.printMap();
		snake_temp.SnakeMove();
		Eatfood();
		if (Gameover())
		{
			break;
		}
		Sleep(50);
		while (_kbhit())
		{
			changeDire();
		}
	}
	cout << "再接再厉" << endl;
	return 0;
}

希望能尽快想出解决方法。

4.2020/4/13 19:48 终于解决了蛇行走的问题,万岁!接下来都比较简单了,就是设置随机数种子,生成食物,判断蛇头坐标和食物以及蛇身坐标以及墙壁坐标是否重合来判断是吃了还是死了,明天进行余下的步骤。我会把正确的代码覆盖之前错误的。

4.2020/4/14 10:30 经过2个小时奋斗,以及将贪吃蛇雏形做出来了,但可惜采取的是刷新地图来达到移动的效果,所以实际体验比较差,等学完数据结构和算法后, 会尝试用链表等知识去写个俄罗斯方块或者扫雷。代码已更新为完成后的。

总结:

这次自己写贪吃蛇,期间还是因为遇到困难而且参考了一些别人的代码,期间遇到几个问题,自我感觉最需要记住的,就是数组中数字的位置的值,因为搞错数值而导致遍历的时候的效果和需要的不同,或者检测代码无误,但已一运行就崩溃,这一点需要注意。

编码期间还体会到了知识掌握的不足,enum枚举,_getch(),_kbhit()这些知识之前都未了解,看了别人代码才知道还可以有控制帧数来增大难度,甚至可以windows自带的图像处理来显示蛇,这样不会像我这个不停刷新界面,眼睛都闪的难受,可以改进的地方还有很多。

学无止境。