基于ncurses的贪吃蛇

本文详细介绍了使用Curses库在终端上开发贪吃蛇游戏的过程,包括初始化curses模式、创建地图、构建蛇身、蛇的移动、方向控制以及碰撞检测。文章还展示了如何通过多线程实现用户输入与蛇的自动移动,以及添加食物元素,让蛇可以增长。在游戏逻辑中,蛇碰到边界或自身时会重新开始。
摘要由CSDN通过智能技术生成

1.curses的基本函数:

                                initscr();    //进入curses模式

                                printw();   //与printf的作用一样

                                getch();     //读取一个字符,如果没有这个函数则不能输出

                                endwin(); //结束curses模式

2.创建地图:

                                

                                地图相当于由20x20个小正方型组成的正方形 

           

void snakMap()
{
        int hang;
        int lie;

        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++){
                                if(lie==0 || lie==20){
                                        printw("|");
                                }
                                else{
                                        printw("  ");
                                }
                        }
                                 printw("\n");

                }
                if(hang==19){
                        for(lie=0;lie<20;lie++){
                                printw("--");
                        }
                        printw("\n");
                }

        }

}

   3.创建蛇身:

首先要创建一个全局变量struct Snak* head当做蛇的头节点,在创建一个struct Snak* tail做尾节点

void creatSnak()
{
    while(head!=NULL){
        struct Snak* p=head;
        head=head->next;
        free(p);
    }
    head=(struct Snak*)malloc(sizeof(struct Snak));
    head->H=2;
    head->L=2;
    head->next=NULL;
    tail=head;

    addNode();        //增加蛇的节点
    addNode();

}

刚开始不考虑方向的情况下(向右)增加蛇的节点:

void addNode()
{

         struct Snak* new=(struct Snak*)malloc(sizeof(struct Snak));
         new->H=tail->H;
         new->L=tail->L+1;   
         new->next=NULL;
         tail->next=new;
         tail=new;
}

创建好蛇身这个链表的的时候,当节点的行和列与地图的行和列对应时就会在地图中把蛇身给显示出来:

int initSnak(int H,int L)
{
        struct Snak* p=head;
        while(p!=NULL){
                if(p->H==H && p->L==L){
                        return 1;
                }
                p=p->next;
        }
        return 0;
}

void snakMap()
{
        int hang;
        int lie;

        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++){
                                if(lie==0 || lie==20){
                                        printw("|");
                                }else if(initSnak(hang,lie)){
                                        printw("[]");
                                }
                                else{
                                        printw("  ");
                                }
                        }
                                 printw("\n");

                }
                if(hang==19){
                        for(lie=0;lie<20;lie++){
                                printw("--");
                        }
                        printw("\n");
                }

        }

}

4.蛇的移动:

蛇的移动就是尾部增加一个节点并删除头一个节点;

void moveSnak()
{

        addNode();

        delatNode();
    
}
void delatNode()
{
        struct Snak* p=head;
        head=head->next;
        free(p);
}

 在移动的过程中会不断创建新的头节点,但是之前的头节点还占据着内存空间,这些节点已经没用了所以要把他所占据的内存空间用 free()给释放出来。 

此时我们需要用到方向键来让这条蛇运动,要运用方向键就需要用到: keypad(stdscr,1) 这个函数,所以在做初始化的时候就要用到这个函数。

void initNcurses()
{
        initscr();
        keypad(stdscr,1);
}

有了 keypad( ) 这个函数之后,我们就可以通过方向键来让蛇开始运动:

int main()
{
        initNcurses();
        creatSnak();
        snakMap();
        while(1){
                key=getch();
                if(key=KEY_RIGHT){
                        moveSnak();
                        snakMap();
                }
        }
        getch();
        endwin();
        return 0;
} 

当我们按下向右的时候,结果并不像我们想的那样向右移动,而是像下面所示一样:

 虽然蛇的确向右移动了,但显示这不是我们想要的效果。为了使得我们的贪吃蛇能在一张图里面移动,就需要把每一张图都覆盖在第一张图哪里,所以我们需要用 move(0,0)这个函数。当我们把move()这个函数加入到 snakMap()这个模块里面去之后就能达到我们想要的结果了。

void snakMap()
{
        int hang;
        int lie;
        move(0,0);

        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++){
                                if(lie==0 || lie==20){
                                        printw("|");
                                }else if(initSnak(hang,lie)){
                                        printw("[]");
                                }
                                else{
                                        printw("  ");
                                }
                        }
                                 printw("\n");

                }
                if(hang==19){
                        for(lie=0;lie<20;lie++){
                                printw("--");
                        }
                        printw("\n");
                }

        }

}

到了这一步蛇已经能向右移动了,但每次移动都需要用到按下向右键,为了让蛇能自己移动我们需要做如下的改动:

int main()
{
        initNcurses();
        creatSnak();
        snakMap();
        while(1){
                moveSnak();
                snakMap();
                refresh();
                usleep(100000); 
        }
        getch();
        endwin();
        return 0;
}

经过改动之后蛇能自己向右移动了,但蛇撞墙之后并没有重新开始,为了让蛇撞墙之后或者蛇吃到自己的时候重新开始,我们需要加入一些限制:

int snakDir()
{
        struct Snak* p=head;
        
        if(tail->hang<0 || tail->lie==0 || tail->hang==19 || tail->lie==20){
                return 1;
        }
        
        while(p->next !=NULL){
                if(tail->hang==p->hang && tail->lie==p->lie){
                        return 1;
                }
                p=p->next;
        }
        return 0;
}
void moveSnak()
{
        addNode();
        delatNode();

        if(snakDir()){
                creatSnak();
        }
}

现在蛇只能自动移动,不能控制方向,为了让蛇即能自己移动又能控制方向就需要用到线程来进行改动:

void* funk1()
{
        while(1){
                moveSnak();
                initMap();
                refresh();
                usleep(100000);
        }
}

void* funk2()
{
        while(1){
                key=getch();
                switch(key){
                        case KEY_UP:
                                dir=UP;
                                break;

                        case KEY_DOWN:
                                dir=DOWN;
                                break;

                        case KEY_LEFT:
                                dir=LEFT;
                                break;

                        case KEY_RIGHT:
                                dir=RIGHT;
                                break;
                }
        }
}

int main()
{
        pthread_t t1;
        pthread_t t2;

        initNcurses();
        creatSnak();
        initMap();

        pthread_create(&t1,NULL,funk1,NULL);
        pthread_create(&t2,NULL,funk2,NULL);
        pthread_join(t1,NULL);
        pthread_join(t2,NULL);

        getch();
        endwin();

        return 0;
}

之前在增加蛇的节点的时候并没有考虑方向,现在要考虑到方向,所以增加节点的这个模块也需要做一些调整:

void addNode()
{

         struct Snak* new=(struct Snak*)malloc(sizeof(struct Snak));
         switch(dir){
                case UP:
                        new->H=tail->H-1;
                        new->L=tail->L;
                        break;

                case DOWN:
                        new->H=tail->H+1;
                        new->L=tail->L;
                        break;

                case LEFT:
                        new->H=tail->H;
                        new->L=tail->L-1;
                        break;

                case RIGHT:
                        new->H=tail->H;
                        new->L=tail->L+1;
                        break;
            
         }
 
         new->next=NULL;
         tail->next=new;
         tail=new;
}

为了让蛇在开始的时候只能向右运动,我们需要让蛇刚开始时就满足 RIGHT这个条件即在creatSnak() 中加入dir= RIGHT,至此贪吃蛇就能上下左右的移动了。但在蛇前进的时候发现蛇可以直接向后移动,为了不让他可以直接向后运动,我们需要用到 abs()这个函数取绝对值进行比较,如果相等,则方向不能改变。

#define UP      1
#define DOWN   -1
#define LEFT    2
#define RIGHT  -2

void direction(int dis)
{
        if(abs(dis)!=abs(dir)){
                dir=dis;
        }
}

void* funk2()
{
        while(1){
                key=getch();
                switch(key){
                        case KEY_UP:
                                direction(UP);
                                break;

                        case KEY_DOWN:
                                direction(DOWN);
                                break;

                        case KEY_LEFT:
                                direction(LEFT);
                                break;

                        case KEY_RIGHT:
                                direction(RIGHT);
                                break;
                }
        }
}

到这里贪吃蛇的移动已经完成。 

5.贪吃蛇吃食物:

首先需要一个创建一个食物:struct Snak food;

做食物的初始化:

void initFood()
{
        int x=rand()%20;
        int y=rand()%20;

        food.H=x;
        food.L=y;

}

让食物在地图上显示:

int snakFood(int H,int L)
{
        if(food.H==H && food.L==L){
                return 1;
        }
        return 0;
}

void snakMap()
{
        int hang;
        int lie;
        move(0,0);

        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++){
                                if(lie==0 || lie==20){
                                        printw("|");
                                }else if(initSnak(hang,lie)){
                                        printw("[]");
                                }else if(snakFood(hang,lie)){
                                        printw("##");
                                }

                                else{
                                        printw("  ");
                                }
                        }
                                 printw("\n");

                }
                if(hang==19){
                        for(lie=0;lie<20;lie++){
                                printw("--");
                        }
                        printw("\n");
                }

        }

}

蛇吃食物就是 当蛇的尾节点与食物重合时,增加一个节点,并让食物初始化。

void initFood()
{
        int x=rand()%20;
        int y=rand()%20;

        food.H=x;
        food.L=y;

}

int snakFood(int H,int L)
{
        if(food.hang==H && food.lie==L){
                return 1;
        }
        return 0;
}

void moveSnak()
{
        addNode();
        if(snakFood(tail->hang,tail->lie)){
                initFood();
        }else{
                delatNode();
        }

        if(snakDir()){
                creatSnak();
        }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值