1. 项目总览:为什么用C语言和链表做贪吃蛇?
大家好,我是老张,一个在C语言和嵌入式领域摸爬滚打了十多年的老程序员。今天想和大家聊聊一个特别经典,也特别能锻炼编程基本功的项目——用C语言在Windows控制台里实现贪吃蛇游戏,而且核心数据结构用的是链表。
你可能觉得,现在游戏引擎那么多,图形界面那么炫,为啥还要折腾这个黑乎乎的“控制台”游戏?我告诉你,这个项目麻雀虽小,五脏俱全。它逼着你去理解几个非常核心的东西:数据结构在真实场景下的应用、操作系统底层的API调用,以及游戏循环和状态管理的基本思想。链表用来管理动态增长的蛇身,简直是天作之合;而Windows控制台的那些API,能让你抛开图形库的束缚,直接和“终端”对话,这种掌控感是高级语言框架很难给你的。
我当年带新人,第一个实战项目往往就是这个。能独立把它啃下来,你对指针、内存管理、结构体的理解会上一个大台阶。网上能找到的代码很多,但不少只是把代码堆砌出来,缺少“为什么这么做”的深度解析。今天,我就带你从零开始,不光把代码写出来,更要把每一步背后的设计思路和踩过的坑讲清楚。我们目标是:让你看完就能自己动手,做出一个可玩、可扩展的贪吃蛇。
2. 环境搭建与核心工具准备
工欲善其事,必先利其器。我们是在Windows环境下开发,用的就是最经典的Visual Studio(我用的是VS 2022社区版,免费的,大家可以去官网下)。当然,你用其他IDE比如Code::Blocks、Dev-C++,甚至直接用记事本和MinGW GCC命令行编译,也完全没问题,原理都是相通的。
2.1 解决第一个“坑”:控制台终端设置
如果你直接用VS运行C程序,弹出的那个黑框,有时候显示会有点问题,比如宽字符显示不全,或者光标闪得烦人。这里有个小技巧需要设置一下。
在VS里,右键点击你的项目,选择“属性”。找到“链接器” -> “系统”,把“子系统”这一项从“控制台 (/SUBSYSTEM:CONSOLE)”改成“Windows (/SUBSYSTEM:WINDOWS)”。别担心,改了之后程序还是控制台程序,只是启动方式有点变化,对宽字符显示更友好。或者,你也可以在“调试” -> “命令”里,把终端改为“控制台主机”。这个小改动能避免很多后续显示上的诡异问题。
2.2 认识我们的“画笔”和“画布”:Windows控制台API
我们的游戏画面,全是在那个黑色的控制台窗口里,用字符“画”出来的。这就需要用到一组Windows提供的函数,我们称之为Win32 API。别被“API”吓到,你就把它想象成一套工具箱,专门用来操作这个黑框窗口。
首先,我们得能控制这个窗口的大小和标题吧?这里用到一个非常直接的函数:system()。它可以直接执行系统命令。
#include <stdlib.h> // 别忘了头文件
// 设置控制台窗口为100列,30行
system("mode con cols=100 lines=30");
// 把窗口标题改成“贪吃蛇”
system("title 贪吃蛇");
这两行代码通常放在游戏初始化最开始。cols是列数,对应x轴;lines是行数,对应y轴。这里有个关键点:我们后面打印的墙体、蛇身用的是宽字符(比如□、●),一个宽字符占2个普通字符的宽度。所以,如果你计划地图宽度是58个宽字符,那么cols最好设置为58*2=116左右,留点边距。我这里设100列,地图内宽是56(即28个墙块),两边留点空间打印分数信息。
2.3 隐藏烦人的闪烁光标
控制台里有个一直闪烁的光标,很影响游戏画面的整洁。我们必须把它藏起来。这个过程稍微绕一点,需要三个API配合。
-
GetStdHandle: 获取控制台输出设备的“句柄”。句柄你可以理解为一个门票或者遥控器,有了它,你才能对控制台进行后续操作。
#include <windows.h> HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); -
GetConsoleCursorInfo: 获取当前光标的信息,比如它的大小和是否可见。我们需要把这些信息存到一个结构体里。
CONSOLE_CURSOR_INFO cursorInfo; GetConsoleCursorInfo(hOutput, &cursorInfo);这个
CONSOLE_CURSOR_INFO结构体有两个成员:dwSize(光标大小百分比,1-100)和bVisible(是否可见,TRUE或FALSE)。 -
SetConsoleCursorInfo: 修改结构体里的信息,然后再设置回去,才能真正生效。
cursorInfo.bVisible = FALSE; // 设置为不可见 SetConsoleCursorInfo(hOutput, &cursorInfo);
我当初在这里踩过一个坑:以为改了cursorInfo就行了,忘了调用SetConsoleCursorInfo,结果光标死活隐藏不了,排查了半天。所以记住这个流程:获取 -> 修改 -> 设置。
2.4 精准定位:在控制台上“坐标”打印
我们的蛇和食物,需要出现在特定的位置,不能总是从左上角开始打印。控制台也有自己的坐标系:左上角是原点(0, 0),X轴向右增长,Y轴向下增长。
这里用到另一个结构体COORD和函数SetConsoleCursorPosition。
typedef struct _COORD

&spm=1001.2101.3001.5002&articleId=155044576&d=1&t=3&u=ef6a78ec05b741018f1cb4be22f1856b)
1030

被折叠的 条评论
为什么被折叠?



