Linux系统下实现贪吃蛇小游戏

Linux系统下实现贪吃蛇小游戏

一、准备工作

首先我们需要安装Ncurses库,读者可自行百度如何安装

#include<curses.h>  //ncurses必备的头文件
#include<stdlib.h>   //malloc函数必备的头文件
#include<pthread.h>  //多线程必备的头文件
//定义一个结构体作为蛇身
struct Snake
{
        int row;
        int line;
        struct Snake *next;
};

#define UP     1
#define DOWN  -1
#define LEFT   2
#define RIGHT -2
//定义键盘输入
int key;
//定义移动方向
int dir;
int score = 0;
//定义蛇头
struct Snake *head = NULL;
//定义蛇尾
struct Snake *tail = NULL;
//定义食物
struct Snake food;
//初始化Ncurses
void initNcurse()
{
        initscr();
        keypad(stdscr,1);
}

二、显示基本图形界面

//对蛇身初始化
void initSnake()
{
        struct Snake *p = NULL;
        //释放死掉的贪吃蛇所占空间
        while(head != NULL){
                p = head;
                head = head->next;
                free(p);
        }
		//定义初始移动方向
        dir = RIGHT;
		//为蛇头开辟空间
        head = (struct Snake *)malloc(sizeof(struct Snake));
		//定义蛇头初始位置
        head->row = 1;
        head->line = 1;

        head->next = NULL;
		//将蛇尾初始化指向蛇头
        tail = head;
		//为蛇身增加节点
        addNode();
        addNode();
}
//初始化食物
void initFood()
{
        int row = rand()%20;
        int line = rand()%20;

        food.row = row;
        food.line = line;
}
//显示蛇身
int snakeBody(int row,int line)
{
        struct Snake *p = head;
        while(p != NULL){
                if(p->row == row && p->line == line){
                        return 1;
                }
                p = p->next;
        }
        return 0;
}
//显示食物
int snakeFood(int row,int line)
{
        if(food.row == row && food.line == line){
                return 1;
        }
        return 0;
}

//定义20X20大小的地图
void gamePic()
{
        int row;
        int line;
		//移动光标
        move(0,0);
        for(row=0;row<=20;row++){
        		//定义上下边界
                if(row == 0 || row == 20){
                        for(line=0;line<20;line++){
                                printw("--");
                        }
                        printw("\n");
                }
				
                if(row > 0 && row < 20){
                		//定义左右边界
                        for(line=0;line<=20;line++){
                                if(line == 0 || line == 20){
                                        printw("|");
                                 //定义蛇身
                                }else if(snakeBody(row,line)){
                                        printw("[]");
                                //定义食物
                                }else if(snakeFood(row,line)){
                                        printw("##");
                                }else{
                                        printw("  ");
                                }
                        }
                        printw("\n");
                }
        }
        printw("by Teng\n");
        //打印分数
        printw("score %d\n",score);
}

三、蛇身移动

算法说明:贪吃蛇是根据蛇尾来移动的,移动方向不同蛇尾的坐标变化也不一样,当蛇尾不吃食物时,需要删除头节点,在尾巴处增加新节点,这样我们视觉效果就会看到蛇在移动,当吃到食物时,无需删除头节点。

//蛇身增加节点
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;
        }
        //将新节点加入蛇身
        tail->next = new;
        tail = new;
}
//删除蛇头
void deleteNode()
{
        struct Snake *p = head;
        head = head->next;
        free(p);
}
//判断蛇的死亡
int snakeDie()
{
        struct Snake *p = head;
        //当贪吃蛇的尾巴碰到了蛇身的其他部位,贪吃蛇重生
        while(p->next != NULL){
                if(p->row == tail->row && p->line == tail->line){
                        return 1;
                }
                p = p->next;
        }
		//当贪吃蛇撞墙后死亡重生
        if(tail->row == 0 || tail->row == 20 || tail->line == 0 || tail->line == 20){
                return 1;
        }

        return 0;
}

void moveSnake()
{
		//当蛇尾碰到食物,判定吃到食物,增加节点,分数加10,并重置新的食物
        if(snakeFood(tail->row,tail->line)){
                addNode();
                initFood();
                score += 10;
                //当食物坐标存在0点时,无法在界面显示,需重置食物位置
                if(food.row == 0 || food.line == 0){
                        initFood();
                }
        }else{
        		//贪吃蛇未能吃到食物,仅在移动
                addNode();
                deleteNode();
        }
		//当贪吃蛇死亡时,全部重置
        if(snakeDie()){
                initSnake();
                initFood();
                score = 0;
        }
}

四、控制移动方向并刷新界面

贪吃蛇不合理走位算法说明:若无绝对值来控制实际方向,贪吃蛇将出现能够反向走位的情况,算是一个小BUG,程序一开始我使用宏定义确定方向的整数值,只需通过对比键盘输入的方向及贪吃蛇走位方向的绝对值,即可判断是否是合理走位。

//刷新界面
void* refreshInterFace()
{
        while(1){
                moveSnake();
                gamePic();
                refresh();
                usleep(100000);
        }
}
//防止贪吃蛇不合理走位
void turn(int direction)
{
        if(abs(dir) != abs(direction)){
                dir = direction;
        }
}
//控制移动方向
void* controlDir()
{
        while(1){
                key = getch();

                switch(key){
                        case KEY_UP:
                                turn(UP);
                                break;
                        case KEY_DOWN:
                                turn(DOWN);
                                break;
                        case KEY_LEFT:
                                turn(LEFT);
                                break;
                        case KEY_RIGHT:
                                turn(RIGHT);
                                break;
                }
        }
}

五、多线程控制

由于我们需要一边控制贪吃蛇走位一边刷新ncurses界面,所以我们需要用到多线程

int main()
{
		//定义两个线程
        pthread_t t1;
        pthread_t t2;

        initNcurse();

        initSnake();

        initFood();

        gamePic();
		//线程1刷新界面
        pthread_create(&t1,NULL,refreshInterFace,NULL);
        //线程2控制方向
        pthread_create(&t2,NULL,controlDir,NULL);
        while(1);

        getch();
        endwin();

        return 0;
}

现在完成了基本的贪吃蛇小游戏,由于笔者能力有限,如有错误之处还请不吝赐教。

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值