ncurses上下左右键的获得——>贪吃蛇地图的实现——>显示贪吃蛇的完整身子——>贪吃蛇向右移动——>贪吃蛇撞墙找死——>贪吃蛇自行向右行走与页面一起刷新(利用线程解决)——>贪吃蛇四个方向的自由走位——>贪吃蛇吃饭——>贪吃蛇咬到自己撞墙找死
按照这个项目思路来设计游戏
头文件#include
#include
//获取
void initcurses()
{
initscr();
keypad(stdscr,1);
noecho();
}
地图规划也用函数封装起来!
0和20行以–为界,0和20列以|为界,中间为空格,顺便显示蛇的身子和食物;分三步打印,首先打印第一行与第0列,然后打印1到18行,再打印19行19列;蛇的身子与食物用函数封装起来。
void gamePic()
{
int hang;
int lie;
move(0,0);//ncurses的库函数,用它改变光标位置,使下一次的地图覆盖在上一次的地图上。
for(hang=0;hang<20;hang++){
if(hang==0){
for(lie=0;lie<20;lie++){
printw("--");
}
}
printw("\n");
if(hang>0&&hang<19){
for(lie=0;lie<=20;lie++){//这里<=20刚好对齐第一行第0列
if(lie==0||lie==20){
printw("|");
}else if(hadSnake(hang,lie)){
printw("[]");
}else if(hadFood(hang,lie)){
printw("##");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang==19){
for(lie=0;lie<20;lie++){
printw("--");
}
printw("\n");
printw("by xiewenhui");
}
}
}
1.## 显示蛇的身子需要用到结构体和链表,先声明链表头的指针和链表尾的指针。
#include
int dir;//全局变量,通过按键控制蛇身子的移动
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
Struct Snake *head=NULL;//防止变成野指针内存泄漏。
Struct Snake *tail=NULL;
2.封装一个函数初始化蛇的身子;蛇的身子是通过尾插法插入;动态创建链表来插入
#include
void initSnake()
{
struct Snake *p;
dir=RIGHT;//初始化按键;
while(head!=NULL){//判断蛇是否撞墙找死;然后清理之前的数据,防止内存泄漏
p=head;
p=p->next;
free(p);
}
head=(struct Snake*)malloc(sizeof(struct Snake));
head->hang=1;
head->lie=1;
head->next=NULL;
tail=head;//从尾巴插入,做一个尾巴;
addNode();
addNode();
addNode();
addNode();
addNode();
}
void addNode()
{
struct Snake *p;
new=(struct snake*)malloc(sizeof(struct Snake));
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;
}
}
在地图上显示蛇的身子,封装一个函数判断蛇身子的坐标是否与地图上的坐标对应。
int handSnake(int hang,int lie)
{
struct Snake *p;
p=head;
while(p!=NULL){//如果头节点是一个空指针不用返回行列坐标,因为根本没有节点增加;只有满足非空才能置1。
if(p->hang==hang&&p->lie==lie){
return 1;
}
p=p->next;
}
return 0;
}
蛇的移动是通过在尾部增加一个节点,然后在删除一个节点,这样保持蛇身子保持不变,然后实现蛇的移动效果。蛇的死亡有两种:一是撞墙而死,二是咬到自己而死;代码如下:
#include
void addNode()
{
struct Snake *p;
p=head;
new=(struct Snake*)malloc(sizeof(struct Snake));
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;
}
}
void deletNode()
{
struct Snake *P;
p=head;
head=head->next;
free(p);//防止游戏运行过久导致内存被撑爆,清除掉。
}
int ifSnakeDie()
{
struct Snake *p;
p=head;
if(tail->hang==0||tail->hang==20||tail->lie==0||tail->lie==20){
return 1;
}
while(p->next!=NULL){//每次遍历到最后一个节点会一直死,所以只要遍历到第二个节点就可以
if(p->hang==tail->hang&&p->lie==tail->lie){
return 0;
}
p=p->next;
}
}
void moveSnake()
{
addNode()
deleteNode();
if(ifSnakeDie()){
initSnake();
}
}
如果不引入线程概念,他只能运行一个函数,比如他只能运行刷新界面的函数,根本不会运行按键这个函数,那么这样的话,这个游戏根本没有任何的意义,所以这个游戏就没必要做了。我们解决这个问题的话引进线程的概念,同时运行两个函数,引用线程的库
代码如下:
主函数:
#include
int main()
{
pthread_t t1; //定义两个地址
pthread_t t2;
iniNcurses();//初始化按键函数
initSnake();//初始化贪吃蛇
initFood();//初始化食物
gamePic();//地图扫描
pthread_create(&t1,NULL,refreshjiemian,NULL);//线程1界面函数
pthread_create(&t2,NULL,chageDir,NULL);//线程2按键函数
while(1);//死循环,防止退出
getch();//获取按键
endwin();//nuurses库自带
return 0;
}
界面函数
void refreshjiemain()
{
while(1){
moveSnake();//蛇的移动
gamepic();//地图扫描
refresh();//刷新界面,ncurses自带的函数
usleep(100000);//提升蛇运行的速度
}
}
这段代码的意思是,当你控制按键让蛇移动的时候,按键与地图都会同时刷新,用到ncurses库的refresh函数,然后用usleep提升蛇的速度,用while(1)循环,防止一次就退出。
按键函数
#include
#include
int key//定义一个按键,全局变量;
void chageDir()
{
while(1){
int key;
switch(key){
case KEY_UP:
turn(UP);
break;
case KEY_DOWN:
turn(UP);
break;
case KEY_LEFT:
turn(LEFT);
break;
case KEY_RIGHT:
turn(UP);
break;
}
}
}
解决不合理走位,因为如果贪吃蛇一下子往上又往右,这样的话会显得很奇怪,所以利用绝对值解决这个问题;让上下的绝对值一样,左右的绝对值一样。
代码如下:
#include
#include
#define UP 1 //宏定义四个按键
#define DOWN -1
#define LEFT 2
#define RIGHT -2
void turn(int direction)
{
if(abs(dir)!=abs(direction)){
dir=direction;
}
}
以上代码的意思是,绝对值不相等的时候方向才改变,如果绝对值相等方向不变。
abs是绝对值的意思
食物的节点显示
#include
#include
struct Snake food;
int hasFood(int hang,int lie)
{
if(food.hang==hang&&food.lie==lie){
return 1;
}
return 0;
}
食物的初始化
#include
#include
void hasFood()
{
int x=rand()%20;
int y=rand()%20;
food.hang=x;
food.lie=y;
}
代码如下:
#include
#include
#include
#include
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
void moveSnake()
{
addNode();//增加节点显示贪吃蛇身子,如果要移动的话就要删除节点
if(hasFood()){
initFood();
}else{
deleteNode();
}
if(ifSnakeDie()){
initSnake();
}
}
蛇的死亡和吃饭都是在蛇的移动中实现的,所以必须在这个函数中实现这个功能。
1.通过ncurses库来获取贪吃蛇的功能键与函数
2.地图显示,食物显示,蛇的身子显示
3.蛇的移动和死亡和蛇进行吃饭
根据以上三步来写出贪吃蛇的代码,并实现功能
全部代码如下:
#include
#include
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head=NULL;
struct Snake *tail=NULL;
int dir;
int key;
struct Snake food;
void initFood()
{
int x=rand()%20;
int y=rand()%20;
food.hang=x;
food.lie=y;
}
void addNode()
{
struct Snake *p;
p=head;
new=(struct Snake*)malloc(sizeof(struct Snake));
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+1;
new->lie=tail->lie;
break;
}
}
void initSnake()//初始化的目的是创建链表,先创建头节点,然后从尾巴插入
{
struct Snake *p;
dir=LEFT;//初始化按键
while(head!=NULL){
p=head;
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 deletNode()
{
struct Snake *p;
p=head;
head=head->next;
free(p);
}
int ifSnakeDie()
{
struct Snake *p;
p=head;
if(tail->lie<0||tail->lie==20||tail->hang==0||tail->==20){
return 1;
}
while(p->next!=NULL){
if(p->hang==hang&&p->lie==lie){
return ;
}
p=p->next;
}
return 0;
}
void moveSnake()
{
addNode();
if(hasFood()){
initFood();
}else{
deletNode();
}
if(ifSnakeDie()){
initSnake();
}
}
void refreshjiemian()
{
while(1){
moveSnake();
gamePic();
refresh();
usleep(100000);
}
}
void turn(int direction)
{
if(abs(dir)!=abs(direction)){
dir=direction;
}
}
void chageDir()
{
int key;
switch(key){
case KEY_UP:
trun(UP);
break;
case KEY_DOWN:
trun(DOWN);
break;
case KEY_LEFT:
trun(LEFT);
break;
case KEY_RIGHT:
trun(RIGHT);
break;
}
}
void initNcurses()//初始化功能键函数
{
initscr();//初始化屏幕
keypad(stdscr,1);//在std中接受功能键
noecho();//控制输入的字符
}
int hasSnake(int hang,int lie)
{
struct Snake *p;
p=head;
while(p!=NULL){
if(p->hang=hang&&p->lie=lie){
return 1;
}
p=p->next;
}
return 0;
}
int hasFood(int hang,int lie)
{
if(food.hang==hang&&food.lie==lie){
return 1;
}
return 0;
}
void gamePic()
{
int hang;
int lie;
move(0,0);//锁定光标在(0,0);
if(hang==0){
for(lie=0;lie<20;lie++){
printw("--");
}
printw("\n");
if(hang>0&&hang<19){
for(lie=0;lie<=20;lie++){//加个等号和第一行对的更准
if(lie==0&&lie==19){
printw("|");
}else if(hasSnake()){
printw("[]");
}else if(hasFood()){
printw("#");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang==19){
for(lie=0;lie<20;lie++){
printw("--")
}
printw("\n");
printw("by xiewenhui\n");
}
}
}
int main()
{
pthread t1;
pthread t2;
initNucrses();
initSnake();
initFood();
gamePic()
pthread_create(&t1,NULL,refreshjiemain,NULL);
pthread_create(&t2,NULL,refreshjiemain,NULL);
while(1);
getch();
endwin();
return 0;
}