【C语言篇】贪吃蛇小游戏

一、一些关于ncurse的知识 

(1)使用ncurses原因:按键响应速度快
(2)一些ncurse函数:
initscr();//ncurse界面初始化函数
printw();//在ncurse模式下的printf
getch();//等待用户输入,如果没有这句话程序就退出了,看不到运行结果
endwin();//程序退出,调用函数来恢复shell终端的显示,如果没有这句话,shell终端会字乱码,坏掉
keypad();//设置了可以从stdscr接收键盘的功能键(快捷键)
//使用keypad(stdtcr,1);
move();//改变光标的位置
//使用move(1,1);
refresh();//刷新
noecho();//不要把功能键的信息打印出来

二、制作贪吃蛇的思路 

1.地图规划

•大小20*20
•地图竖直方向上的边界"|"
•地图水平方向上的边界"--"
•贪吃蛇的身子"[]"
•食物"##"

2.蛇的身子的初始化(链表)
3.蛇向右移动(链表的添加,删除)
4.蛇碰到边界,重新开始(初始化蛇)
5.引入线程(方向改变和刷新界面)
6.蛇四方向的移动(要解决不合理走位)
7.蛇吃食物(食物的随机,链表的增加)
8.贪吃蛇的头碰到身子(注意链表的头结点) 

三、编程实现 

#include <curses.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

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

// 定义贪吃蛇的结构体
struct Snake
{
        int row;
        int column;
        struct Snake *next;
};

struct Snake *head = NULL; // 蛇头指针
struct Snake *tail = NULL; // 蛇尾指针
struct Snake food; // 食物的位置

int key; // 保存按键值
int dir; // 蛇的当前方向

// 初始化食物位置的函数
void initFood()
{
        srand((unsigned)time(NULL)); // 用时间作为随机数生成的种子
        int x = rand() % 15 + 3; // 随机生成x坐标
        int y = rand() % 15 + 3; // 随机生成y坐标

        food.row    = x;
        food.column = y;
}

// 检查某个位置是否有蛇身的函数
int allSnakeNode(int i, int j)
{
        struct Snake *p = head;

        while(p != NULL){
                if(p->row == i && p->column == j){
                        return 1; // 该位置有蛇身
                }
                p = p->next;
        }
        return 0; // 该位置没有蛇身
}

// 检查某个位置是否有食物的函数
int setFood(int i, int j)
{
        return (food.row == i && food.column == j) ? 1 : 0;
}

// 绘制游戏地图的函数
void gameMap()
{
        clear(); // 在渲染新帧之前清除屏幕
        int row, column;
        move(0,0); // 将光标移动到屏幕左上角

        for(row = 0; row < 20; row++){
                if(row == 0 || row == 19){
                        for(column = 0; column < 20; column++){
                                printw("--"); // 绘制上下边界
                        }
                        printw("\n");
                } else {
                        for(column = 0; column <= 20; column++){
                                if(column == 0 || column == 20){
                                        printw("|"); // 绘制左右边界
                                } else if(allSnakeNode(row, column)){
                                        printw("[]"); // 绘制蛇身
                                } else if(setFood(row, column)){
                                        printw("##"); // 绘制食物
                                } else {
                                        printw("  "); // 绘制空白区域
                                }
                        }
                        printw("\n");
                }
        }
        printw("Made by ZBB, key=%d, food.row=%d, food.column=%d\n", key, food.row, food.column); // 打印游戏信息
}

// 向蛇身添加节点的函数
void addNode()
{
        struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
        new->next = NULL;
        new->row = tail->row;
        new->column = tail->column;

        switch(dir){
                case UP:
                        new->row -= 1;
                        break;
                case DOWN:
                        new->row += 1;
                        break;
                case LEFT:
                        new->column -= 1;
                        break;
                case RIGHT:
                        new->column += 1;
                        break;
        }

        tail->next = new;
        tail = new;
}

// 初始化贪吃蛇的函数
void initSnake()
{
        while(head != NULL){
                struct Snake *p = head;
                head = head->next;
                free(p);
        }

        dir = RIGHT; // 设置蛇的初始方向为右
        initFood(); // 初始化食物位置

        head = (struct Snake *)malloc(sizeof(struct Snake)); // 创建蛇头节点
        head->row = 3;
        head->column = 4;
        head->next = NULL;

        tail = head; // 初始时蛇头即蛇尾

        // 初始状态下蛇身长度为5
        for(int i = 0; i < 4; i++){
                addNode();
        }
}

// 删除蛇尾节点的函数
void deleteNode()
{
        if(head != NULL){
                struct Snake *p = head;
                head = head->next;
                free(p);
        }
}

// 检查蛇是否死亡的函数
int snakeDie()
{
        if(tail->row < 1 || tail->row >= 19 || tail->column < 1 || tail->column >= 20)
        {
                return 1; // 蛇撞墙死亡
        }

        struct Snake *p = head;
        while(p != tail){
                if(p->row == tail->row && p->column == tail->column){
                        return 1; // 蛇撞到自己死亡
                }
                p = p->next;
        }
        return 0;
}

// 控制蛇移动的函数
void moveSnake()
{
        addNode(); // 向蛇身添加节点,即前进一格

        if(setFood(tail->row, tail->column)){
                initFood(); // 如果蛇吃到食物,重新生成食物
        } else {
                deleteNode(); // 如果蛇没有吃到食物,删除蛇尾节点
        }

        if(snakeDie()){
                initSnake(); // 如果蛇死亡,重新初始化蛇
        }
}

// 刷新窗口的线程函数
void* refreshWindow(void* arg)
{
        while(1)
        {
                moveSnake(); // 移动蛇
                gameMap(); // 绘制游戏地图
                refresh(); // 刷新屏幕
                usleep(105000); // 控制游戏速度
        }
}

// 改变蛇移动方向的函数
void turn(int direction)
{
        if(abs(dir) != abs(direction)){
                dir = direction;
        }
}

// 处理按键改变方向的线程函数
void* changeDir(void* arg)
{
        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()
{
        pthread_t t1, t2;

        initscr(); // 初始化ncurses
        keypad(stdscr, TRUE); // 启用键盘
        noecho(); // 关闭回显
        curs_set(0); // 隐藏光标

        initSnake(); // 初始化蛇
        gameMap(); // 绘制游戏地图

        pthread_create(&t1, NULL, refreshWindow, NULL); // 创建刷新窗口线程
        pthread_create(&t2, NULL, changeDir, NULL); // 创建处理按键线程
        
        pthread_join(t1, NULL); // 等待线程结束
        pthread_join(t2, NULL);

        getch(); // 等待用户输入以退出
        endwin(); // 结束ncurses模式

        return 0;
}

贪吃蛇游戏演示视频

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿gao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值