【C语言/数据结构】零基础打造控制台游戏:贪吃蛇实战教程----链表与Win32 API的完美结合!

【C语言/数据结构】零基础打造控制台游戏:贪吃蛇实战教程----链表与Win32 API的完美结合!p br strong 个人主页 strong EXtreme35 br p p strong 个人专栏 strong p 专栏名称 专栏主题简述 C 语言 C 语言基础 语法解析与实战应用 数据结构 线性表 树 图等核心数据结构详解 题解思维

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



 

📚 个人专栏:

专栏名称 专栏主题简述 《C语言》 C语言基础、语法解析与实战应用 《数据结构》 线性表、树、图等核心数据结构详解 《题解思维》 算法思路、解题技巧与高效编程实践

在这里插入图片描述

目录

  • 一、Win 32 API介绍
    • 1.1 控制台设置
    • 1.2程序中设置控制台
      • 1.2.1 COORD控制台屏幕坐标
      • 1.2.2 GetStdHandle 函数
      • 1.2.3 GetConsoleCursorInfo 函数
        • CONSOLE_CURSOR_INFO 结构
      • 1.2.4 SetConsoleCursorPosition 函数
        • 设置光标位置函数set_pos
      • 1.2.5 GetAsyncKeyState 函数
  • 二、游戏演示与界面设计
    • 2.1 成果演示
    • 2.2 游戏界面分解
      • 2.2.1 欢迎界面和操作指南
      • 2.2.2 主游戏界面布局
        • 宽字符的打印
        • 游戏边界的打印
        • 蛇和食物的打印
    • 2.3 数据结构设计
    • 2.4 操作说明
    • 2.5 游戏流程设计
  • 三、核心逻辑实现分析
    • 3.1 创建地图
    • 3.2 初始化蛇身
    • 3.3 创建食物
    • 3.4 游戏状态
    • 3.5 蛇的移动
      • 3.5.1 EatFood 函数详解
      • 3.5.2 NoFood 函数详解(未吃到食物)
      • 3.6 碰撞检测函数
    • 3.6 游戏结束
  • 四、总结

学习完C语言和链表,最好的方式就是通过项目实战来巩固知识。 贪吃蛇是一个经典的控制台游戏项目,它不仅能让你熟练掌握 链表这种重要的数据结构,还能让你体验 Win32 API在控制台程序中的强大应用。

这个多作业系统除了协调应⽤程序的执⾏、分配内存、管理资源之外, 它同时也是⼀个很⼤的服务中⼼,调⽤这个服务中⼼的各种服务(每⼀种服务就是⼀个函数),可以帮应⽤程序达到开启视窗、描绘图形、使⽤周边设备等⽬的,由于这些函数服务的对象是应⽤程序(), 所以便称之为 ,简称 函数。也就是的应⽤程序编程接⼝。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

函数用来执行系统命令。

GPT plus 代充 只需 145
在这里插入图片描述
 
  
在这里插入图片描述

1.2.1 COORD控制台屏幕坐标

是中定义的⼀个结构体,表⽰⼀个字符在控制台屏幕上的坐标。

GPT plus 代充 只需 145
在这里插入图片描述
在这里插入图片描述

屏幕上任何一个位置就是一个坐标。那怎么定义一个坐标呢?

 
  

1.2.2 GetStdHandle 函数

要控制控制台窗口,首先要获得这个窗口。

是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使⽤这个句柄可以操作设备(也就是屏幕)。

句柄 其实是一个数字,只是叫句柄而已,简单理解为抓手。

函数定义(飞机票)

GPT plus 代充 只需 145
在这里插入图片描述
在这里插入图片描述

1.2.3 GetConsoleCursorInfo 函数

在这里插入图片描述
 
  

它的第一个参数就是刚才获取到的句柄

第二个参数是指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关控制台游标的信息。

CONSOLE_CURSOR_INFO 结构
GPT plus 代充 只需 145
  • ,由光标填充的字符单元格的百分⽐。 此值介于1到100之间。 光标外观会变化,范围从完全填充单元格到单元底部的⽔平线条。
  • ,游标的可⻅性。 如果光标可⻅,则此成员为 。

示例代码:

 
  
在这里插入图片描述
这里需要注意的是,不能只修改,不然将会不成功,而是设置 —> 修改 —> 再设置。

不是我没截,是真的没了。













1.2.4 SetConsoleCursorPosition 函数

GPT plus 代充 只需 145

设置指定控制台屏幕缓冲区中的光标位置,将想要设置的坐标信息放在类型的中,⽤将光标位置设置到指定的位置。

指定新光标位置(以字符为单位)的 COORD 结构。 坐标是屏幕缓冲区字符单元的列和行。 坐标必须位于控制台屏幕缓冲区的边界以内。

在这里插入图片描述
 
  

光标被定位到(5,5)。

设置光标位置函数set_pos
GPT plus 代充 只需 145

1.2.5 GetAsyncKeyState 函数

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

 
  
在这里插入图片描述

的返回值是short类型,在上⼀次调⽤ 函数后,如果返回的16位的short数据中,最⾼位是1,说明按键的状态是按下,如果最⾼是0,说明按键的状态是抬起;如果最低位被置为1则说明,该按键被按过,否则为0。

如果我们要判断⼀个键是否被按过,可以检测返回值的最低值是否为1.

GPT plus 代充 只需 145
 
  
在这里插入图片描述

那这几个页面我们需要全部实现,接下来就对每个页面实现进行拆解。

我们的贪吃蛇游戏界面主要由以下几个部分构成:

  • 欢迎界面和操作指南:
    在游戏开始前, 函数会打印出欢迎信息和详细的按键指南(↑, ↓, ←, → 移动;W 加速;S 减速;ESC 退出)。



  • 主游戏界面布局:
    函数绘制了游戏的边框( 字符 )。蛇身( 字符 )和食物( 字符 )在边界内部移动。



  • 信息面板展示:
    在游戏区域的右侧,实时显示游戏的当前总分数当前食物分数 ,以及操作提示。



2.2.1 欢迎界面和操作指南

在这里插入图片描述

这里为了便于后续设置光标位置和游戏边界,那就统一将我们的操作界面设置为一个大小,那么后续也就不回出现乱码的情况了。

GPT plus 代充 只需 145

这里我们将大小确定好之后,光标也已经进行隐藏了,那只需要大概找到屏幕正中间位置就可以开始打印信息了。这里可以一点一点去试试,不难,一会就好了,至于这里为啥长和宽比例如此离谱,请耐心往下读!!!

这里有个小细节,那就是切换界面时,需要让用户自己操作,那就进行一个暂停操作。前一个界面会影响第二个界面的观感,那就进行一次清屏操作。

这里给出伪码,部分可能看不懂,别着急,往下接着看:

 
  

2.2.2 主游戏界面布局

在这里插入图片描述

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

这⾥再简单的讲⼀下C语⾔的国际化特性相关的知识,过去C语⾔并不适合⾮英语国家(地区)使⽤。C语⾔最初假定字符都是但⾃⼰的。但是这些假定并不是在世界的任何地⽅都适⽤。C语⾔字符默认是采⽤ASCII编码的,ASCII字符集采⽤的是单字节编码,且只使⽤了单字节中的低7位,最⾼位是没有使⽤的,可表⽰为0xxxxxxxx;可以看到,ASCII字符集共包含128个字符,在英语国家中,128个字符是基本够⽤的,但是,在其他国家语⾔中,⽐如,在法语中,字⺟上⽅有注⾳符号,它就⽆法⽤ ASCII 码表⽰。于是,⼀些欧洲国家就决定,利⽤字节中闲置的最⾼位编⼊新的符号。⽐如,法语中的é的编码为130(⼆进制)。这样⼀来,这些欧洲国家使⽤的编码体系,可以表⽰最多256个符号。但是,这⾥⼜出现了新的问题。不同的国家有不同的字⺟,因此,哪怕它们都使⽤256个符号的编码⽅式,代表的字⺟却不⼀样。⽐如,130在法语编码中代表了é,在希伯来语编码中却代表了字⺟Gimel ,在俄语编码中⼜会代表另⼀个符号。但是不管怎样,所有这些编码⽅式中,0–127表⽰的符号是⼀样的,不⼀样的只是128–255的这⼀段。⾄于亚洲国家的⽂字,使⽤的符号就更多了,汉字就多达10万左右。⼀个字节只能表⽰256种符号,肯定是不够的,就必须使⽤多个字节表达⼀个符号。⽐如,简体中⽂常⻅的编码⽅式是 GB2312,使⽤两个字节表⽰⼀个汉字,所以理论上最多可以表⽰ 256 x 256 = 65536 个符号。
后来为了使C语⾔适应国际化,C语⾔的标准中不断加⼊了国际化的⽀持。⽐如:加⼊和宽字符的类型wchar_t 和宽字符的输⼊和输出函数,加⼊和 头⽂件,其中提供了允许程序员针对特定地区(通常是国家或者说某种特定语⾔的地理区域)调整程序⾏为的函数。

1、 本地化
提供的函数⽤于控制C标准库中对于不同的地区会产⽣不⼀样⾏为的部分。在标准可以中,依赖地区的部分有以下⼏项:





  • 数字量的格式
  • 货币量的格式
  • 字符集
  • ⽇期和时间的表⽰形式

2、类项
通过修改地区,程序可以改变它的⾏为来适应世界的不同区域。但地区的改变可能会影响库的许多部分,其中⼀部分可能是我们不希望修改的。所以C语⾔⽀持针对不同的类项进⾏修改,下⾯的⼀个宏,指定⼀个类项:



  • :影响字符串比较函数和。
  • :影响字符处理函数的行为。
  • :影响货币格式。
  • :影响的数字格式。
  • :影响时间格式和。
  • - 针对所有类项修改,将以上所有类别设置为给定的语言环境。

3、setlocale函数

GPT plus 代充 只需 145
 
  

当地区设置为”C”时,库函数按正常⽅式执⾏,⼩数点是⼀个点。

当程序运⾏起来后想改变地区,就只能显⽰调⽤setlocale函数。⽤作为第2个参数,调⽤setlocale函数就可以切换到本地模式,这种模式下程序会适应本地环境。⽐如:切换到我们的本地模式后就⽀持宽字符(汉字)的输出等。

GPT plus 代充 只需 145
在这里插入图片描述
  • 字符集:基于 ASCII(或非常简单的字符集)。
  • 格式化规则
    • 数字:小数点使用点号 。
    • 时间/日期格式:遵循 C/POSIX 标准格式(例如 )。
    • 货币符号:通常只有基本的美元符号 ,没有本地化格式。
    • 字符串比较(排序):严格基于 ASCII 码值(例如 排在 前面)。

当调用 空字符串 :是程序主动“入乡随俗”,去适应操作系统用户所在的语言和文化环境。也就是。

宽字符的打印
  • 前缀在单引号前面,表示宽字符,对应的占位符为;
  • 前缀在双引号前面,表示宽字符串,对应的占位符为;
  • 格式串前面也要加上前缀。
在这里插入图片描述
游戏边界的打印

那实际上在我们的终端上也就是这个页面(假设我标出边框的是终端界面):

在这里插入图片描述
在这里插入图片描述
蛇和食物的打印
在这里插入图片描述

贪吃蛇最核心的就是对蛇身的表示,我们选择使用单向链表,因为蛇的移动是典型的头部插入、尾部删除操作。

枚举类型定义:
我们使用枚举 () 来清晰地定义蛇的方向 () 和游戏的状态 ()。



 
  

结构体详解:
结构体是整个游戏的上下文,它包含了游戏的所有状态信息。



GPT plus 代充 只需 145

链表节点的设计:
每个节点代表蛇身的一个部分,存储其在屏幕上的坐标以及指向下一个节点的指针。



 
  
  • 蛇的移动过程:
    蛇每走一步,就相当于在头部方向多了一个新节点,并在尾部少了一个旧节点,从而实现“移动”的视觉效果。



  • 食物生成和得分机制:
    食物在非墙壁、非蛇身的位置随机生成。当蛇头移动到食物位置时,它会吃掉食物,然后长度会变长,总分数增加,并在地图上立即重新生成一个新的食物。



  • 加速减速效果:
    通过按 WS 键,可以动态调整游戏的速度,这通过改变 参数来实现。 越短,蛇移动越快,同时食物分数 () 也会相应变化,实现了加速得分更高的Tips。



在这里插入图片描述

这里就是一些核心函数的实现了,就不一一展开了,只展示一些核心的内容,完了给出我的源码链接(文末),有兴趣的可以自己去拿一下。

GPT plus 代充 只需 145

创建地图函数

 
  

这里就需要根据个人定义的边界条件具体判断了,我定义的是30行。

蛇最开始长度为4节,每节对应链表的⼀个节点,蛇⾝的每⼀个节点都有⾃⼰的坐标。创建4个节点,然后将每个节点存放在链表中进⾏管理。创建完蛇⾝后,将蛇的每⼀节打印在屏幕上。(这里我定义的是四节,你可以自己改)

再设置当前游戏的状态,蛇移动的速度,默认的⽅向,初始成绩,蛇的状态,每个⻝物的分数。

蛇⾝打印的宽字符:

初始化蛇⾝函数:

GPT plus 代充 只需 145

我们使用头插法创建初始蛇身(例如4个节点)。初始时,蛇头位于链表的第一个节点。

先随机⽣成⻝物的坐标

  • x坐标必须是2的倍数
  • ⻝物的坐标不能和蛇⾝每个节点的坐标重复

这里说详细一些,为什么必须是2的倍数呢?
因为每一个蛇头都占据1个宽字符,两个字节,后续需要用食物坐标和蛇头坐标进行判断,判断是否吃到食物,那如果不是2的倍数,蛇头永远到不了这个食物的坐标,这游戏也就没法继续下去了。
为什么不能重复呢? 重复了你连食物都找不到,那还玩啥?










 
  

游戏运行期间,右侧打印帮助信息提示玩家,根据游戏状态检查游戏是否继续,如果是状态是,游戏继续否则游戏结束。如果游戏继续,就是检测按键情况,确定蛇下⼀步的⽅向,或者是否加速减速,是否暂停或者退出游戏。确定了蛇的⽅向和速度,蛇就可以移动了。

这里最重要的就是把交互做好,也就是键盘响应,很多代码都是一样的,可以定义宏进行简化代码,完整代码就只给个伪码了。

GPT plus 代充 只需 145

这里加速减速,也实现的没有那么复杂,就是将沉睡时间加长或者减少,但也不能无限制,在范围内就可以。

蛇的移动算法 (): 这是游戏的核心逻辑。

  1. 根据当前方向 (),计算出下一个位置的坐标,并创建一个新节点
  2. 判断下一个位置是否为食物 ()。
    • 如果是食物 (): 将 节点直接连接到蛇头(头插),让它成为新的蛇头。不进行尾部删除,从而实现蛇身变长。然后重新创建食物。
    • 如果不是食物 (): 将 连接到蛇头(头插)。找到链表的倒数第二个节点,释放它的 节点(即删除旧尾巴),并将其 置为 。
 
  

3.5.1 函数详解

函数处理蛇吃到食物的逻辑,其核心操作是让蛇身增长并更新游戏状态。

首先,它通过头插法将食物节点 () 插入到蛇头 () 的前面,使食物节点成为新的蛇头,从而实现蛇身的增长,因为这次移动没有移除尾部节点。接着,它释放了原本为下一个位置预分配的临时节点 ,避免内存泄漏。

随后,函数从新的蛇头开始遍历整个蛇身链表,并在控制台上重新打印蛇身的每个节点,确保蛇身的新状态在屏幕上正确显示 ()。

最后,它根据当前食物的分值 () 更新总得分 (),并调用 在地图上生成一个新的食物.

GPT plus 代充 只需 145

3.5.2 函数详解(未吃到食物)

当蛇移动到的下一个位置不是食物时, 函数执行蛇身平移逻辑:

  1. 头插法将代表新位置的节点 连接到蛇头 () 的前面,使其成为新的蛇头;
  2. 遍历链表,找到倒数第二个节点 (),并在遍历过程中重新打印除尾部外的所有蛇身节点;
  3. 函数定位到最后一个节点(旧尾巴),先在屏幕上将其位置打印成两个空格 () 来清除旧尾巴的痕迹,然后在内存中释放该节点 ();
  4. 将倒数第二个节点的 指针设置为 (),正式截断链表,使蛇的长度保持不变,完成一步移动。
 
  

这里打印空格的目的是为了在控制台屏幕上清除蛇身旧尾巴的痕迹实现蛇的平滑移动效果。

3.6 碰撞检测函数

、 这两个函数共同实现了贪吃蛇游戏的碰撞检测机制,用于判断游戏是否结束。

函数检查蛇头 () 的坐标是否触及地图边界(X坐标为 0 0 0 或 56 56 56,Y坐标为 0 0 0 或 28 28 28)(这里就是判断边界,如果你的边界不是我的这个大小,自己修改一下就好了),如果触墙,则立即将游戏状态 设置为 。

函数则通过遍历蛇头后面的所有蛇身节点 (),检查是否有任何一个节点 () 的坐标与当前蛇头坐标完全重合;如果发生重合(即蛇头咬到自己),则将游戏状态 设置为 。

一旦任一函数检测到碰撞,就会设置相应的状态,导致 函数中的循环终止,游戏结束。

GPT plus 代充 只需 145
 
  

每次蛇移动都要检测是否结束哦!

游戏状态不再是OK(游戏继续)的时候,要告知游戏结束的原因,然后直接退出?

不不不!!!

你的蛇是链表啊!链表节点是动态申请的啊,它在上,直接退出只把栈区的内存空间归还给操作系统,你还得干啥呢?对喽,把节点释放了,把内存还给人家,有借有还,再借不难啊!

GPT plus 代充 只需 145
在这里插入图片描述

通过实现C语言贪吃蛇小游戏,不仅将理论知识转化为实际代码,更深入理解了几个关键点:

  1. 链表是处理动态、顺序数据的理想结构,其头部插入和尾部删除的特性完美契合了蛇的移动需求。
  2. Win32 API是控制台程序交互的关键,它让我们能够突破标准C库的限制,实现对光标、窗口和键盘的精细控制。
  3. 正确的内存管理至关重要,特别是对链表的动态操作。
    • 链表的动态内存分配: 无论是初始化蛇身 ()、创建食物 ()、还是蛇移动时创建新的头节点 (),都需要使用 进行动态内存分配,确保每次操作都有足够的空间。
    • 节点添加和删除: 在 中,我们通过 释放了尾部节点,防止内存泄漏。
    • 游戏结束时的资源释放 (): 无论游戏是正常退出还是失败,都必须调用 函数。该函数会遍历整个蛇身链表,逐个释放所有动态分配的节点,彻底清理占用的内存。

如果你对这个感兴趣,可以尝试添加更多功能,例如墙体穿透、不同颜**分蛇头与蛇身,或实现多种不同的食物类型!

在这里插入图片描述

小讯
上一篇 2026-03-21 14:54
下一篇 2026-03-21 14:52

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/239992.html