C语言 基于Ncurse库的贪吃蛇游戏项目
为了敲键盘及时响应,需要用到ncurse
测试代码: ncurse1.c
/* ncurse1.c */
#include <curses.h> //ncurse的头文件。int main()
{char c;int i = 0;//ncurse界面的初始化函数。initscr(); for(i=0;i<=2;i++){c = getch();printw("\n");//ncurse下的打印函数。printw("Input: %c\n",c); }//等待用户的输入,没有该函数程序会直接退出,看不到printw()的结果。getch(); //调用程序退出函数,恢复shell终端的显示,没有该函数终端可能会乱码。 endwin(); return 0;
}
//输入指令测试 gcc ncurse1.c -lcurses
游戏的规划实现步骤
- Ncurse 函数初始化
- 边框图像设置 (for循环)
- 贪吃蛇身子节点 (结构体:行,列,下一个节点)
- 第一个节点 (静态设置)
- 设的整的身子 (静态设置)
- 改进 4,5蛇身子的问题, (采用结构体指针指向下一个,尾插法的方式)
- 蛇身子向右移动 move(0,0)光标界面不乱,通过按键key来向右行驶, 尾部加节点,注意必须要释放头节点来达到平衡。
- 贪吃蛇撞壁
- 主动向右行驶,不需要按键key ,边框图像然后refresh()刷新界面。
- 方向键控制蛇变化 , key按键与dir配合,如果key向下,dir就变化,并switch选择增加往那个方向加节点。
- 主函数采用linux两个线程,(按键与自动)同时移动,并且都不退出。
- 贪吃蛇随机位置的设定。
- 咬死自己。
代码:
#include <curses.h>
#include <stdlib.h>#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2struct Snake
{int row;int line;struct Snake* next;
};struct Snake* head = NULL;
struct Snake* tail = NULL;
int key;
int dir;struct Snake food;void initFood()//初始化食物节点
{int x = rand() % 20;//随机出现位置int y = rand() % 20;food.row = x;food.line = y;
}void initNcurse()//初始化curses图形库
{initscr();keypad(stdscr,1);//使用keypad可以在stdscr中接受键盘的功能键noecho();//对于输出的功能键的值不做出回应,以免界面溢出
}int hasSnakeNode(int i,int j)//传参:行号,列号
{struct Snake* p;p = head;while(p != NULL){if(p->row==i && p->line==j){return 1;//gamePic中打印一个节点"[]"}p=p->next;}return 0;
}int hasFood(int i,int j)//打印食物
{if(food.row==i && food.line==j){return 1;}return 0;
}void gamePic()//打印图形界面
{int row;int line;move(0,0);for(row=0;row<20;row++){//第0行为20个"--"if(row==0){for(line=0;line<20;line++){printw("--");}printw("\n");}//第0-19行为左右两边一个"|",中间21个" "(空格)为了对齐第一行if(row>=0 && row<=19){//一共打印20行for(line=0;line<=20;line++){if(line==0 || line==20){printw("|");//打印两边}else if(hasSnakeNode(row,line)){printw("[]");//打印蛇的身体}else if(hasFood(row,line)){printw("##");//打印食物}else{printw(" ");//打印中间}}printw("\n");}//打印第19行,与第一行一样if(row==19){for(line=0;line<20;line++){printw("--");}printw("\n");printw("by Andy,key = %d\n",key);//打印作者名字以及方向键的值}}
}void addNode()//加节点
{struct Snake* new = (struct Snake*)malloc(sizeof(struct Snake));//开辟新节点空间new->next = NULL;switch(dir){//根据用户按的功能键的方向来决定如何添加一个节点case UP://列不变,行减一的位置添加new->row = tail->row - 1;new->line = tail->line;break;case DOWN:new->row = tail->row + 1;new->line = tail->line;break;case LEFT:new->row = tail->row;new->line = tail->line - 1;break;case RIGHT:new->row = tail->row;new->line = tail->line + 1;break;}tail->next = new;tail = new;//新节点变成尾部
}void deleteNode()//删除节点
{struct Snake* p;p = head;head = head->next;free(p);
}void initSnake()//初始化蛇的身体
{struct Snake* p;//使用链表dir = RIGHT;//初始化方向为右while(head != NULL){p = head;head = head->next;free(p);//遍历节点,释放空节点内存}initFood();//初始化食物head = (struct Snake*)malloc(sizeof(struct Snake));//为头节点开辟空间head->row = 1;//第一行head->line = 1;//第一列head->next = NULL;//防止野指针tail = head;//头插法addNode();//加4个节点addNode();addNode();addNode();
}int ifSnakeDie()//蛇死亡的条件
{struct Snake* p;p = head;if(tail->row < 0 || tail->line == 0 || tail->row == 20 || tail->line == 20){//四个边界return 1;}while(p->next != NULL){if(p->row == tail->row && p->line == tail->line){//头咬到尾巴return 1;}p = p->next;}return 0;
}void moveSnake()//蛇的移动
{addNode();if(hasFood(tail->row,tail->line)){initFood();}else{deleteNode();}if(ifSnakeDie()){initSnake();}
}void* refreshBox()//刷新界面
{while(1){moveSnake();//蛇移动gamePic();//重新打印界面refresh();//刷新覆盖原界面usleep(100000);//0.1s}
}void turn(int direction)//绝对值解决方向问题
{if(abs(dir) != abs(direction)){dir = direction;}
}void* changeDir()//根据用户按的功能键走位
{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()
{initNcurse();//第一步:搭建curses环境initSnake();//第三步:打印蛇的身体gamePic();//第二步:打印图形界面pthread_t th1;//线程1pthread_t th2;//线程2pthread_create(&th1,NULL,refreshBox,NULL);//运行线程1:不断刷新界面pthread_create(&th2,NULL,changeDir,NULL);//运行线程2:不断改变方向while(1);//让线程一直进行getch();//让程序一直重复输入功能键还能执行endwin();//curses函数return 0;
}
还需要不断改进。。。。。。