最近在准备学linux操作系统,为了熟练ubuntu虚拟机和对前面C语言和链表的总结,做了一个贪吃蛇小项目练练手,代码如下:(注意:我这里是在linux操作系统下,所以可以使用#include <curses.h>,在标准的C语言编译器好像是编译不过的)
#include <curses.h>
#include <stdlib.h>
#include <pthread.h>
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
struct Snake
{
int x;
int y;
struct Snake *next;
};
/*
struct Snake snake1 = {6,8,NULL};
struct Snake snake2 = {6,9,NULL};
struct Snake snake3 = {6,10,NULL};
*/
/*
snake1.next = &snake2;
snake2.next = &snake3;
*/
struct Snake *head = NULL;
struct Snake *tail = NULL;
int key;
int Flag_dir;
struct Snake food;
void Food_Init(void)
{
int x = rand()%20;
int y = rand()%23;
if (x == 0) x = x + 1;
if (x == 20) x = x - 1;
if (y == 0) y = y + 1;
if (y == 22) y = y - 1;
food.x = x;
food.y = y;
}
void Curses_Init(void)
{
initscr();
keypad(stdscr,1);
noecho();
printw("this is a game\n");
}
int JudgeSnake(int lin,int col)
{
struct Snake *p = head;
while(p != NULL)
{
if ((lin == p->x) && (col == p->y))
return 1;
p = p->next;
}
return 0;
}
int JudgeFood(int lin,int col)
{
if (food.x == lin && food.y == col)
{
return 1;
}
else
{
return 0;
}
}
void AddNode(void)
{
struct Snake *new;
new = (struct Snake*)malloc(sizeof(struct Snake));
if (Flag_dir == RIGHT)
{ new->x = tail->x;
new->y = tail->y+1;
}
if (Flag_dir == UP)
{
new->x = tail->x-1;
new->y = tail->y;
}
if (Flag_dir == DOWN)
{
new->x = tail->x+1;
new->y = tail->y;
}
if (Flag_dir == LEFT)
{
new->x = tail->x;
new->y = tail->y-1;
}
new->next =NULL;
tail->next = new;
tail = new;
}
void DeletNode(void)
{
struct Snake *p = head;
head = head->next;
free(p);
}
void Snake_Init(void)
{
struct Snake *p;
while(head != NULL)
{
p = head;
head = head->next;
free(p);
}
int i = 0;
//Flag_dir = RIGHT;
head = (struct Snake*)malloc(sizeof(struct Snake));
head->x = 6;
head->y = 8;
head->next = NULL;
tail = head;
struct Snake *new;
for (i = 0; i< 3;i++)
{
new = (struct Snake*)malloc(sizeof(struct Snake));
new->x = tail->x;
new->y = tail->y+1;
new->next =NULL;
tail->next = new;
tail = new;
}
/*
AddNode();
AddNode();
AddNode();
*/
}
int JudgeDie(void)
{
struct Snake *p = head;
if ((tail->y == 22) || (tail->x == 0) || (tail->x == 20) || (tail->y == 0))
{
return 1;
}
while(p->next != NULL)
{
if ((tail->x == p->x) && (tail->y == p->y))
return 1;
p = p->next;
}
return 0;
}
void Map_Init(void)
{
int lin,col;
move(0,0);
for (lin = 0;lin <= 20;lin++)
{
for(col = 0;col <= 22;col++)
{
if (JudgeSnake(lin,col))
printw("[]");
//else if (JudgeFood(lin,col))
// printw("$$");
else if (((lin == 0) || (lin == 20)) && (col < 22))
printw("--");
else if (((col == 0)&&(lin != 0)&&(lin != 20)) || ((col == 22)&&(lin != 0)&&(lin != 20)))
printw("|");
else if (JudgeFood(lin,col))
printw("$$");
else
printw(" ");
}
printw("\n");
}
}
void Snake_Move(void)
{
AddNode();
if (JudgeFood(tail->x,tail->y))
{
Food_Init();
}
else
{
DeletNode();
}
if (JudgeDie())
{
Snake_Init();
//Food_Init();
}
}
void* RefIntFace()
{
while(1)
{
Snake_Move();
Map_Init();
refresh();
usleep(100000);
}
}
void JudegTurnDir(int dir)
{
if (abs(Flag_dir) != abs(dir))
{
Flag_dir = dir;
}
}
void* ChangeDir()
{
while(1)
{
key = getch();
switch(key)
{
case KEY_DOWN:/*0402*/printw("KEY_DOWN\n");Snake_Move();Map_Init();JudegTurnDir(DOWN);break;
case KEY_UP:/*0403*/printw("KEY_UP\n");Snake_Move();Map_Init();JudegTurnDir(UP);break;
case KEY_LEFT:/*0404*/printw("KEY_LEFT\n");Snake_Move();Map_Init();JudegTurnDir(LEFT);break;
case KEY_RIGHT:/*0405*/printw("KEY_RIGHT\n");Snake_Move();Map_Init();JudegTurnDir(RIGHT);break;
}
}
}
int main()
{
pthread_t t1;
pthread_t t2;
Curses_Init();
//snake1.next = &snake2;
//snake2.next = &snake3;
Snake_Init();
Map_Init();
pthread_create(&t1,NULL,RefIntFace,NULL);
pthread_create(&t2,NULL,ChangeDir,NULL);
while(1);
// {
// //JudgeDie();
// key = getch();
// //printw("%d\n",c);
// switch(key)
// {
// case KEY_DOWN:/*0402*/printw("KEY_DOWN\n");Snake_Move();Map_Init();Flag_dir = -1;break;
// case KEY_UP:/*0403*/printw("KEY_UP\n");Snake_Move();Map_Init();Flag_dir = 1;break;
// case KEY_LEFT:/*0404*/printw("KEY_LEFT\n");Snake_Move();Map_Init();Flag_dir = 2;break;
// case KEY_RIGHT:/*0405*/printw("KEY_RIGHT\n");Snake_Move();Map_Init();Flag_dir = -2;break;
// }
// }
getch();
endwin();
return 0;
}
接下来,我将对这次贪吃蛇小项目进行代码流程概述和总结:
1.首先,制作一个游戏地图——一个大的矩形框,这里我是用的for循环来打印的,大家可能会想为什么不直接用printf()来打印,直接用printf()可以直观上得到地图,但是对后续操作极其不便,因为这是一个固定地图,不能动态变化,所以我们用for循环加上if-else if语句来动态打印地图(注意:要根据打印出来的地图的样子不断修改)。
2.接下来就是贪吃蛇本身了,游戏中贪吃蛇是由3个[]组成的,在地图中打印3个[]可以通过静态链表和动态链表来完成,毫无疑问我们要选择动态链表。
3.有了地图有了贪吃蛇,我们先不着急有食物,我们先想想怎么让贪吃蛇动起来,这是我们就是引入curses,其作用就是让我们可以用键盘来控制贪吃蛇的上下左右。(注意:它的相关函数不能省略)有了curses我们就可以通过判断上下左右哪个键被按下来让贪吃蛇移动(移动就是增删节点)。这里移动式按一下移动一下,但是我们是需要按一下一直移动的,所以我们要写在while循环里。
4.到了第三步就会发现目前其实是实现不了通过判断上下左右哪个键被按下来让贪吃蛇移动,因为贪吃蛇在动的过程中地图就需要更新一次,所以更新地图这个函数也需要写在while里,但是怎么样才能实现呢?这时候我又引入线程的概念,简单来说就是同时执行两个while循环。
5.有了前面的基础,我们就可以制作食物了,食物同样通过链表来制作,但是食物的坐标不能固定,并且要随机产生,而且每一次蛇头(在链表里是尾节点)碰到食物蛇尾(链表头节点)要加一个节点。
6.根据网上已有的贪吃蛇游戏可知,贪吃蛇碰到墙或者碰到自己身体都是需要重新初始化贪吃蛇的,所以要通过if语句来进行判断,while语句来遍历链表。
以上就是这个贪吃蛇小项目代码编写的思路,下面再来聊一聊项目开发中遇到的一下坑吧,.也是要值得注意的小细节:
1.之前初始化贪吃蛇时Snake_Init,调用AddNode函数可以实现打印贪吃蛇身子,但是在AddNode里面加入方向判断后打印会出错。解决方法:在Snake_Init中使用for循环来打印或给定一个初始方向。
2.打印地图时要多留意一些条件,有时一个条件并不能满足要求。
3.打印食物时,要分析else if 的先后顺序,放在打印地图的后面的话会导致食物出现在地图边界上,再一个就是随机产生数时要考虑边界问题,不然会出现食物消失这种bug。
这个贪吃蛇项目总结到此就结束了,希望对大家有所帮助。