当前位置: 首页 > article >正文

C语言实现贪吃蛇小游戏

    ✅博客主页:爆打维c-CSDN博客​​​​​​ 🐾

🔹分享c语言知识及代码 🐾

目录

游戏展示视频

一、项目准备工作

二、功能实现分析

1.游戏开始

a.设置本地化、创建窗口、标题

b.隐藏光标,封装定位光标的函数

c.打印欢迎界面及提示信息

d.打印游戏地图及右侧的提示信息

e.创建蛇

f.创建食物

2.游戏运行

a.打印右侧帮助信息

b.按键判断

c.蛇移动过程中

3.游戏结束

a.打印游戏结束原因

b.销毁蛇结点

c.销毁食物结点

三、游戏代码实现如下

snack.h

snack.cpp

test.cpp


游戏展示视频

c语言 贪吃蛇游戏实现

一、项目准备工作

1. 打开VS新建一个空项目

2. 创建三个文件

  • snack.h       写游戏需要的数据类型和函数声明
  • snack.cpp   写函数的定义
  • test.cpp      写游戏的测试逻辑

准备工作就绪,现在我们来分析需要实现的功能


二、功能实现分析

1.游戏开始

a.设置本地化、创建窗口、标题

需要用到的函数:

int system (const char* command);   //头文件为<stdlib.h> 用于执行系统指令

char *setlocale(int category, const char *locale); 

//头文件为<locate.h> 用于设置或查询程序的本地化信息

//设置为本地模式 支持宽字符(汉字)的输出
setlocale(LC_ALL, ""); 

//设置标题
system("title 贪吃蛇");

//设置窗体长和宽
system("mode con cols=100 lines=30");

b.隐藏光标,封装定位光标的函数

 //获取句柄
 HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);

 CONSOLE_CURSOR_INFO cursor_info;   //包含有关控制台游标的信息的结构体
 GetConsoleCursorInfo(houtput, &cursor_info);  //获取控制台光标信息
 cursor_info.bVisible = false;  //设置光标可见度
 SetConsoleCursorInfo(houtput, &cursor_info);   //设置光标

//定位光标的函数
void Setpos(short x,short y) {
    //获取句柄
    HANDLE handle = NULL;
    handle = GetStdHandle(STD_OUTPUT_HANDLE);
    //设置光标位置
    COORD pos = { x,y };
    SetConsoleCursorPosition(handle, pos);
}

对于Win32 API控制台函数 的使用不清楚的同学可以去查阅官方文档,里面给出了详细的解释

控制台函数 - Windows Console | Microsoft Learnicon-default.png?t=O83Ahttps://learn.microsoft.com/zh-cn/windows/console/console-functions

c.打印欢迎界面及提示信息

实现效果如图

void WelcomeToGame() {
    Setpos(40, 15);
    printf("欢迎来到贪吃蛇游戏!\n");
    Setpos(40, 25);    //设置按任意键继续的位置
    system("pause");
    system("cls");
    Setpos(25, 13);
    printf("使用按键“↑↓←→”控制蛇的移动,按F3加速,F4减速\n");
    Setpos(40, 25);   //设置按任意键继续的位置
    system("pause");
    system("cls");
}

d.打印游戏地图及右侧的提示信息

void PrintMap() {
    int i = 0;
    //打印上横排
    for (i = 0; i < 58; i+=2) {  //0~56
        Setpos(i, 0);
        wprintf(L"%c",WALL);
    }
    //打印下横排
    for (i = 0; i < 58; i+=2) {  //0~56
        Setpos(i, 26);
        wprintf(L"%c", WALL);
    }
    //打印左竖排
    for (i = 1; i < 26; i++) {
        Setpos(0, i);
        wprintf(L"%c", WALL);
    }
    //打印右竖排
    for (i = 1; i < 26; i++) {
        Setpos(56, i);
        wprintf(L"%c", WALL);
    }
}

e.创建蛇

先在snack.h中声明维护蛇的结构体

//蛇的方向
enum DIRECTTION {
    UP = 1,
    DOWN,
    LEFT,
    RIGHT
};
//蛇的状态
enum GAME_STATUS {
    OK,            //存活
    KILL_BY_SELF,  //撞到自己
    KILL_BY_WALL, //撞墙死亡
    END_NORMAL  //正常退出
};
//蛇节点结构体 单链表
typedef struct SnackNode {
    int x;
    int y;
    struct SnackNode* next;
}SnakcNode, * pSnackNode;
//蛇结构体 维护蛇的信息
typedef struct Snack {
    int _sleep_time;    //休眠时间越短 蛇越快
    int _foodscore;  //一个食物的得分
    int _score;    //总得分
    enum DIRECTTION _dir;//蛇的方向
    enum GAME_STATUS _status;  //蛇的状态
    pSnackNode _pfood; //指向食物的指针
    pSnackNode _phead; //指向蛇头的指针
}Snack, * pSnack;

初始化蛇身

//初始化蛇身
void InitSnack(pSnack ps){
    //蛇身初始化五个节点
    int i = 0;
    pSnackNode cur = NULL;
    for (i = 0; i < 5; i++) {
        cur = (pSnackNode)malloc(sizeof(SnackNode));
        if (cur == NULL) {
            perror("malloc cur fail");
            return;
        }
        cur->next = NULL;
        cur->x = SNACK_X + i * 2;
        cur->y = SNACK_Y;

        if (ps->_phead == NULL) {  //空链表
            ps->_phead = cur;
        }
        else {
            //头插法
            cur->next = ps->_phead;
            ps->_phead = cur;
        }
    }
    cur = ps->_phead;
    while (cur) {
        Setpos(cur->x, cur->y);
        wprintf(L"%c",BODY);
        cur = cur->next;
    }
    //设置其他属性
    ps->_dir = RIGHT;
    ps->_foodscore = 20;
    ps->_score = 0;
    ps->_sleep_time = 200;
    ps->_status = OK;
}

f.创建食物

//创建食物
void CreateFood(pSnack ps) {
    int x=0, y=0;
again:
    do {
        x = rand() % 53 + 2; //2~54
        y = rand() % 25 + 1;//1~25
    } while (x % 2 != 0);//x必须要是2的倍数

    //x,y坐标不能与蛇身重合
    pSnackNode cur = ps->_phead;
    while (cur) {
        if (cur->x == x && cur->y == y) { 
            goto again;
        }
        cur = cur->next;
    }
    //创建食物节点
    pSnackNode pfood = (pSnackNode)malloc(sizeof(SnackNode));
    if (!pfood) {
        perror("malloc pfood fail");
        return;
    }
    else {
        pfood->x = x;
        pfood->y = y;
        ps->_pfood = pfood;
        Setpos(x, y);
        wprintf(L"%c",FOOD);
    }
}

2.游戏运行

a.打印右侧帮助信息

b.按键判断

判断用户是否按键 

  •   按下上下左右则改变蛇的方向 f3则加速 f4则减速 暂停键 退出键

c.蛇移动过程中

判断蛇下一步往哪走 是否吃掉食物?

  •          如果吃掉食物那么蛇身增长 加分
  •          如果没吃掉食物 那么往前移动

判断是否存活

  •         如果存活 继续游戏
  •         如果撞到自己或撞到墙 游戏结束

3.游戏结束

a.打印游戏结束原因

b.销毁蛇结点

c.销毁食物结点


三、游戏代码实现如下

snack.h

#pragma once
#include<stdio.h>
#include<Windows.h>
#include<locale.h>
#include<stdbool.h>
#define BODY L'●'
#define FOOD L'★'
#define WALL L'□'
#define SNACK_X 26
#define SNACK_Y 5
//判断?个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1.
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1)?1:0)

//蛇的方向
enum DIRECTTION {
    UP = 1,
    DOWN,
    LEFT,
    RIGHT
};
//蛇的状态
enum GAME_STATUS {
    OK,            //存活
    KILL_BY_SELF,  //撞到自己
    KILL_BY_WALL, //撞墙死亡
    END_NORMAL  //正常退出
};
//蛇节点结构体 单链表
typedef struct SnackNode {
    int x;
    int y;
    struct SnackNode* next;
}SnakcNode, * pSnackNode;
//蛇结构体 维护蛇的信息
typedef struct Snack {
    int _sleep_time;    //休眠时间越短 蛇越快
    int _foodscore;  //一个食物的得分
    int _score;    //总得分
    enum DIRECTTION _dir;//蛇的方向
    enum GAME_STATUS _status;  //蛇的状态
    pSnackNode _pfood; //指向食物的指针
    pSnackNode _phead; //指向蛇头的指针
}Snack, * pSnack;

void Setpos(short x, short y);
void WelcomeToGame();
void My_time();
void PrintMap();
void CreateFood(pSnack ps);
void GameStart(pSnack ps);
void GameRun(pSnack ps);
void GameOver(pSnack ps);

snack.cpp

//函数的声明
#include"snake.h"
void Setpos(short x,short y) {
    //获取句柄
    HANDLE handle = NULL;
    handle = GetStdHandle(STD_OUTPUT_HANDLE);
    //设置光标位置
    COORD pos = { x,y };
    SetConsoleCursorPosition(handle, pos);
}

void WelcomeToGame() {
    Setpos(40, 15);
    printf("欢迎来到贪吃蛇游戏!\n");
    Setpos(40, 25);    //设置按任意键继续的位置
    system("pause");
    system("cls");
    Setpos(25, 13);
    printf("使用按键“↑↓←→”控制蛇的移动,按F3加速,F4减速\n");
    Setpos(40, 25);   //设置按任意键继续的位置
    system("pause");
    system("cls");
}
void PrintMap() {
    int i = 0;
    //打印上横排
    for (i = 0; i < 58; i+=2) {  //0~56
        Setpos(i, 0);
        wprintf(L"%c",WALL);
    }
    //打印下横排
    for (i = 0; i < 58; i+=2) {  //0~56
        Setpos(i, 26);
        wprintf(L"%c", WALL);
    }
    //打印左竖排
    for (i = 1; i < 26; i++) {
        Setpos(0, i);
        wprintf(L"%c", WALL);
    }
    //打印右竖排
    for (i = 1; i < 26; i++) {
        Setpos(56, i);
        wprintf(L"%c", WALL);
    }
}
//初始化蛇身
void InitSnack(pSnack ps){
    //蛇身初始化五个节点
    int i = 0;
    pSnackNode cur = NULL;
    for (i = 0; i < 5; i++) {
        cur = (pSnackNode)malloc(sizeof(SnackNode));
        if (cur == NULL) {
            perror("malloc cur fail");
            return;
        }
        cur->next = NULL;
        cur->x = SNACK_X + i * 2;
        cur->y = SNACK_Y;

        if (ps->_phead == NULL) {  //空链表
            ps->_phead = cur;
        }
        else {
            //头插法
            cur->next = ps->_phead;
            ps->_phead = cur;
        }
    }
    cur = ps->_phead;
    while (cur) {
        Setpos(cur->x, cur->y);
        wprintf(L"%c",BODY);
        cur = cur->next;
    }
    //设置其他属性
    ps->_dir = RIGHT;
    ps->_foodscore = 20;
    ps->_score = 0;
    ps->_sleep_time = 200;
    ps->_status = OK;
}
//创建食物
void CreateFood(pSnack ps) {
    int x=0, y=0;
again:
    do {
        x = rand() % 53 + 2; //2~54
        y = rand() % 25 + 1;//1~25
    } while (x % 2 != 0);//x必须要是2的倍数

    //x,y坐标不能与蛇身重合
    pSnackNode cur = ps->_phead;
    while (cur) {
        if (cur->x == x && cur->y == y) { 
            goto again;
        }
        cur = cur->next;
    }
    //创建食物节点
    pSnackNode pfood = (pSnackNode)malloc(sizeof(SnackNode));
    if (!pfood) {
        perror("malloc pfood fail");
        return;
    }
    else {
        pfood->x = x;
        pfood->y = y;
        ps->_pfood = pfood;
        Setpos(x, y);
        wprintf(L"%c",FOOD);
    }
}
void GameStart(pSnack ps) {
    
    //设置窗体长和宽
    system("mode con cols=100 lines=30");

    //获取句柄
    HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cursor_info;  

    GetConsoleCursorInfo(houtput, &cursor_info);  //获取控制台光标信息
    cursor_info.bVisible = false;  //设置光标可见度
    SetConsoleCursorInfo(houtput, &cursor_info);   //设置光标
    //1.打印欢迎窗口 
    //2.功能展示
    WelcomeToGame();
    //3.打印地图
    PrintMap();
    //4.创建蛇
    InitSnack(ps);
    //5.创建食物
    CreateFood(ps);
}

void PrintHelpInfo() {
   
    Setpos(62, 14);
    printf("不能撞到墙,不能撞到自己");
    Setpos(62,15);
    printf("使用按键“↑↓←→”控制蛇的移动");
    Setpos(62,16);
    printf("按F3加速,F4减速");
    Setpos(62, 17);
    printf("按空格暂停游戏,按ESC退出游戏");

}
void Pause() {
    while (1) {
        Sleep(200);
        if (KEY_PRESS(VK_SPACE)) {
            break;
        }
    }
}
int NextisFood(pSnackNode psn,pSnack ps) {
    return (psn->x == ps->_pfood->x) && (psn->y == ps->_pfood->y);
}
void EatFood(pSnackNode psn, pSnack ps) {
    //头插法
    psn->next = ps->_phead;
    ps->_phead = psn;
    //加分
    ps->_score += ps->_foodscore;
    //打印蛇身
    pSnackNode cur = ps->_phead;
    while (cur) {
        Setpos(cur->x, cur->y);
        wprintf(L"%lc", BODY);
        cur = cur->next;
    }
    //释放食物节点
    free(ps->_pfood);
    //创建新的⻝物
    CreateFood(ps);
}
void NoFood(pSnackNode psn, pSnack ps){
    psn->next = ps->_phead;
    ps->_phead = psn;
    pSnackNode cur = ps->_phead;
    while (cur->next->next!=NULL) {
        Setpos(cur->x, cur->y);
        wprintf(L"%lc", BODY);
        cur = cur->next;
    }
    //最后一个节点空格覆盖
    Setpos(cur->next->x, cur->next->y);
    printf("  ");
    //释放最后一个节点 并把此时的蛇尾next指针置为空 
    free(cur->next);
    cur->next = NULL;
}
int KillbySelf(pSnack ps) {
    //判断蛇身与蛇头是否重合
    pSnackNode cur = ps->_phead->next;
    while (cur) {
        if (ps->_phead->x == cur->x && ps->_phead->y == cur->y) {   //撞到自己 
            ps->_status = KILL_BY_SELF;
            return 1;
        }
        cur = cur->next;
    }
    return 0;
}
int KillbyWall(pSnack ps) {
    //判断蛇头是否撞墙
    if (ps->_phead->x == 0 || ps->_phead->x == 56 || ps->_phead->y == 0 || ps->_phead->y == 26) {
        ps->_status = KILL_BY_WALL;
        return 1;
     }
    return 0;
}
void SnackMove(pSnack ps) {
    //创建一个新的节点 表示蛇下一步到的位置 
    pSnackNode pNextNode = (pSnackNode)malloc(sizeof( SnackNode));
    if (!pNextNode) {
        perror("SnakeMove()::malloc()");
        return;
    }
    //判断下一步的方向
	switch (ps->_dir) {

	case UP:
        pNextNode->x = ps->_phead->x;
        pNextNode->y = ps->_phead->y - 1;
		break;
	case DOWN:
        pNextNode->x = ps->_phead->x;
        pNextNode->y = ps->_phead->y + 1;
        break;
	case LEFT:
        pNextNode->x = ps->_phead->x - 2;
        pNextNode->y = ps->_phead->y;
        break;
	case RIGHT:
        pNextNode->x = ps->_phead->x + 2;
        pNextNode->y = ps->_phead->y;
        break;
	}
    if (NextisFood(pNextNode,ps)) {
        EatFood(pNextNode, ps);
    }
    else {
        NoFood(pNextNode, ps);
    }
    KillbySelf(ps);
    KillbyWall(ps);

}
void GameRun(pSnack ps) {
    PrintHelpInfo();  //打印右侧帮助信息
    //按键判断
    do {
        Setpos(62, 10);
        printf("总得分:%d",ps->_score);
        Setpos(62, 11);
        printf("一个食物的分数:%d", ps->_foodscore);
        if (KEY_PRESS(VK_UP) && ps->_dir != DOWN) {
            ps->_dir = UP;
        }
        else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP) {
            ps->_dir = DOWN;
        }
        else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT) {
            ps->_dir = RIGHT;
        }
        else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT) {
            ps->_dir = LEFT;
        }
        else if (KEY_PRESS(VK_ESCAPE)) {
            ps->_status = END_NORMAL; //正常退出
            break;
        }
        else if (KEY_PRESS(VK_SPACE)) {
            Pause();  //让页面暂停 再按一次空格恢复
        }
        else if (KEY_PRESS(VK_F3)) {
            //加速
            if (ps->_sleep_time < 320) {
                ps->_sleep_time -= 30;
                ps->_foodscore += 10;
            }
        }
        else if (KEY_PRESS(VK_F4)) {
            //减速
            if (ps->_sleep_time>=80) {
                ps->_sleep_time += 30;
                ps->_foodscore -= 10;
            }
        }
        SnackMove(ps);
        Sleep(ps->_sleep_time);

    } while (ps->_status==OK);
}
void GameOver(pSnack ps) {
    pSnackNode cur = ps->_phead;
    Setpos(30, 12);
    switch (ps->_status)
    {
    case END_NORMAL:
        printf("您主动退出游戏\n");
        break;
    case KILL_BY_SELF:
        printf("您撞上自己了 ,游戏结束!\n");
        break;
    case KILL_BY_WALL:
        printf("您撞墙了,游戏结束!\n");
        break;
    } 
    //释放蛇身的节点
    while (cur)
    {
        pSnackNode del = cur;
        cur = cur->next;
        free(del);
    }

}

test.cpp

void game() {
    char c ='Y';
    do {
        Snack snack = { 0 };
        GameStart(&snack);
        GameRun(&snack);
        GameOver(&snack);
        Setpos(30, 13);
        printf("是否再来一局:Y/N >");
        c=getchar();
        getchar();
    } while (c == 'Y'||c=='y');
} 
int main(){

    setlocale(LC_ALL, ""); //设置为本地模式 支持宽字符(汉字)的输出等。
    system("title 贪吃蛇"); //设置标题
    srand((unsigned int)time(0));
    game();
    system("pause");
    return 0;
}

如果这篇文章对你有帮助的话,请给博主一个免费的赞鼓励一下吧~ 💓

本文仅简单介绍了有关类和对象的一些基本概念和相关代码实现,以上个人拙见,若有错误之处,希望各位能提出宝贵的建议和更正,感谢您的观看!


http://www.kler.cn/a/307257.html

相关文章:

  • 三种单例实现
  • 【go从零单排】Mutexes互斥锁
  • Pandas | 数据分析时将特定列转换为数字类型 float64 或 int64的方法
  • Django 详细入门介绍
  • 考研季来啦!考研过程中有哪些事情需要避坑?
  • git修改当前分支名称并推送到远程仓库
  • 【技术科普】揭秘图像处理:从零开始的计算机视觉之旅!
  • 海量数据查找最大K个值:数据结构与算法的选择
  • 【Node.js】初识微服务
  • CANopen协议的理解
  • 不用禁用 iptables 来解决 UFW 和 Docker 的安全问题
  • 智汇创想pytest接口自动化测试框架
  • 通俗地类比计算机视觉中各种层或操作的作用
  • 自动曝光算法
  • IDEA 常用插件推荐,美观又实用!
  • Vue生命周期;Vue路由配置;vue网络请求;vue跨域处理
  • vue3+ts 使用amCharts展示地图,1.点击左侧国家,可以高亮并放大右侧地图对应的国家。 2.展示数据球。
  • python tkinter
  • 物联网智能项目
  • Android Tools | 如何使用Draw.io助力Android开发:从UI设计到流程优化
  • 腾讯云使用
  • 将jar包作为lib导入和maven依赖导入有什么区别?
  • seafaring靶场渗透测试
  • 【C语言】(指针系列2)指针运算+指针与数组的关系+二级指针+指针数组+《剑指offer面试题》
  • 重塑科普展厅魅力,以用户体验为核心的策略性规划新探索!
  • 『功能项目』切换职业面板【48】