贪吃蛇项目实践——C语言和数据结构知识应用

目录

1. 游戏背景

2. 游戏效果演示

3. Win32API 介绍

3.1 Win32API

3.2 控制台程序

(1)mode命令

(2)title命令

3.3 控制台屏幕上的坐标COORD

COORD类型的声明:

用COORD给坐标赋值:

3.4 GetStdHandle

3.5 GetConsoleCursorInfo

3.5.1 CONSOLE_CURSOR_INFO

3.6 SetConsoleCursorInfo

3.7 SetConsoleCursorPosition

3.8 GetAsyncKeyState

4.基本功能的实现

4.1贪吃蛇地图绘制

4.1.1 本地化

4.1.2 类 项

4.1.3 setlocale函数 

4.1.4宽字符的打印

4.1.5地图坐标

4.2 蛇身和食物

​编辑

4.2.1 蛇身

4.2.2 食物

4.3 数据结构设计

​编辑​编辑

4.4 游戏运行(GameRun)

4.4.1 KEY_PRESS   

4.4.2 PrintHelpInfo

4.4.3 蛇身移动

4.4.3.1 蛇的下一个移动位置为食物

4.4.3.2 蛇吃食物

4.4.3.3 蛇的下一个移动位置不是食物,蛇正常行走

4.4.3.4 蛇若是撞墙则游戏结束

4.4.3.5 蛇若是撞到自己则游戏结束

4.5游戏结束

5.完整代码演示

5.1    snake.h  

5.2     test.c

5.3    snake.c


1. 游戏背景

      贪吃蛇是久负盛名的游戏,它也和俄罗斯⽅块,扫雷等游戏位列经典游戏的⾏列。 在编程语⾔的应用中,我将以贪吃蛇为例,从设计到代码实现来提升自己的编程能⼒和逻辑能⼒。

2. 游戏效果演示

        提前说明:大家可以看到这种运行界面和我们平常看到的界面不一样,只有通过以下设置后,才能达到上图中的效果。这是VS的运行窗口:

记得要保存!!!!

3. Win32API 介绍

本次实现贪吃蛇会使⽤到的⼀些 Win32API 知识,接下来我们就学习⼀下

3.1 Win32API

Windows这个多作业系统除了协调应用程序的执行、分配内存、管理资源之外,它同时也是一个很大的服务中心,调⽤这个服务中心的各种服务(每一种服务就是一个函数),可以帮应用程序达到开启视窗、描绘图形、使用周边设备等目的,由于这些函数服务的对象是应用程序(Application),所以便称之为Application Programming Interface,简称 API 函数。WIN32 API也就是Microsoft Windows 32位平台的应用程序编程接口。

3.2 控制台程序

平常我们运行起来的黑框程序其实就是控制台程序

我们可以使用cmd命令(mode命令)来设置控制台窗口的长宽:设置控制台窗口的大小,30行,100

(1)mode命令

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

(2)title命令

system("title 贪吃蛇");           //用来设置cmd窗口名称

3.3 控制台屏幕上的坐标COORD

COORD是WindowsAPI中定义的⼀个结构体,表示一个字符在控制台屏幕幕缓冲区上的坐标坐标系(0,0)的原点位于缓冲区的顶部左侧单元格

COORD类型的声明:

typedef  struct _COORD {

        SHORT X;

        SHORT Y; 

} COORD, *PCOORD;

用COORD给坐标赋值:

COORD pos = { 10, 15 };

3.4 GetStdHandle

GetStdHandle是一个WindowsAPI函数。它用于从⼀个特定的标准设备(标准输入、标准输出或标准错误)中取得⼀个句柄(用来标识不同设备的数值),使用这个句柄可以操作设备。

3.5 GetConsoleCursorInfo

用于检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息

3.5.1 CONSOLE_CURSOR_INFO

这个结构体,包含有关控制台光标的信息

3.6 SetConsoleCursorInfo

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

3.7 SetConsoleCursorPosition

设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调用SetConsoleCursorPosition函数将光标位置设置到指定的位置

3.8 GetAsyncKeyState

获取按键情况,GetAsyncKeyState的函数原型如下:

参考:虚拟键码 (Winuser.h) - Win32 apps | Microsoft Learn

4.基本功能的实现

4.1贪吃蛇地图绘制

我们最终的贪吃蛇大概要是这个样子,那我们的地图如何布置呢?

        在游戏地图上,我们打印墙体使⽤宽字符:,打印蛇使⽤宽字符,打印⻝物使⽤宽字符 普通的字符是占1个字节的,这类宽字符是占⽤2个字节

         那具体宽字符是怎么打印的呢?那我就来一一介绍。

这里再简单的讲⼀下C语言的国际化特性相关的知识,过去C语言并不适合非英语国家(地区)使用。C语言最初假定字符都是单字节的。但是这些假定并不是在世界的任何地方都适用。

后来为了使C语言适应国际化,C语言的标准中不断加入了国际化的支持。比如:加入了宽字符的类型wchar_t和宽字符的输入和输出函数,加入了头文件,其中提供了允许程序员针对特定地区(通常是国家或者说某种特定语言的地理区域)调整程序行为的函数。

4.1.1 <locale.h>本地化

        提供的函数用于控制C标准库中对于不同的地区会产生不⼀样行为的部分。在标准中,依赖地区的部分有以下几项:

                                 •数字量的格式

                                 •货币量的格式

                                 •字符集

                                 •日期和时间的表形式、

4.1.2 类 项

•LC_COLLATE:影响字符串比较函数 strcoll()和 strxfrm()

•LC_CTYPE:影响字符处理函数的行为。

•LC_MONETARY:影响货币格式。

•LC_NUMERIC:影响 printf() 的数字格式。

•LC_TIME:影响时间格式 strftime() 和 wcsftime()

•LC_ALL:针对所有类项修改,将以上所有类别设置为给定的语言环境。

4.1.3 setlocale函数 

4.1.4宽字符的打印

4.1.5地图坐标

我们假设实现⼀个棋盘27行,58列的棋盘(行和列可以根据自己的情况修改),再围绕地图画出墙,如下:

#define WALL L'□'
#define BODY L'●'
#define FOOD L'★'

void CreateMap()
{
    //上
    int i = 0;
    for (i = 0; i < 29; i++)
    {
        wprintf(L"%lc", WALL);
    }

    //下
    SetPos(0, 26);
    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);
    }
}

4.2 蛇身和食物

4.2.1 蛇身

void InitSnake(pSnake ps)
{
    int i = 0;
    pSnakeNode cur = NULL;
    for (i = 0; i < 5; i++)
    {
        cur=(pSnakeNode)malloc(sizeof(SnakeNode));
        if (cur == NULL)
        {
            perror("InitSnake()::malloc()");
            return;
        }
        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->_score = 0;
    ps->_food_weight = 10;
    ps->_sleep_time = 200;//单位是毫秒
    ps->_status = OK;

}

4.2.2 食物

4.3 数据结构设计

4.4 游戏运行(GameRun)

4.4.1 KEY_PRESS   

检测按键状态,我们封装了⼀个宏

4.4.2 PrintHelpInfo

4.4.3 蛇身移动

4.4.3.1 蛇的下一个移动位置为食物

int NextIsFood(pSnakeNode pn, pSnake ps)
{
    if (ps->_pFood->x == pn->x && ps->_pFood->y == pn->y)
        return 1;
    else
        return 0;
}

4.4.3.2 蛇吃食物

void EatFood(pSnakeNode pn, pSnake ps)
{
     //头插法
    ps->_pFood->next = ps->_pSnake;
    ps->_pSnake = ps->_pFood;

    //释放下一个位置的节点
    free(pn);
    pn = 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);
}

4.4.3.3 蛇的下一个移动位置不是食物,蛇正常行走

void NoFood(pSnakeNode pn, pSnake ps)
{
    //头插法
    pn->next = ps->_pSnake;
    ps->_pSnake = pn;
    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);

    //把倒数第二个节点的地址置为NULL
    cur->next = NULL;
}

4.4.3.4 蛇若是撞墙则游戏结束

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;
    }
}

4.4.3.5 蛇若是撞到自己则游戏结束

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;
    }
}

4.5游戏结束

void GameEnd(pSnake ps)
{
    SetPos(24, 12);
    switch (ps->_status)
    {
    case END_NORMAL:
        wprintf(L"您主动结束游戏\n");
        break;
    case KILL_BY_WALL:
        wprintf(L"您撞到墙上,游戏结束\n");
        break;
    case KILL_BY_SELF:
        wprintf(L"您撞到了自己,游戏结束\n");
        break;
    }
    

    //释放蛇身的链表
    pSnakeNode cur = ps->_pSnake;
    while (cur)
    {
        pSnakeNode del = cur;
        cur = cur->next;
        free(del);
    }
}

5.完整代码演示

5.1    snake.h  

5.2     test.c

5.3    snake.c

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值