提到DevC++,网上的评价一直两极分化。有人嫌它界面老气,有人觉得它只配用来交C语言作业。但如果你目标明确——在C++学习路上写点小游戏练手,DevC++反而是我见过最省心的环境。它不用装几GB的组件,不用折腾环境变量,下载完就能敲代码,按F11就能看到自己的程序跑起来,这对刚接触C++的人来说,吸引力比那些"专业IDE"强太多。这篇内容把我实测能在DevC++直接编译运行的C++小游戏做了个合集,覆盖猜数字、贪吃蛇、2048、扫雷和俄罗斯方块,每款都给出核心实现思路和关键代码,适合刚学完循环、数组、函数,想用项目巩固语法的新手,也适合上课需要交课程设计但不想用图形库折腾的同学。
1. 为什么挑选DevC++来整理小游戏合集
1.1 DevC++是不是真的"过时"了
很多人一听到DevC++就摇头,理由无非是"项目停更好多年""界面不好看"。这个评价有一定道理,但要分场景看。DevC++这个IDE确实是老东西,可它自带的编译器不是上古版本。常见的DevC++ 5.11默认集成TDM-GCC 4.9.2,支持C++11的大部分特性,写小游戏涉及到的随机数、容器、文件流、Windows API都能正常编译。对小体量的控制台游戏来说,它的性能完全够用。
更重要的是,DevC++的工作流极其简单。新建一个源代码文件,写好逻辑,编译运行,就这三步。不用像Visual Studio那样创建一个工程还要选一堆模板,也不用像VSCode那样手动配置tasks.json和launch.json。写贪吃蛇这种单文件程序,DevC++天然就是顺手的工具。
我见过太多初学者在配置IDE阶段就被劝退,装了VS发现还要装工作负载,装完又不知道去哪写第一行代码。用DevC++的话,双击打开就是编辑器,这种"打开即用"的流畅感,在折腾过各种环境之后才会真正意识到多珍贵。
1.2 小游戏开发对编译环境的核心需求
控制台小游戏虽然简单,但对编译器有一些基本要求,这些要求DevC++正好都满足。
一是C++标准支持。小游戏用到的随机数、时间函数、文件读写这些,都需要C++98以上的标准,偶尔会用到C++11的语法。DevC++自带的GCC编译器可以在编译器选项中手动指定支持标准,我建议拿到手先把编译标准设置为C++11,这样代码写起来束缚更少。
二是平台相关API的支持。控制台游戏想要控制光标位置、改变文字颜色、读取键盘按键,绕不开Windows API和conio.h。DevC++在Windows下对这些头文件的支持很成熟,不像在Linux上用codeblocks还得装额外的库。
三是单文件编译能力。小游戏合集最适合的形式就是每款游戏一个.cpp文件,甚至可以把五款游戏塞进一个文件通过菜单切换。DevC++的"编译当前文件"功能对这种组织方式很友好,不像工程型IDE那样强制你建一堆目录和源文件。
拿Visual Studio对比一下:VS对单文件编译不是不行,但每回新建项目都给你生成一整套预编译头、解决方案,对一个只想写两百行贪吃蛇的人来说,这些结构反而是负担。Code::Blocks也还行,但配置编译器那一步就劝退了不少新手。VSCode更不用说了,配置C/C++环境本身就能写出一篇排错文章。
1.3 环境准备:安装、设置与最容易被忽略的细节
如果你还没装DevC++,去官网下载5.11版本,或者用它的后续维护分支。安装过程一路Next就行,唯一要注意的是安装路径和源码路径尽量别带中文和空格,虽然大多数情况下没问题,但偶尔会因为路径编码引发奇怪报错,没必要赌这个。
装完之后建议做三件事。
第一件,设置编译器标准。菜单栏"工具 -> 编译器选项 -> 常规 -> 在编译命令中加入以下命令",输入-std=c++11,或者直接在设置里勾选对应的标准选项。这样代码里用C++11特性时才不会莫名报错。
第二件,调整编辑器的制表符。小游戏代码缩进比较多,建议在"工具 -> 编辑器选项 -> 基本"里把Tab设为4个空格,然后勾选"使用空格代替制表符"。这样代码无论是拷贝到博客还是发给同学,格式都不会乱。
第三件,把自动保存打开。"工具 -> 编辑器选项 -> 基本 -> 自动保存"里设置一个间隔,防止写一半DevC++崩溃导致白干。
还有个容易踩的坑:网上很多DevC++安装包会捆绑其他东西。下载时认准SourceForge上的官方链接,或者用可信的下载源。如果杀毒软件提示什么"风险程序",先别急着点允许,确认下载来源没问题再说。TDM-GCC编译器本身会被某些杀软误报,这是老问题了,根源是DevC++调用了编译器生成可执行文件的动作,和病毒的某些行为有点像。遇到这种情况,优先选择加入信任区而不是关闭杀软。
2. 控制台游戏真正需要的公共能力
2.1 想清楚再动手:控制台环境下的小游戏长什么样
先说个反常识的点:控制台不是做不出好玩的游戏,而是很多人不知道控制台能做的事比想象中多。除了最基础的printf输出文字,Windows控制台还支持光标定位、颜色切换、读取方向键,甚至能通过Beep函数发出简单音效。这些能力组合起来,足够做出画面流畅、有交互反馈的小游戏。
但控制台也有天然的限制。它是逐行刷新的字符终端,不是一个像素画布。所以控制台小游戏的设计思路要跟着它的特性走:用字符代表游戏对象,用坐标来管理位置,用定时循环来驱动画面更新。比如贪吃蛇里的蛇身可以用一串坐标点表示,食物就是一个坐标点,整个地图是一张二维数组。理解了"游戏 = 状态 + 输入 + 刷新"这个模型,后续每款游戏都会觉得顺很多。
2.2 公共模块:把光标、颜色、按键这些脏活统一封装
小游戏里的很多操作是共用的,比如把光标移动到指定坐标画一个字符。与其每个游戏都重复写一遍,不如先封装一个简单的控制台工具函数集合。
光标定位是控制台游戏最核心的函数,几乎所有游戏都依赖它。通过Windows API里的SetConsoleCursorPosition可以精确控制光标到第几行第几列,实现"局部重绘"而不是把整个屏幕清掉重画。局部重绘的好处后面讲闪屏问题时还要细说,这里先记住结论:尽量别用system("cls")全屏清屏,闪烁会非常严重。
隐藏光标这个细节也很关键。玩贪吃蛇或者2048的时候,如果光标一直在屏幕上一闪一闪,体验会差很多。用SetConsoleCursorInfo函数可以把光标隐藏掉。
颜色控制用SetConsoleTextAttribute,传入颜色常量就能让输出的文字变色。用它可以做出不同颜色的蛇、不同颜色的数字块、醒目的游戏结束提示。
按键检测用到_kbhit()和_getch(),这两个函数在conio.h里。_kbhit()用来判断当前是否有按键按下,_getch()用来读取一个字符但不回显。这两个函数组合起来能让游戏在"无阻塞"的状态下响应键盘,这是实时游戏的关键。
随机数模块用rand()配合srand(),种子来自time(NULL)。小游戏里的随机数主要用在生成食物位置、洗牌、生成数字方块上。需要注意的是srand只需要调用一次,写在程序开头就行,别放在循环里反复种种子。
延时用Sleep()函数,单位是毫秒。游戏主循环通过它控制刷新频率,比如每50毫秒刷新一次,贪吃蛇的移动节奏就稳定了。
下面是一个精简版的控制台工具模块,代码不长,我实测在DevC++里能直接编译通过:
#include <windows.h> #include <conio.h> #include <iostream> using namespace std; void gotoxy(int x, int y) { COORD pos = {x, y}; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos); } void hideCursor() { HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cci; GetConsoleCursorInfo(hOut, &cci); cci.bVisible = false; SetConsoleCursorInfo(hOut, &cci); } void setColor(int color) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color); }这套封装在Windows环境下非常稳定,所有小游戏都可以直接复用。
2.3 难度梯度:五款游戏怎么排列最合理
合集不能只堆代码,得照顾学习节奏。我安排了一个难度梯度,从纯语法练习到综合逻辑训练,一层层递进。
猜数字最简单,只用到了循环、分支和随机数,适合刚学完基础语法的人,跑通它最大的意义是建立"我写的东西能跑起来"的信心。
贪吃蛇是第一道坎,涉及地图、坐标存储、方向输入和碰撞检测,数组和函数的运用开始密集起来。但它的核心逻辑很直观,蛇移动的本质是"在蛇头加一个点,在蛇尾删一个点",想通这一点难度就降下来了。
扫雷的难点在递归展开,但地图本身只是二维数组,反而是很好的数组练习题。它和贪吃蛇的难点方向不一样,贪吃蛇考的是状态更新,扫雷考的是搜索算法。
2048的难度在于方向处理和合并规则,四方向移动如果硬写成四套逻辑会很痛苦,用坐标变换把四个方向归一到同一套逻辑才是正解。
俄罗斯方块放在压轴,因为它把数组、矩阵旋转、碰撞检测、消除判定全用上了,是控制台小游戏的集大成者。如果前面几款都能写出来,俄罗斯方块就是检验综合能力的最好项目。
这五款游戏每一款的背后都有明确的练习目标:猜数字练随机与循环,贪吃蛇练动态坐标管理,扫雷练递归与边界,2048练矩阵变换,俄罗斯方块练旋转与碰撞。别跳着玩,按顺序啃下来收获最大。
3. 五款小游戏的实现拆解与关键代码
3.1 猜数字:用随机数和循环搞定第一个可玩程序
猜数字的逻辑一句话就能说清:程序随机生成一个1到100的整数,玩家输入数字,程序告诉玩家大了还是小了,直到猜中为止。
核心是随机数生成。DevC++的C++环境下,rand()返回一个0到32767之间的整数,取模100就得到0到99,再加1就是1到100。关键是程序每次运行时都要调用srand(time(NULL))设置随机种子,否则每次启动都是同一个序列,游戏就没法玩了。
下面是完整可运行版本:
#include <iostream> #include <cstdlib> #include <ctime> using namespace std; int main() { srand(time(NULL)); int target = rand() % 100 + 1; int guess, times = 0; cout << "系统已生成一个 1-100 之间的数字,来猜猜看:" << endl; while (true) { cin >> guess; times++; if (guess < target) { cout << "小了,继续" << endl; } else if (guess > target) { cout << "大了,继续" << endl; } else { cout << "猜对了!你用了 " << times << " 次。" << endl; break; } } return 0; }运行起来之后可以自己改几个地方练手:限制最多7次猜完,超过就失败;或者把范围扩大到1到1000。这些小改动都能加深对循环和分支的理解。
3.2 贪吃蛇:数组模拟蛇身,头尾指针管理移动
贪吃蛇是控制台小游戏的经典入门项目。它在DevC++下的难点不在"蛇怎么动",而在"方向控制"和"画面刷新"。
我推荐用两个数组分别存蛇的x坐标和y坐标,再用head和tail两个指针标记蛇头蛇尾在数组中的位置。蛇的移动逻辑极其简洁:在head方向加一个点,如果吃到了食物就不删尾部,否则删除尾部。这种用数组模拟环形队列的做法,比用链表更容易被初学者理解。
蛇移动的核心代码:
const int MAX_LEN = 1000; int sx[MAX_LEN], sy[MAX_LEN]; int head = 0, tail = 0; // dir: 0上 1右 2下 3左 int dx[4] = {0, 1, 0, -1}; int dy[4] = {-1, 0, 1, 0}; void moveSnake(int dir) { int nx = sx[head] + dx[dir]; int ny = sy[head] + dy[dir]; // 检查是否会撞墙或撞到自己 if (map[nx][ny] == '#' || map[nx][ny] == 'O') { gameOver(); return; } if (map[nx][ny] == '*') { // 吃到食物,不删尾部 map[nx][ny] = 'O'; score += 10; head++; sx[head] = nx; sy[head] = ny; createFood(); } else { // 普通移动,删尾 map[sx[tail]][sy[tail]] = ' '; tail++; head++; sx[head] = nx; sy[head] = ny; map[nx][ny] = 'O'; } }这个数组写法有个好处:head和tail指针只增不减,只要最大长度设得够大就不会越界,也不用移动数组元素,时间上非常高效。
方向控制的坑在于方向键在Windows下的读取方式比较特殊。_getch()读取方向键需要调用两次,先返回224,再返回一次方向码。为了简化代码,我建议在游戏里直接用WASD控制方向,这样一次_getch()就能拿到字符,逻辑简洁很多。如果想支持方向键,就额外加一个判断分支,这种"扩展"留给你自己实现更有价值。
画面刷新方面,我用gotoxy(0, 0)把光标移到左上角,然后每帧重新绘制整个地图。地图大小控制在20x20以内,每帧绘制量很小,重绘速度飞快,闪烁问题基本可以忽略。
3.3 扫雷:二维数组存储,递归展开空格
扫雷地图本质上是一个M行N列的二维数组。数组里存三种信息:是否埋雷、周围雷的数量、当前是否被翻开。可以把这三个信息合成到一个结构体里,也可以用三个数组分别表示,初学者用三个数组反而更直观。
生成地雷后,要计算每个非雷格子周围八个格子的雷数。这个计算用双重循环遍历每个格子,再嵌套两层循环检查周围八个方向。虽然粗暴,但对小地图来说效率绰绰有余。
扫雷最核心的算法是"点开一个空格,自动展开相邻所有空格"——这是递归展开。当玩家点开的格子周围雷数为0,就递归检查它周围的八个格子,如果周围仍然为0就继续递归,直到遇到数字格子停下。
递归展开函数:
int mineMap[10][10]; // -1表示雷,其他表示周围雷数 bool opened[10][10]; int n = 10, m = 10; void openBlock(int x, int y) { if (x < 0 || x >= n || y < 0 || y >= m) return; if (opened[x][y]) return; opened[x][y] = true; if (mineMap[x][y] == -1) { // 踩雷,游戏结束 gameOver(); return; } if (mineMap[x][y] > 0) return; // 是数字格,只翻开自己 // 空格,递归展开周围 for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { openBlock(x + i, y + j); } } }注意边界判断必须放在递归函数最前面。很多初学者写扫雷时程序运行到一半崩溃,就是因为递归展开时坐标越界了。边界判断越早返回,代码越安全。
扫雷还有一个体验上的问题:控制台程序没有鼠标,怎么点击格子?我在DevC++版本里用的方案是光标移动加空格键确认。用键盘上下左右控制光标位置,空格键翻开格子,F键标记旗帜。这样虽然不如鼠标方便,但程序逻辑反而更清晰,按键处理也简单。
3.4 2048:四方向移动背后的统一逻辑
2048的控制台版本用4x4数组存储数字。每次移动时,所有数字向某个方向滑动,相同数字相邻时合并。很多人卡在"四个方向要写四种移动规则"上,其实完全不用。
聪明做法是:只实现"向左移动"一种逻辑,其他三个方向通过数组坐标变换转换成向左移动。向右移动等于把每行数组反转后执行左移,再反转回来;向上移动等于把矩阵转置后执行左移,再转置回来;向下移动等于转置+反转+左移+反转+转置。写过一次坐标变换的辅助函数后,四个方向都只需要一行调用。
向左合并的核心代码:
void mergeRow(int row[]) { int tmp[4] = {0}; int k = 0; for (int i = 0; i < 4; i++) { if (row[i] != 0) { tmp[k++] = row[i]; } } // 合并相邻相同方块 for (int i = 0; i < 4; i++) row[i] = 0; k = 0; for (int i = 0; i < 4; i++) { if (tmp[i] == tmp[i + 1]) { row[k++] = tmp[i] * 2; i++; } else { row[k++] = tmp[i]; } } }这里有一个细节:合并只能一次完成,不能连锁合并。比如一行是 [2, 2, 4, 0],向左移动后应该得到 [4, 4, 0, 0],而不是 [8, 0, 0, 0]。上面的代码通过"先取数暂存再配对合并"的方式,天然避免了一次移动内反复合并的问题。
每次移动后,在空白格子里随机生成一个2或者4。生成策略很简单:收集所有空白格子的坐标,随机选一个,按一定概率填2或4。判断游戏结束的条件是"没有任何空白格子"且"任意相邻格子都不相等"。
2048的画面刷新我用4x4表格输出,每个数字右对齐占4个字符,配合SetConsoleTextAttribute给不同数字设置不同颜色,视觉效果比黑白的要好很多。
3.5 俄罗斯方块:矩阵旋转和碰撞检测收尾
俄罗斯方块是合集里最复杂的一款,也是代码量最大的一款。它的核心数据结构是7种方块,每种方块用4x4矩阵表示,用1表示有格子,0表示空位。7种方块的形状数据直接写成常量数组。
方块的旋转是难点。常见的做法是对矩阵做顺时针旋转:先把矩阵转置,再把每一行左右反转。这个操作写成函数非常短:
void rotate(int block[4][4]) { int tmp[4][4]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { tmp[i][j] = block[3 - j][i]; } } for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { block[i][j] = tmp[i][j]; } } }旋转之后的碰撞检测逻辑是检查方块落点会不会和边界或已堆积方块重叠。这个检测用一个canMove函数实现,返回true表示可以移动,false表示发生碰撞。
消行逻辑反而简单:从地图底部往上遍历,如果某一行全部为1,就把它上面所有行整体下移一行。注意下移要从上往下处理,否则会错位。消行后计分,每消一行分数翻倍或者按阶梯加分,这个可以自由设计。
俄罗斯方块在控制台下的运行节奏和贪吃蛇类似,主循环不断处理输入、检测碰撞、更新画面。我给这部分建议的完整代码量在300行左右,初学者写到这个程度已经能收获很大的成就感。这里不再贴完整代码,因为前面已经拆出了最核心的旋转和消行逻辑,把方块生成和主循环补上就能跑起来,这个"补全"过程本身也是很好的练习。
4. DevC++跑这些代码的典型报错与排错记录
4.1 中文乱码:源代码与控制台的编码战争
DevC++跑小游戏最常见的毛病就是中文乱码。程序输出的中文变成一堆"锟斤拷"或者"烫烫烫",让人瞬间没有继续玩下去的欲望。
造成乱码的根本原因是"源码文件编码"和"控制台代码页"不一致。DevC++的老版本默认把源码保存为ANSI编码,在简体中文Windows上也就是GBK,而控制台默认代码页通常是936(GBK),这时候源码里的中文字符串和运行时的编码是匹配的,显示正常。
问题往往出现在这些时候:编辑器某次自动判断编码失误,把文件保存成了UTF-8;或者你从网上复制了一段带中文字符的代码,文件编码被混用了;又或者你手动设置了控制台输出代码页为UTF-8。
解决办法有几个,按优先级排列:
- 最省事:游戏界面全部用英文提示。英文不涉及编码问题,在任何环境下都不会乱码。合集里的小游戏我默认用英文输出。
- 保留中文:检查DevC++的"工具 -> 编辑器选项 -> 基本 -> 新文件编码",选择ANSI,并确保你的源码本来就保存为ANSI。
- 在代码开头调用
SetConsoleOutputCP(65001)切换到UTF-8输出,前提是你的源码确实是UTF-8编码,并且DevC++使用的GCC版本能正确处理中文字符串字面量。这个方法在老版本上并不稳定,不一定推荐。
乱码问题排查的核心是"先确认源码文件是什么编码,再确认控制台是什么编码",两边对上就不会乱码。
4.2 编译报错中的几个"经典款"
小游戏代码量不大,报错种类也比较集中。我在整理合集过程中反复遇到几类,记录在这儿方便对照。
[Error] stray '\241' in program,这个报错最常见。它的意思是源码里出现了非法字符,多半是中文字符或全角标点混进了代码。你可能在注释里用了中文没问题,但如果在中文字符串外面误用了中文分号、中文引号,GCC就直接不认。排查办法是看报错行号,把全角标点改成半角。
undefined reference to WinMain@16,这个报错说明你的编译选项按Windows窗口程序处理,但它没找到对应的WinMain入口函数。原因通常是你用"新建项目"时选了Windows Application,或者编译器参数里加了-mwindows。解决办法是写控制台程序时直接新建源代码文件,或者检查工程属性里的GUI/Console选项是不是切错了。
ld returned 1 exit status,这个报错很唬人,其实大部分时候不是代码问题,而是上一次运行的程序还没退出,exe文件被占用导致链接器无法写入。把之前运行起来的黑窗口关掉,重新编译就解决了。
还有一种情况:DevC++编译没有任何报错,但是双击运行exe时窗口一闪而过。这是因为程序执行完就退出了,控制台窗口自动关闭。解决办法是在main函数结束前加一句cin.get()或system("pause"),让它等着用户按任意键。
4.3 运行时的闪屏与按键异常
小游戏做出来之后,很多人遇到的第一个体验问题是"画面闪烁严重"。根源在于每帧都用system("cls")清除整个屏幕再重新输出。控制台刷新速度有限,全屏清空重画就会闪。
解决办法是用gotoxy回到起点,然后一帧一帧覆盖整个地图,而不是清空屏幕。这样即使重绘全部地图,也不会产生"先空白再显示"的过程。
第二个问题是按键方向异常。如果你在贪吃蛇里用了方向键,会发现_getch()第一次调用返回224,第二次才返回真正的方向码。这是因为方向键在Windows里属于扩展按键,读取方式和普通字符不一样。处理方式有两种:一是用WASD代替方向键;二是读取到224后,再调用一次_getch()。
第三个问题是游戏运行时CPU占用率高。如果不加延时直接死循环,一个小游戏可能吃掉一个完整CPU核心。解决办法很简单,在主循环里调用Sleep(50)。实测贪吃蛇50毫秒刷新一次,速度适中且CPU占用低。
4.4 用DevC++自带的调试器定位逻辑错误
小游戏写多了难免遇到逻辑错误:贪吃蛇无敌穿墙、扫雷递归死循环、2048合并结果不对。这种问题光看代码很难发现,用调试器单步执行是最快的定位方式。
DevC++的调试器很简单,在代码行号区域点击一下设置断点,然后按F8启动调试,程序会停在断点处。接着用F7逐行执行,鼠标悬停在变量上就能看到它的当前值,或者把变量拖到"监视"窗口里持续观察。
我举个实际排查案例。写2048时合并结果总是不对,我就在mergeRow里设置了断点,每次合并后查看tmp数组的值,发现是"先压缩再合并"的顺序出了问题。如果没有调试器,靠肉眼看数组变化特别容易漏。
调试器的使用习惯值得早点养成。小游戏项目小,调试成本低,非常适合作为学习断点和单步执行的入门素材。
5. 从"能跑"到"好玩":写代码习惯和体验优化
5.1 找准属于自己的项目组织方式
五款游戏写完之后,可以把它们整合到一个程序里,做一个菜单选择界面。这样有五合一的成就感,所有代码都在一个cpp文件里,DevC++编译起来也很轻松。
菜单层级的main函数很简单:
int main() { while (true) { system("cls"); cout << "1. Guess Number" << endl; cout << "2. Snake" << endl; cout << "3. Minesweeper" << endl; cout << "4. 2048" << endl; cout << "5. Tetris" << endl; cout << "0. Exit" << endl; char ch = _getch(); switch (ch) { case '1': guessNumber(); break; case '2': snakeGame(); break; case '3': minesweeper(); break; case '4': game2048(); break; case '5': tetris(); break; case '0': return 0; } } }把每款游戏封装成独立函数,主菜单只负责调度。这样每个游戏互不干扰,后续想删掉哪个或者新增哪个都很方便。
5.2 用enum管理游戏状态,别用一堆if硬扛
小游戏的流程往往分为几个阶段:菜单、游戏中、暂停、游戏结束。初学者习惯用int变量加一堆if判断当前是哪个状态,代码一长就混乱了。
更好的做法是用枚举类型定义游戏状态:
enum GameState { MENU, PLAYING, GAMEOVER }; GameState state = MENU; switch (state) { case MENU: showMenu(); if (keyPressed()) state = PLAYING; break; case PLAYING: updateGame(); render(); break; case GAMEOVER: showGameOver(); break; }这种结构把每个状态的处理逻辑隔离开,游戏逻辑清晰很多。以后加一个"暂停"状态,只需要在枚举里加一个值,再补一个case就行,不需要改其他逻辑。
5.3 让游戏更有温度:计分、最高纪录和音效
一个能跑的小游戏和一个让人愿意多玩几遍的小游戏,差距往往在细节上。
计分是最基本的。贪吃蛇每吃一个食物加10分,扫雷可以按翻开格数算分,2048直接看最大数字和总分数,俄罗斯方块按消行数给分。分数显示在界面固定的角落,配合颜色变化提示分数提升。
最高纪录的保存用小游戏很容易实现:
#include <fstream> int readHighScore() { ifstream in("score.dat"); int score = 0; if (in) in >> score; return score; } void saveHighScore(int score) { ofstream out("score.dat"); out << score; }文件读写是C++里很基础但特别实用的能力,游戏里加入最高分记录后,玩家会为了突破纪录反复玩,这个成就感是单纯练语法给不了的。
音效方面,DevC++可以直接用Windows API里的Beep函数发出特定频率的声音。比如吃食物时Beep(1000, 50),游戏结束Beep(200, 300)。注意Beep是阻塞的,在执行期间程序会停住,所以音效要短,或者只用在关键事件上,别放在需要连续反馈的动画里。
5.4 从控制台走向图形界面:EGE库是自然的下一步
控制台小游戏做完之后,你会发现自己已经掌握了游戏开发里最重要的一半:逻辑、状态管理、碰撞检测、刷新循环。剩下的一半是图形渲染,而这是控制台永远给不了的。
DevC++配合EGE库(Easy Graphics Engine)是很多人的选择。EGE是一个图形库,它能让你用几乎同样的思路在窗口里画矩形、画图片、响应鼠标键盘,但画面质感完全不一样。网上说"DevC++配置EGE"其实很简单,把EGE库的几个文件放进DevC++的include和lib目录,然后在编译器配置里加上对应的库名,就能用了。注意EGE库需要和你的编译器版本匹配,这是配置时最大的坑,下载前先看清楚位数和GCC版本。
不过我的建议是:先把控制台版本的五款游戏做完,再考虑EGE。控制台版本的难点全在逻辑,图形库版本的难点全在接口和资源配置。前者是真正的编程基础,后者更多是工具使用。顺序颠倒的话,容易陷入"调库调不出来"的挫败感。
我在实际整理这套合集的过程中最深的感觉是,DevC++老归老,但它把"写程序"这件事的门槛降到了极低。你不用在环境配置上消耗意志力,能把所有精力花在思考游戏逻辑本身。五款游戏写下来,涉及的C++语法点几乎覆盖了大学C++课程的大部分核心内容,远远比做一百道填空题来得扎实。如果你正好在学C++学到瓶颈,别去找那些花花绿绿的网页小游戏,试着打开DevC++,从猜数字开始,敲出一个真正能跑的表达自己的程序,收获会超出你的预期。