基于Ncueses库的贪吃蛇小游戏
1、项目简介
-
视频演示:
https://www.bilibili.com/video/BV1pt421a7Nu/?spm_id_from=333.999.0.0&vd_source=82b65865be0947de29bd55efc8cdb40a
-
编译环境:linux(Vmware16.2.4 + Ubantu20.04.3);
-
小蛇🐍只能在固定的范围内活动;
-
可以利用键盘方向键控制小蛇🐍的前进方向;
-
活动范围内会随机生成食物;
-
小蛇🐍吃到食物,身体将会变长;
-
小蛇🐍咬到自己或者碰到墙壁将会死亡,重新开始游戏;
完整代码文末奉上,欢迎大家一起交流谈论!
2、项目演示&整体框架
-
-
框架
3、项目介绍
为了方便理解,我将从
main()
函数程序的顺序开始讲起,慢慢将各各函数扩展开;
-
main()
函数主要分为4块-
initNcuse()
//初始化ncuses,页面以及按键; -
initSnake()
//小蛇🐍的初始化和食物🍔的初始化 -
gamePic()
//初始化地形、遍历各各点、打印边界、小蛇🐍 和食物🍔 -
pthread两个进程
-
refreshJieMian()
//不断刷新页面 -
changeDir()
//不断的检测按键
-
- else
-
3.1、头文件&宏定义&全局变量
- 本项目所需的头文件、宏定义和全局变量程序如下
//头文件&宏定义&全局变量 #include<curses.h> //图形库 #include<stdlib.h> #include<unistd.h> //sheep(1) #include<pthread.h> //多线程的头文件 /* #define UP 1 #define DOWN 2 #define LEFT 3 #define RIGHT 4 *///利用正负的差别,方便为后面控制蛇,不能往倒着走 #define UP 1 #define DOWN -1 #define LEFT 2 #define RIGHT -2 struct Snake { int hang; int lie; struct Snake *next; }; int key; int dir; struct Snake *head = NULL; struct Snake *tail = NULL; struct Snake food;
3.2、初始化ncuses,页面以及按键initNcuse()
- 分为两步,均是ncurse库的函数
- 第一:初始化ncurse页面
- 第二:允许按键的使用
-
initNcuse()
void initNcuse() { initscr(); //初始化ncurse页面 keypad(stdscr,1);//允许使用功能键 // noecho();//不把无关的信息,功能类的信息显示在页面上 }
3.3、小蛇🐍的初始化和食物🍔的初始化initSnake()
- 分为5步
- 第一:初始化小蛇的运动方向;
- 第二:清空节点,仅留一个头;
- 第三:随机生成食物;
- 第四:设置小蛇起始位置;
- 第五:初始化小蛇长度;
-
initSnake()
void initFood() //随机生成食物//子函数 { int x = rand()%20; int y = rand()%20; food.hang = x; food.lie = y; } void addNode() //根据方向添加节点,一节🐍//子函数 { struct Snake *new = (struct Snake*)malloc(sizeof(struct Snake)); new->next = NULL; switch(dir) { case UP: new->hang = tail->hang-1; new->lie = tail->lie; break; case DOWN: new->hang = tail->hang+1; new->lie = tail->lie; break; case LEFT: new->hang = tail->hang; new->lie = tail->lie-1; break; case RIGHT: new->hang = tail->hang; new->lie = tail->lie+1; break; } tail->next = new; tail = new; } void initSnake()//初始化 { struct Snake *p; dir = RIGHT;//刚开始的方向是向右边 while(head!=NULL) { p = head; //head向下一个结点移一下,直到最后一个,将所有结点清空,只留一个 head=NULL head = head->next; free(p); } initFood();//随机生成食物 head = (struct Snake*)malloc(sizeof(struct Snake)); head->hang = 1; //🐍的起始位子 head->lie = 1; head->next = NULL; tail = head; addNode(); addNode(); addNode(); addNode(); }
3.4、初始化地形、遍历各各点、打印边界、小蛇🐍 和食物🍔gamePic()
- 分为三步:
- 第一:初始化光标;
- 第二:遍历各点,打印所需;
- 第三:打印作者及食物位置;
-
gamePic()
int hasSnakeNode(int i,int j) //这个点有🐍吗?有返回1, 没有返回0 { struct Snake *p; p = head; while(p != NULL) //遍历🐍的身子,是不是这个点 { if(p->hang==i && p->lie==j) { return 1; } p = p->next; } return 0; } int hasFood(int i,int j)//这个点是不是食物所在的点,有的返回1; { if(food.hang == i && food.lie == j)//吃到了返回1 { return 1; } return 0;//没吃到,返回0 } void gamePic() { int hang,lie; move(0,0);//将光标设定在(0,0) for(hang=0;hang<20;hang++) { if(hang==0)//第一行,打印“------” { for(lie=0;lie<20;lie++) { printw("--"); } } if(hang>0 && hang<=19) //打印左右界限 { printw("\n"); for(lie=0;lie<=20;lie++) { if(lie==0 || lie==20) { printw("|"); } else if(hasSnakeNode(hang,lie))//🐍的位置是不是这个点,打印身子 { printw("[]"); } else if(hasFood(hang,lie))//打印食物的位置 { printw("##"); } else { printw(" "); } } } if(hang == 19)//最后一行,打印边界“------” { printw("\n"); for(lie=0;lie<20;lie++) { printw("--"); } } } printw("\nBy TP, food.hang=%d,food.lie=%d\n\n",food.hang,food.lie); }
3.5 两个进程pthread
-
refreshJieMian()
//不断刷新页面-
moveSnake()
- 添加蛇头节点;
-
hasFood()
//有没有吃到食物 -
ifSnakeDie
//小蛇的死亡条件
-
gamePic()
//同3.4; -
refresh()
//ncurses库的文件内置函数,只有某次缓存与上次不一样,才输出缓冲区的值; -
usleep()
//让该进程休眠指定us;
-
-
changeDir()
//不断的检测按键-
getch()
//获取键盘输入; - 根据键盘输入,改变方向;
-
-
其他
-
while(1)
//锁定程序,别退了; -
getch()
//等待键盘输入不让程序推出; -
endwin()
//恢复终端
-
-
pthread
文章来源:https://www.toymoban.com/news/detail-832938.htmlvoid deleNode()//删除🐍尾巴的节点,动起来 { struct Snake *p; p =head; head = head->next; free(p); } void moveSnake() //吃到了食物与没吃到食物,蛇的长度和位置的变化;蛇死了怎么办; { addNode(); //添加节点 if(hasFood(tail->hang,tail->lie)) // 尾结点(头),是不是食物所在的点 { initFood(); // 是--重新创建食物--且不删除head(蛇尾) 结点 } else { deleNode();//删除 } if(ifSnakeDie()) //如果🐍死了 { refresh();//Curses库,屏幕刷新 usleep(100000);//暂停100000us initSnake();//小蛇🐍初始化和食物🍔初始化 } } int ifSnakeDie()//🐍的死亡条件 { struct Snake *p; p = head; if(tail->hang==0 || tail->lie==0 || tail->hang==20 || tail->lie==20)//撞墙 { return 1; } while(p->next != NULL)//从到了自己吗 { if(p->hang == tail->hang && p->lie == tail->lie) { return 1; } p = p->next; } return 0; } void turn(int direction)//防止🐍上下往回走,左右走; { if(abs(dir) != abs(direction))//绝对值不一样,才可以转变方向 { dir = direction; } } void *refreshJieMian()//进程1 刷新界面 { while(1) { moveSnake(); //前进一格, gamePic(); refresh(); usleep(100000); //0.1s,通过多线程,刷新界面,也起到了调速的作用; } } void *changeDir() //进程2 通过按键改方向 { while(1) { key = getch(); switch(key) { case KEY_DOWN: turn(DOWN); break; case KEY_UP: turn(UP); break; case KEY_LEFT: turn(LEFT); break; case KEY_RIGHT: turn(RIGHT); break; } } } *///将这些放在main函数里面 pthread_t t1; //定义进程 1 pthread_t t2; //定义进程 2 pthread_create(&t1,NULL,refreshJieMian,NULL);//不断的刷新界面 pthread_create(&t2,NULL,changeDir,NULL); //不断的检测按键输入 while(1); getch(); //等待键盘输入,不让程序退出 endwin(); //恢复终端 */
3.6、main()
int main()
{
pthread_t t1; //定义进程 1
pthread_t t2; //定义进程 2
initNcuse(); //初始化ncuses,页面以及按键
initSnake(); //🐍的初始化和食物
gamePic(); //初始化地形,遍历各各点,打印边界、🐍、和食物
pthread_create(&t1,NULL,refreshJieMian,NULL);//不断的刷新界面
pthread_create(&t2,NULL,changeDir,NULL); //不断的检测按键输入
while(1);
getch(); //等待键盘输入,不让程序退出
endwin(); //恢复终端
return 0;
}
4、代码整合
- 283行
//snake.c
#include<curses.h> //图形库
#include<stdlib.h>
#include<unistd.h> //sheep(1)
#include<pthread.h>
/*
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
*/
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
int key;
int dir;
struct Snake *head = NULL;
struct Snake *tail = NULL;
struct Snake food;
void initFood() //随机生成食物
{
int x = rand()%20;
int y = rand()%20;
food.hang = x;
food.lie = y;
}
void initNcuse()
{
initscr(); //初始化ncurse页面
keypad(stdscr,1);//允许使用功能键
// noecho();//不把无关的信息,功能类的信息显示在页面上
}
int hasSnakeNode(int i,int j) //这个点有🐍吗?有返回1, 没有返回0
{
struct Snake *p;
p = head;
while(p != NULL) //遍历🐍的身子,是不是这个点
{
if(p->hang==i && p->lie==j)
{
return 1;
}
p = p->next;
}
return 0;
}
int hasFood(int i,int j)//这个点是不是食物所在的点,有的返回1;
{
if(food.hang == i && food.lie == j)//吃到了返回1
{
return 1;
}
return 0;//没吃到,返回0
}
void gamePic()
{
int hang,lie;
move(0,0);//将光标设定在(0,0)
for(hang=0;hang<20;hang++)
{
if(hang==0)//第一行,打印“------”
{
for(lie=0;lie<20;lie++)
{
printw("--");
}
}
if(hang>0 && hang<=19) //打印左右界限
{
printw("\n");
for(lie=0;lie<=20;lie++)
{
if(lie==0 || lie==20)
{
printw("|");
}
else if(hasSnakeNode(hang,lie))//🐍的位置是不是这个点,打印身子
{
printw("[]");
}
else if(hasFood(hang,lie))//打印食物的位置
{
printw("##");
}
else
{
printw(" ");
}
}
}
if(hang == 19)//最后一行,打印边界“------”
{
printw("\n");
for(lie=0;lie<20;lie++)
{
printw("--");
}
}
}
printw("\nBy TP, food.hang=%d,food.lie=%d\n\n",food.hang,food.lie);
}
void addNode() //根据方向添加节点,一节🐍
{
struct Snake *new = (struct Snake*)malloc(sizeof(struct Snake));
new->next = NULL;
switch(dir)
{
case UP:
new->hang = tail->hang-1;
new->lie = tail->lie;
break;
case DOWN:
new->hang = tail->hang+1;
new->lie = tail->lie;
break;
case LEFT:
new->hang = tail->hang;
new->lie = tail->lie-1;
break;
case RIGHT:
new->hang = tail->hang;
new->lie = tail->lie+1;
break;
}
tail->next = new;
tail = new;
}
void initSnake()//初始化
{
struct Snake *p;
dir = RIGHT;//刚开始的方向是向右边
while(head!=NULL)
{
p = head; //head向下一个结点移一下,直到最后一个,将所有结点清空,只留一个 head=NULL
head = head->next;
free(p);
}
initFood();//随机生成食物
head = (struct Snake*)malloc(sizeof(struct Snake));
head->hang = 1; //🐍的起始位子
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deleNode()//删除🐍尾巴的节点,动起来
{
struct Snake *p;
p =head;
head = head->next;
free(p);
}
int ifSnakeDie()//🐍的死亡条件
{
struct Snake *p;
p = head;
if(tail->hang==0 || tail->lie==0 || tail->hang==20 || tail->lie==20)//撞墙
{
return 1;
}
while(p->next != NULL)//从到了自己吗
{
if(p->hang == tail->hang && p->lie == tail->lie)
{
return 1;
}
p = p->next;
}
return 0;
}
void moveSnake() //吃到了食物与没吃到食物,蛇的长度和位置的变化;蛇死了怎么办;
{
addNode(); //添加节点
if(hasFood(tail->hang,tail->lie)) // 尾结点(头),是不是食物所在的点
{
initFood(); // 是--重新创建食物--且不删除head(蛇尾) 结点
}
else
{
deleNode();//删除
}
if(ifSnakeDie()) //如果🐍死了
{
refresh();//Curses库,屏幕刷新
usleep(100000);//暂停100000us
initSnake();//小蛇🐍初始化和食物🍔初始化
}
}
void *refreshJieMian()//进程1 刷新界面
{
while(1)
{
moveSnake(); //前进一格,
gamePic();
refresh();
usleep(100000); //0.1s,通过多线程,刷新界面,也起到了调速的作用;
}
}
void turn(int direction)//防止🐍上下往回走,左右走;
{
if(abs(dir) != abs(direction))//绝对值不一样,才可以转变方向
{
dir = direction;
}
}
void *changeDir() //进程2 通过按键改方向
{
while(1)
{
key = getch();
switch(key)
{
case KEY_DOWN:
turn(DOWN);
break;
case KEY_UP:
turn(UP);
break;
case KEY_LEFT:
turn(LEFT);
break;
case KEY_RIGHT:
turn(RIGHT);
break;
}
}
}
int main()
{
pthread_t t1; //定义进程 1
pthread_t t2; //定义进程 2
initNcuse(); //初始化ncuses,页面以及按键
initSnake(); //🐍的初始化和食物
gamePic(); //初始化地形,遍历各各点,打印边界、🐍、和食物
pthread_create(&t1,NULL,refreshJieMian,NULL);//不断的刷新界面
pthread_create(&t2,NULL,changeDir,NULL); //不断的检测按键输入
while(1);
getch(); //等待键盘输入,不让程序退出
endwin(); //恢复终端
return 0;
}
欢迎大家一起交流讨论!文章来源地址https://www.toymoban.com/news/detail-832938.html
到了这里,关于【C语言】做一个贪吃蛇小游戏,完整代码&附带视频演示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!