文章目录
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:控制上下左右
运行成功收工,不要慌,我这里整理了两份自己的源码供参考,都能正常运行
以下给出贪吃蛇源码
贪吃蛇源码