分享缩略图

分享到:
链接已复制
首页>新闻中心>

C语言:【项目实现】贪吃蛇

2025-06-24 11:51:02

来源:新华网

字体:

目录

1.介绍:

       (1).控制台程序(Console)(也就是控制控制台窗口的长和宽)

       (2).控制光标位置

       (3).GetStdHandle函数:

       (4).GetConsoleCursorInfo函数:

       (5).SetConsoleCursorInfo函数:

       (6).SetConsoleCursorPosition函数:

       (7).GetAsyncKeyState:

2.宽字符打印与本地化设置:

       (1).前情提要:

       (2).本地化介绍:本地化:

       (3).类项:

       (4).setlocale函数:设置本地化状态

       (5).宽字符的打印:

3.贪吃蛇游戏的实现:

       (1).test.c文件:

       (2).snake.h

      (3).snake.c


Hello everyone,今天我来教大家实现一下贪吃蛇游戏的相关操作:

首先呢,我们大家在写贪吃蛇代码前要先了解一些知识点(Win32 API技术),在这里,我就带着大家一起来看一下吧:

1.介绍:

        Windows这个多作业系统除了协调应用程序的执行等操作,他同时也是一个很大的服务中心,这个服务中心它包含着很多种类的服务,其实这些服务就是有一系列的函数组成的,换句话说,就是我们现在在这个电脑上所完成的一系列步骤,其实在都是由函数来实现的,Windows规定这些函数我们都可以其直接使用,比如说打开视窗,描绘图形,使用周边设备等功能,也就是Windows的每一个函数他都能完成一个特定的功能,因为这些函数服务的对象是应用程序(Applicancation),因此简称API函数,Win32 API也就是Microsoft Windows 32位平台的应用程序编程接口。那么,接下来,我就来给大家介绍几种实现我们的贪吃蛇游戏需要用到的函数吧。

       (1).控制台程序(Console)(也就是控制控制台窗口的长和宽)

       但我们想要去改变控制台的长和宽的时候,mode命令去实现这个功能,例如:mode con coles=100 lines=30,当我们在Windows系统控制台上输入这个命令时,然后按Enter键那么这个时候控制台就会变成长100,宽30。

       我们也可以通过title命令去改变控制台窗口的名字,例如:title 贪吃蛇,当我们在Windows系统控制台上输入这个命令时,然后按Enter键那么这个时候控制台的题目就会变成贪吃蛇(只仅限于是在程序没结束时控制台的题目一直都会使贪吃蛇)。

       注意:mode命令和title命令不可以直接在C语言中使用,但是我们可以使用system()函数来执行这一命令,代码如下所示:

system("mode con cols=100 lines=30");

system("title 贪吃蛇");

aef94fe4eb18482289e864f8e07301d5.png

调试控制台屏幕如图所示:调试控制台屏幕变成100行30列,而且题目变成贪吃蛇。

       (2).控制光标位置

       在了解这个知识点前,我们首先要知道控制台屏幕其实就是一个坐标轴,而其中的每一个位置就是那个白点一样是一个坐标,

17d86746472048df8c30d28657e34c57.png

d81cc1e39e274376810b47f85e236788.jpeg

        我们要想让光标定位(注意:现在只是定位,不是转变到这个位置)到某一个坐标位置,在Win32 API上面有一个结构体:COORD结构体:typedef struct _COORD{

                                                                         SHORT x;

                                                                         SHORT y;

                                                                   }COORD,*PCOORD;例如:COORD pos={10,30};//给这个结构体x赋值10,y赋值30,换句话说,pos定位到(10,30)这个坐标位置。

       (3).GetStdHandle函数:

       用于从一个特定的标准设备(标准输入(例如键盘),标准输出(屏幕),标准错误)中获得一个句柄(换句话说,就是你想要去操作一个设备,你就得去获得这个设备的一些数据,(比如说:你现在想要去使用锅,那么你就得抓住这口锅的手柄,这样你才能去操作这口锅)这里的句柄和锅的手柄是一个道理,当我们得到这个句柄之后,我们就可以去操作这个设备了)。

       HANDLE GetStdHandle (DWORD nStdHandle);//(HANDLE是句柄的类型,DWORD是GetStdHandle参数的类型,nStdHandle是参数的取值)

                                     {STD_INPUT_HANDLE    标准输入设备(输入缓冲区的控制台)

       nStdHandle的取值{STD_OUTPUT_HANDLE    标出输出设备(控制台屏幕缓冲区)

                                     {STD_ERROR_HANDLE    标准错误设备(控制台屏幕缓冲区)

例如:HANDLE output=GetStdHandle(STD_OUTPUT_HANDLE);//定义了一个句柄output里面有标准输出是设备的一些信息。

       (4).GetConsoleCursorInfo函数:

       检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息(获得光标信息)。

       BOOL WINAPT GetConsoleCursorInfo(HANDLE nStdHandle,PCONSOLE_CURSOR_INFO IpConsoleCursorInfo);//(nStdHandle句柄,PCONSOLE_CURSOR_INFO是指向CONSOLE_CURSOR_INFO结构体的指针,该结构接收有关主机游标(光标)的信息

                         typedef struct  _CONSOLE_CURSOR_INFO{

                                  DWORD dwSize;//光标的大小

                                  BOOL bVisible;//光标的显示度

                         };CONSOLE_CURSOR_INFO,*PCONSOLE_CURSOR_INFO;)。

例如:我想要获得控制台屏幕中光标的信息

                HANDLE   houtput=GetStdHandle(STD_OUTPUT_HANDLE);

                CONSOLE_CURSOR_IN   cursor_info=;

                GetConsoleCursorInfo(houtput,&cursor_info);//将houtput句柄中有关光标的大小和显示度这两个信息放到cursor_info数组里面。(注意:这个函数仅仅只是获得信息,并不是改变)

       (5).SetConsoleCursorInfo函数:

       设置指定控制台屏幕缓的光标大小和可见度。

       BOOL WINAPT SetConsoleCursorInfo(HANDLE nStdHandle,const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo);//(nStdHandle句柄,CONSOLE_CURSOR_INFO *lpConsoleCursorInfo是指向CONSOLE_CURSOR_INFO结构体的指针)

例如:     HANDLE   houtput=GetStdHandle(STD_OUTPUT_HANDLE);

                CONSOLE_CURSOR_IN   cursor_info=;

                GetConsoleCursorInfo(houtput,&cursor_info);

                cursor_info.bVisible=false;

                SetConsoleCursorInfo(houtput,&cursor_info);//将cursor_info中的光标信息在控制台屏幕中展现出来。

       (6).SetConsoleCursorPosition函数:

       设置光标位置。

       BOOL WINAPT SetConsoleCursorInfo(HANDLE nStdHandle,COORD dwCurPosition);//(nStdHandle句柄,dwCurPosition是COORD类型的结构体

                   typedef struct _COORD{

                            SHORT x;

                            SHORT y;

                   }COORD,*PCOORD;)

例如:        HANDLE   houtput=GetStdHandle(STD_OUTPUT_HANDLE);

                   COORD pos{10,20};

                   SetConsoleCursorPosition(houtput,pos);//将屏幕中光标的位置定位到pos中所指定的位置。

       (7).GetAsyncKeyState:

       获取键盘中各键的状态,它会返回一个16位的short类型的数据,如果返回值的最低位被置为1则说明,该键被按过,否则为0。

       SHORT GetAsyncKeyState(int vkey);//(每个键都有一个对应的虚拟键值,再此函数中就是指vkey)

例如:我想要判断一下键5是否被按过:

                    #define AK(vk)   ((GetAsyncKeyState(vk)&0x1)?1:0)//这里我们需要注意一下,GetAsyncKeyState函数它的返回值的16位的值是不确定的,但是我们只需要去判断它的最后是不是1或者是0即可。基于这种原因,我们让GetAsyncKeyState函数的返回值和0x1&,也就是如下列代码所示:比如说GetAsyncKeyState函数返回了值是

(函数返回的随机值)&   00101010   01010101       |          &             00101010   01010100

                                     00000000   00000001       |                         00000000   00000001

            最后的结果为   00000000   00000001       |                          00000000   00000000

2.宽字符打印与本地化设置:

       (1).前情提要:

acf459f79c094e38aad05e8ed213e250.jpeg

       (2).本地化介绍:本地化:

       此头文件中所提供的函数用于控制C语言库中对于不同的地区会产生不一样行为的部分。换句话说,就是这个C语言在最开始的时候它是由美国人创造的,因此它仅限与使用英语地区的人使用,但是当你本地化之后,你就可以在屏幕上打印出汉字(我们的汉字占两个坐标位置(也就是可以打印宽字符))。

       (3).类项:

       通过上述解释,相信大家对于本地化已经有了一些了解,但是怎么修改呢?

比如:LC_ALL------针对所有类项进行修改

           LC_TIME----只影响时间的格式(类项不只有这两个,这里时间有限,就不给大家一一去介绍了,大家感兴趣的话可以去自行了解)

       (4).setlocale函数:设置本地化状态

       char*setlocale(int category,const char*locale);//(category这个参数指的就是上述类项其中的一种,针对第二个参数,标准只给了两种取值"C"(正常模式(也就是英语模式))和""(本地模式))。

例如:          setlocale(LC_ALL,"");//将所有的类项均转换为本地模式。

       (5).宽字符的打印:

       当我们将其转化为本地模式后,那么此时的编译器按逻辑来说是可以打印出汉字的,但还是要做一些处理,所用到的函数是什么,占位符是什么。

       我们在打印宽字符的时候只能使用wprintf函数,若打印的是宽字符,则所需占位符为%Lc,若为宽字符串,则所需占位符为%Ls。

例如:            setlocale(LC_ALL,"");

                       wprintf(L"%Lc",L'比');//比,占两个坐标位置

                       wprintf(L"%Ls",L'进击的巨人');//进击的巨人

3.贪吃蛇游戏的实现:

         OK好的,同志们,经过了上述知识点的解释,现在以我们的知识储备量就可以开始我们的游戏了。

       (1).test.c文件:

#include "snake.h"//完成的是游戏的测试逻辑void test(){    int ch = 0;    do    {        system("cls");        Snake snake = { 0 };        stratgame(&snake);        rungame(&snake);        //finishgame(&snake);        setpos(20, 15);        wprintf(L"再来一局吗?(Y/N):");        ch = getchar();        while (getchar() != '\n');        //system("cls");    } while (ch == 'y' || ch == 'Y');    setpos(0, 27);}int main(){    //设置适配本地环境       setlocale(LC_ALL, "");    srand((unsigned int)time(NULL));    test();    return 0;}

       (2).snake.h

#pragma once#define _CRT_SECURE_NO_WARNINGS 1#include #include #include #include #include #include #define POS_X 24#define POS_Y 5#define WALL L'□'#define BODY L'●'#define FOOD L'★'enum DIRECTION{    UP = 1,    DOWN,    LEFT,    RIGHT};//蛇的状态enum GAME_STATUS{    OK, //正常    KILL_BY_WALL, //撞墙    KILL_BY_SELF, //撞到自己    END_NORMAL //正常退出};//蛇身的节点类型typedef struct SnakeNode{    //坐标    int x;    int y;    //指向下一个节点的指针    struct SnakeNode* next;}SnakeNode, * pSnakeNode;//贪吃蛇typedef struct Snake{    pSnakeNode _pSnake;    pSnakeNode _pFood;    enum DIRECTION _dir;    enum GAME_STATUS _status;    int _food_weight;    int _score;          int _sleep_time; }Snake, * pSnake;//定位光标位置void setpos(short x, short y);//游戏的初始化void stratgame(pSnake ps);//打印环境页面和功能介绍void welcomesnake();//创建地图void createmap();//创建蛇void createsnake(pSnake ps);//创建食物void createfood(pSnake ps);//运行游戏void rungame(pSnake ps);//打印帮助信息void printhelpinfo();//游戏暂停void pause();//贪吃蛇移动void movesnake(pSnake ps);//吃食物void eatfood(pSnakeNode newnode, pSnake ps);//不是食物void nofood(pSnakeNode newnode, pSnake ps);//碰到墙壁void killbywall(pSnake ps);//碰到自己void killbyself(pSnake ps);//结束游戏void finishgame(pSnake ps);

      (3).snake.c

#include "snake.h"//创建食物void createfood(pSnake ps){        int x = 0;        int y = 0;again:        do        {            x = rand() % 53 + 2;             y = rand() % 25 + 1;        } while (x % 2 != 0);        pSnakeNode cur = ps->_pSnake;        while (cur)        {            if (cur->x == x && cur->y == y)            {                goto again;            }            cur = cur->next;        }        pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));        if (food == NULL)        {            perror("createfood()::malloc()");            return 1;        }        food->x = x;        food->y = y;        food->next = NULL;        setpos(x, y);        wprintf(L"%lc", FOOD);        ps->_pFood = food;}//创建蛇void createsnake(pSnake ps){    pSnakeNode cur = NULL;    for (int i = 0; i < 5; i++)    {        cur = (pSnakeNode)malloc(sizeof(SnakeNode));        if (cur == NULL)        {            perror("createsnake()::malloc()");            return 1;        }        cur->next = NULL;        cur->x = POS_X + 2 * i;        cur->y = POS_Y;        if (ps->_pSnake == NULL)        {            ps->_pSnake = cur;        }        else        {            cur->next = ps->_pSnake;            ps->_pSnake = cur;        }    }    cur = ps->_pSnake;    while (cur)    {        setpos(cur->x, cur->y);        wprintf(L"%lc", BODY);        cur = cur->next;    }    //设置贪吃蛇的属性    ps->_dir = RIGHT;    ps->_food_weight = 10;    ps->_score = 0;    ps->_sleep_time = 200;    ps->_status = OK;}//创建地图void createmap(){    int i = 0;    for (i = 0; i < 29; i++)    {        wprintf(L"%lc", WALL);    }    for (i = 1; i <= 25; i++)    {        setpos(0, i);        wprintf(L"%lc", WALL);    }    for (i = 1; i <= 25; i++)    {        setpos(56, i);        wprintf(L"%lc", WALL);    }    setpos(0, 26);    for (i = 0; i < 29; i++)    {        wprintf(L"%lc", WALL);    }}//定位光标位置void setpos(short x, short y){    HANDLE houtput = NULL;    houtput = GetStdHandle(STD_OUTPUT_HANDLE);    COORD pos = { x,y };    SetConsoleCursorPosition(houtput, pos);}//打印环境页面和功能介绍void welcomesnake(){    setpos(40,14);    wprintf(L"%ls",L"欢迎来到贪吃蛇小游戏n");    setpos(42, 14);    system("pause");    system("cls");    setpos(25,14);    wprintf(L"%ls", L"用↑.↓.←.→ 来控制蛇的移动,按F3加速,F4减速n");    setpos(25,15);    wprintf(L"%ls", L"加速能得到更好的分数n");    setpos(42, 20);    system("pause");    system("cls");}//游戏开始 void stratgame(pSnake ps){    system("mode con cols=100 lines=30");    system("title 贪吃蛇");    HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);    CONSOLE_CURSOR_INFO cursorinfo;    GetConsoleCursorInfo(houtput, &cursorinfo);    cursorinfo.bVisible = false;    SetConsoleCursorInfo(houtput, &cursorinfo);    //打印环境页面和功能介绍    welcomesnake();    //创建地图    createmap();    //创建蛇    createsnake(ps);    //创建食物    createfood(ps);}//判断按键是否被按#define KEY_PRESS(vk)  ((GetAsyncKeyState(vk)&1)?1:0)//游戏暂停void pause(){    while (1)    {        Sleep(200);        if (KEY_PRESS(VK_SPACE))        {            break;        }    }}//碰到自己void killbyself(pSnake ps){    pSnakeNode cur = ps->_pSnake->next;    while (cur)    {        if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)        {            ps->_status = KILL_BY_SELF;            break;        }        cur = cur->next;    }}//碰到墙壁void killbywall(pSnake ps){    if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 || ps->_pSnake->y == 0 || ps->_pSnake->y == 26)    {        ps->_status = KILL_BY_WALL;    }}//打印帮助信息void printhelpinfo(){    setpos(64, 14);     wprintf(L"%ls", L"不能穿墙,不能咬到自己");    setpos(64, 15);    wprintf(L"%ls", L"用 ↑. ↓ . ← . → 来控制蛇的移动");    setpos(64, 16);     wprintf(L"%ls", L"按F3加速,F4减速");    setpos(64, 17);    wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏");    setpos(64, 18);    wprintf(L"%ls", L"郑源博制作");}//不是食物void nofood(pSnakeNode newnode, pSnake ps){    newnode->next = ps->_pSnake;    ps->_pSnake = newnode;    pSnakeNode cur = ps->_pSnake;    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("  ");    free(cur->next);    cur->next = NULL;}//吃食物void eatfood(pSnakeNode newnode, pSnake ps){    ps->_pFood->next = ps->_pSnake;    ps->_pSnake = ps->_pFood;    free(newnode);    newnode=NULL;    pSnakeNode cur = ps->_pSnake;    while (cur)    {        setpos(cur->x, cur->y);        wprintf(L"%lc", BODY);        cur = cur->next;    }    ps->_score += ps->_food_weight;    createfood(ps);}//贪吃蛇移动void movesnake(pSnake ps){    pSnakeNode newnode = (pSnakeNode)malloc(sizeof(SnakeNode));    if (newnode == NULL)    {        perror("movesnake()::malloc()");        return 1;    }    switch (ps->_dir)    {    case RIGHT:        newnode->x = ps->_pSnake->x + 2;        newnode->y = ps->_pSnake->y;        break;    case LEFT:        newnode->x = ps->_pSnake->x - 2;        newnode->y = ps->_pSnake->y;        break;    case DOWN:        newnode->x = ps->_pSnake->x;        newnode->y = ps->_pSnake->y + 1;        break;    case UP:        newnode->x = ps->_pSnake->x;        newnode->y = ps->_pSnake->y - 1;        break;    }    if (newnode->x == ps->_pFood->x && newnode->y == ps->_pFood->y)    {        eatfood(newnode,ps);    }    else    {        nofood(newnode, ps);    }    killbywall(ps);    killbyself(ps);}//运行游戏void rungame(pSnake ps){    printhelpinfo();    do    {        setpos(64, 10);        printf("总分数:%d\n", ps->_score);        setpos(64, 11);        printf("当前食物的分数:%2d\n", ps->_food_weight);        if (ps->_dir != RIGHT && KEY_PRESS(VK_LEFT))        {            ps->_dir = LEFT;        }        else if (ps->_dir != LEFT && KEY_PRESS(VK_RIGHT))        {            ps->_dir = RIGHT;        }        else if (ps->_dir != UP && KEY_PRESS(VK_DOWN))        {            ps->_dir = DOWN;        }        else if (ps->_dir != DOWN && KEY_PRESS(VK_UP))        {            ps->_dir = UP;        }        else if (KEY_PRESS(VK_SPACE))//判断是否按下空格键        {            pause();//游戏暂停        }        else if (KEY_PRESS(VK_ESCAPE))        {            ps->_status = END_NORMAL;        }        else if (KEY_PRESS(VK_F3))        {            if (ps->_sleep_time > 80)            {                ps->_sleep_time -= 30;                ps->_food_weight+=2;            }        }        else if (KEY_PRESS(VK_F4))        {            if (ps->_food_weight > 2)            {                ps->_sleep_time += 30;                ps->_food_weight -= 2;            }        }        movesnake(ps);        Sleep(ps->_sleep_time);    } while (ps->_status == OK);}//结束游戏void finishgame(pSnake ps){    setpos(24, 12);    switch (ps->_status)    {    case KILL_BY_WALL:        wprintf(L"%ls", L"您撞到墙上,结束游戏n");        break;    case KILL_BY_SELF:        wprintf(L"%ls", L"您撞到自己,结束游戏n");        break;    case END_NORMAL:        wprintf(L"%ls", L"您主动结束游戏n");        break;    }    pSnakeNode cur = ps->_pSnake;    while (cur)    {        pSnakeNode del = cur;        cur = cur->next;        free(del);    }}

【责任编辑:新华网】
返回顶部