为了敲键盘及时响应,需要用到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 -2
struct 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;//线程1
pthread_t th2;//线程2
pthread_create(&th1,NULL,refreshBox,NULL);//运行线程1:不断刷新界面
pthread_create(&th2,NULL,changeDir,NULL);//运行线程2:不断改变方向
while(1);//让线程一直进行
getch();//让程序一直重复输入功能键还能执行
endwin();//curses函数
return 0;
}
还需要不断改进。。。。。。