EasyX图形库入门:C/C++图形化编程与游戏开发实战指南 1. 项目概述为什么选择EasyX作为C/C图形化入门利器如果你正在学习C或C厌倦了黑乎乎的控制台想亲手做出一个能看、能玩的图形程序甚至是一个小游戏那么EasyX几乎是你绕不开的起点。我第一次接触它是在大学的一门课程设计里当时用几十行代码就画出了一个会动的太阳系模型那种从抽象代码到具象图形的成就感至今记忆犹新。EasyX是一个专为Visual C以及兼容的C环境设计的轻量级绘图库它的核心价值就体现在“简单”二字上。你不需要先去啃OpenGL或DirectX那些庞杂的API也不用去折腾复杂的窗口消息循环EasyX帮你封装好了最基础的绘图函数让你能像在画板上涂鸦一样用代码“画”出图形界面和游戏。它解决的正是初学者从命令行到图形界面的巨大鸿沟问题。通过一系列如circle、line、putpixel这样直观的函数你可以立即看到编程的视觉化成果这对于保持学习兴趣、理解程序运行逻辑至关重要。无论是想验证一个算法比如排序过程可视化、完成课程设计还是纯粹想做个《贪吃蛇》、《俄罗斯方块》这样的小游戏练手EasyX都能让你快速上手。它的学习曲线极其平缓你只需要有C/C的基础语法知识就可以开始你的图形化编程之旅。对于教学、个人兴趣项目以及需要快速原型验证的场景EasyX都是一个高效且友好的工具。2. 环境搭建与第一个图形程序2.1 开发环境的选择与配置EasyX官方主要支持Microsoft Visual StudioVC系列从古老的VC6.0到最新的VC2022都能完美运行。对于初学者我强烈推荐使用Visual Studio 2022 Community版它是免费的并且集成了现代C开发所需的一切。安装时记得勾选“使用C的桌面开发”工作负载。如果你习惯于其他轻量级IDE如Dev-C、Code::Blocks或CLionEasyX也能通过一些配置来使用。以**Dev-C**为例你需要手动将EasyX的库文件libeasyx.a和头文件easyx.h等复制到Dev-C的MinGW对应目录下并在项目链接器设置中添加-leasyx参数。不过这条路偶尔会遇到一些编译兼容性问题。对于追求稳定和官方最佳支持的场景Visual Studio仍然是首选它能让你避开很多环境配置的坑把精力完全集中在代码逻辑本身。注意EasyX是一个基于Windows GDI的库因此你的程序本质上是Windows图形界面程序。这意味着它通常无法直接在Linux或macOS上运行。如果你的目标是跨平台后期可能需要转向SDL、SFML或Qt等框架但作为图形编程的入门和原型工具EasyX在Windows下的便捷性无与伦比。2.2 EasyX的安装与验证安装EasyX简单到令人发指。访问其官方网站easyx.cn下载对应你Visual Studio版本的安装包。运行安装程序它会自动检测已安装的VS版本你只需点击“安装”即可整个过程不超过一分钟。安装完成后如何验证打开Visual Studio创建一个新的“空项目”或“控制台应用”。这里有一个关键选择我建议创建“空项目”然后手动添加一个.cpp源文件。因为“控制台应用”模板会默认链接一些控制台相关的库有时在关闭图形窗口时可能会遇到控制台窗口残留的问题。而在“空项目”中一切由你掌控。在新建的.cpp文件中输入以下经典的“Hello Graphics World”代码#include graphics.h // 引入EasyX图形库头文件 #include conio.h // 用于_getch()函数等待按键 int main() { // 初始化一个640x480的图形窗口 initgraph(640, 480); // 设置当前绘图颜色为红色 setcolor(RED); // 在窗口中央画一个圆圆心(320,240)半径100 circle(320, 240, 100); // 设置填充颜色为黄色并填充圆 setfillcolor(YELLOW); floodfill(320, 240, RED); // 从圆心开始遇到红色边界则填充黄色 // 在圆下方输出文字 settextstyle(24, 0, _T(宋体)); // 设置字体高24像素默认宽度宋体 settextcolor(BLUE); outtextxy(250, 360, _T(你好EasyX!)); // 按任意键关闭图形窗口 _getch(); closegraph(); return 0; }编译并运行F5。如果一切顺利你将看到一个红色边框、黄色填充的圆以及一行蓝色的文字。恭喜你的图形化编程世界已经开启这个简单的程序包含了EasyX最核心的几个步骤初始化图形窗口、设置属性颜色、字体、绘制图形/文字、消息循环这里用_getch()简单模拟、关闭窗口。2.3 理解EasyX的坐标系统与双缓冲机制运行第一个程序后你需要立刻建立两个核心概念坐标系统和双缓冲。坐标系统EasyX的绘图窗口左上角是原点(0,0)X轴向右递增Y轴向下递增。这和我们数学中常见的Y轴向上递增不同需要一点时间适应。circle(320,240,100)中的(320,240)就是指距离窗口左边界320像素上边界240像素的点。双缓冲机制这是图形编程中防止屏幕闪烁的关键技术。你可以把屏幕想象成一张画布。如果没有双缓冲你的程序直接在屏幕上“作画”比如先擦掉旧圆再画新圆用户就会看到短暂的擦除过程造成闪烁。EasyX默认在initgraph后为你开启了双缓冲。它的原理是所有绘图操作实际上是在内存中一张不可见的“后台画布”上进行的。当你完成一帧的所有绘制后调用FlushBatchDraw()函数或EndBatchDraw()EasyX会将整张后台画布一次性“翻页”到前台显示。这样用户看到的就是完整的、瞬间更新的画面没有任何中间状态的闪烁。在游戏开发中我们通常在循环里这样写while (true) { cleardevice(); // 清空后台画布 // ... 绘制本帧所有图形 ... FlushBatchDraw(); // 将后台画布显示到屏幕 Sleep(10); // 控制帧率暂停10毫秒 }理解并善用双缓冲是做出流畅动画的第一步。3. 核心绘图函数详解与图形学基础实践掌握了环境搭建和基本流程后我们来深入工具箱看看EasyX提供了哪些“画笔”和“颜料”。3.1 基本图形绘制点、线、面EasyX提供了一套非常直观的几何图形绘制函数其参数命名清晰几乎无需查阅文档即可使用。点putpixel(x, y, color)在指定坐标画一个像素点。这是所有图形的基础。线line(x1, y1, x2, y2)从点(x1,y1)到点(x2,y2)画一条直线。与之相关的还有lineto(x, y)它从“当前点”画线到指定点并更新当前点为(x,y)。moveto(x,y)则用于移动当前点而不画线。矩形rectangle(left, top, right, bottom)绘制空心矩形。fillrectangle用于绘制实心填充矩形。圆/椭圆circle(x, y, radius)画圆。ellipse(left, top, right, bottom)在指定的矩形边界内画椭圆。多边形polygon(const POINT *points, int num)根据点数组绘制多边形。这些函数看似简单但组合起来威力巨大。例如我们可以用line函数来模拟一个时钟的秒针动画#include graphics.h #include conio.h #include cmath #include ctime int main() { initgraph(400, 400); setbkcolor(WHITE); // 设置背景色为白色 cleardevice(); int centerX 200, centerY 200; int radius 150; // 画一个静态的钟表外圈 setcolor(BLACK); setlinestyle(PS_SOLID, 3); // 设置线型为实线3像素宽 circle(centerX, centerY, radius); // 动态秒针 while (!_kbhit()) { // 当没有按键时循环 // 获取当前时间的秒数 time_t now time(0); struct tm* t localtime(now); int second t-tm_sec; // 计算秒针末端坐标 (每秒走6度) double angle second * 6 * 3.14159 / 180; // 转换为弧度 int endX centerX (int)(radius * 0.9 * sin(angle)); int endY centerY - (int)(radius * 0.9 * cos(angle)); // 注意Y轴向下所以用减号 // 用双缓冲机制绘制 cleardevice(); // 重绘静态表盘 circle(centerX, centerY, radius); // 绘制秒针红色2像素宽 setlinestyle(PS_SOLID, 2); setcolor(RED); line(centerX, centerY, endX, endY); FlushBatchDraw(); Sleep(50); // 每50毫秒刷新一次让动画更平滑 } closegraph(); return 0; }这个例子综合运用了图形绘制、双缓冲、简单数学计算和时间函数是一个很好的综合练习。3.2 颜色、样式与文字输出丰富的视觉表现离不开颜色和样式。颜色EasyX使用COLORREF类型表示颜色。你可以直接用预定义常量如RED,GREEN,BLUE也可以用RGB(r, g, b)宏来自定义例如RGB(255, 200, 0)是橙色。setcolor设置线条和文字颜色setfillcolor设置填充颜色setbkcolor设置背景颜色。线型setlinestyle可以设置线的样式实线、虚线、点线、宽度。这在绘制坐标轴、网格线时非常有用。填充样式setfillstyle可以设置填充图案除了实色填充还支持各种预定义的斜线、网格等图案。文字输出这是让程序与用户交互的重要途径。settextstyle用于设置字体高度、宽度为0表示自适应、字体名称。outtextxy(x, y, text)在指定位置输出字符串。这里需要注意字符编码问题为了兼容性建议使用_T()宏包裹字符串字面量如_T(“Hello”)并在项目属性中设置字符集为“使用多字节字符集”或“使用Unicode字符集”并保持一致。一个常见的坑是文字位置的计算。outtextxy指定的坐标是文字区域的左上角。如果你想让文字居中显示需要先使用textwidth和textheight函数获取字符串的像素宽度和高度然后进行计算const TCHAR* str _T(居中文字); int textWidth textwidth(str); int textHeight textheight(str); int x (getwidth() - textWidth) / 2; int y (getheight() - textHeight) / 2; outtextxy(x, y, str);3.3 图像处理加载、显示与简单操作除了绘制矢量图形EasyX也支持位图操作这是游戏开发中显示角色、背景的基础。加载图像使用loadimage函数从文件如jpg,png,bmp或资源中加载图像到IMAGE对象中。IMAGE是一个结构体承载了图像的像素数据。IMAGE img; loadimage(img, _T(hero.png)); // 从文件加载 // 或者 loadimage(img, _T(res/hero.png), 100, 100); // 同时缩放到100x100显示图像使用putimage函数将IMAGE对象绘制到屏幕上。// 在坐标(50,50)处显示原图 putimage(50, 50, img); // 使用三元光栅操作码进行透明贴图后续详解 // putimage(50, 50, img, SRCAND); // 与操作常用于黑色背景白色图案的掩码图图像操作你可以通过GetImageBuffer函数获取IMAGE对象内部像素数据的指针直接操作内存中的每一个像素BGR顺序实现自定义的图像滤镜如灰度化、反色、模糊等。这是深入理解计算机图形学中光栅操作的好机会。实操心得处理图像路径时尽量使用相对路径并将资源文件如图片放在项目目录下的一个子文件夹如res中。这样在发布程序时只需将整个文件夹一起打包可以避免因绝对路径不同导致的“找不到文件”错误。另外png格式支持透明度但在EasyX中需要特殊处理使用SRCAND和SRCPAINT组合才能实现透明效果这比现代游戏引擎要繁琐但理解其原理对底层图形编程很有帮助。4. 交互逻辑与游戏循环构建图形有了如何让它动起来并响应用户操作这就进入了游戏开发的核心——交互循环。4.1 键盘与鼠标消息处理EasyX提供了非阻塞式的函数来获取输入设备状态这比传统的Windows消息循环更简单直观。键盘检测_kbhit()函数检查当前是否有按键被按下它不等待。_getch()会等待并读取一个按键。在游戏循环中我们通常用_kbhit()来非阻塞地检测。if (_kbhit()) { char key _getch(); switch (key) { case a: case A: playerX - speed; break; // 左移 case d: case D: playerX speed; break; // 右移 case w: case W: playerY - speed; break; // 上移 case s: case S: playerY speed; break; // 下移 case 27: isRunning false; break; // ESC键退出 } }注意_getch()无法直接获取方向键、功能键。对于这些扩展键它会先返回一个0或224你需要再调用一次_getch()来获取具体的键值码。鼠标检测MouseHit()类似_kbhit()用于检测鼠标事件。GetMouseMsg()获取一个鼠标消息结构体MOUSEMSG其中包含了坐标、按键状态左键、右键、中键是否按下以及滚轮信息。while (MouseHit()) { MOUSEMSG msg GetMouseMsg(); if (msg.uMsg WM_MOUSEMOVE) { // 鼠标移动msg.x, msg.y是坐标 } if (msg.uMsg WM_LBUTTONDOWN) { // 左键按下 } }4.2 游戏主循环与状态管理一个典型的游戏主循环Game Loop结构如下bool isRunning true; initgraph(800, 600); // 初始化 while (isRunning) { // 1. 处理输入 processInput(isRunning); // 2. 更新游戏逻辑位置、碰撞、状态等 updateGameLogic(); // 3. 渲染图形 cleardevice(); renderGraphics(); FlushBatchDraw(); // 4. 控制帧率避免CPU占用率100% static DWORD lastTime GetTickCount(); DWORD currentTime GetTickCount(); DWORD deltaTime currentTime - lastTime; if (deltaTime 1000 / 60) { // 目标每秒60帧 Sleep(1000 / 60 - deltaTime); } lastTime GetTickCount(); } closegraph();这个循环清晰地分离了输入、更新、渲染I-U-R三个核心阶段是游戏程序的基本骨架。状态管理是另一个关键。一个游戏可能有“开始菜单”、“游戏中”、“暂停”、“游戏结束”等多个状态。我们可以用一个枚举变量gameState来标识当前状态并在主循环中根据不同的状态执行不同的逻辑和渲染分支。enum GameState { MENU, PLAYING, PAUSED, OVER }; GameState gameState MENU; while (isRunning) { switch (gameState) { case MENU: processMenuInput(); renderMenu(); break; case PLAYING: processGameInput(); updateGame(); renderGame(); break; // ... 其他状态 } // ... 帧率控制 }这种模式让代码结构清晰易于维护和扩展。4.3 定时器与动画实现让物体动起来本质上是随时间改变其属性位置、颜色、大小等。除了在主循环中通过帧率控制来更新EasyX还提供了简单的定时器功能SetTimer和KillTimer它们基于Windows消息机制可以在指定的时间间隔触发一个事件。但对于大多数小游戏在主循环中使用基于时间的增量delta time来控制动画更为常见和灵活。基于时间的动画我们记录上一帧到这一帧的时间差deltaTime然后用这个时间差乘以速度来计算物体应该移动的距离。这样能保证在不同性能的电脑上物体的运动速度是均匀的而不是与帧率挂钩。// 在循环开始或结束时计算时间差 DWORD currentFrameTime GetTickCount(); float deltaTime (currentFrameTime - lastFrameTime) / 1000.0f; // 转换为秒 lastFrameTime currentFrameTime; // 更新位置 playerX playerSpeedX * deltaTime; playerY playerSpeedY * deltaTime;这种方式是实现平滑动画的关键。5. 实战从“贪吃蛇”到“飞机大战”的完整项目剖析理论说得再多不如动手做一个。我们以经典的《贪吃蛇》为例拆解其用EasyX实现的核心模块。5.1 游戏《贪吃蛇》的数据结构与初始化贪吃蛇的核心数据结构是蛇身我们可以用一个std::vectorPOINT或数组来存储每一节身体的坐标。食物是一个单独的POINT。游戏区域用一个固定的矩形范围表示。初始化工作包括创建指定大小的图形窗口。初始化蛇身比如从屏幕中心开始长度为3节。在游戏区域内随机生成一个不落在蛇身上的食物点。初始化蛇的移动方向如向右。设置游戏分数、速度等变量。#include graphics.h #include conio.h #include vector #include ctime #include cstdlib using namespace std; const int BLOCK_SIZE 20; // 每个格子的大小 const int WIDTH 30; // 网格宽度格子数 const int HEIGHT 20; // 网格高度 vectorPOINT snake; // 蛇身每个元素是一个格子的坐标 POINT food; // 食物坐标 int dir; // 方向0上1右2下3左 int score; bool isGameOver; void GameInit() { // 初始化随机种子 srand((unsigned)time(NULL)); // 初始化蛇从中心开始三节身体 snake.clear(); snake.push_back({WIDTH/2, HEIGHT/2}); snake.push_back({WIDTH/2-1, HEIGHT/2}); snake.push_back({WIDTH/2-2, HEIGHT/2}); // 初始方向向右 dir 1; // 生成第一个食物 GenerateFood(); score 0; isGameOver false; }5.2 核心游戏逻辑移动、吃食、碰撞与增长游戏主循环中每一帧的逻辑更新步骤如下处理输入检测方向键改变dir变量。注意不能直接反向移动比如正在向右走不能立即按左键。移动蛇头根据dir计算新的蛇头位置。POINT newHead snake[0]; // 获取当前蛇头 switch(dir) { case 0: newHead.y--; break; // 上 case 1: newHead.x; break; // 右 case 2: newHead.y; break; // 下 case 3: newHead.x--; break; // 左 }碰撞检测撞墙检查newHead.x或newHead.y是否超出游戏边界。撞自身遍历蛇身从第二节开始检查是否有任何一节坐标与newHead相同。发生碰撞则游戏结束。吃食物检测如果newHead坐标与food坐标相同则分数增加。不需要删除蛇尾蛇身增长一节。在空白处重新生成食物GenerateFood函数需确保食物不出现在蛇身上。更新蛇身如果吃到食物将newHead插入蛇身头部(snake.insert(snake.begin(), newHead))。如果没吃到将newHead插入头部并删除蛇尾(snake.pop_back())。5.3 图形渲染与用户体验优化渲染部分相对直观就是将数据结构的“状态”画出来。清屏与绘制网格可选用cleardevice()清屏可以用浅色线画出网格方便观察。绘制蛇身遍历snake向量为每一节身体画一个填充矩形。蛇头可以用不同的颜色区分。for (size_t i 0; i snake.size(); i) { int x snake[i].x * BLOCK_SIZE; int y snake[i].y * BLOCK_SIZE; if (i 0) { setfillcolor(GREEN); // 蛇头绿色 } else { setfillcolor(LIGHTGREEN); // 蛇身浅绿色 } fillrectangle(x, y, x BLOCK_SIZE, y BLOCK_SIZE); }绘制食物用红色实心圆表示。显示分数在窗口角落用outtextxy输出当前分数。游戏结束画面如果isGameOver为真在屏幕中央用醒目的字体和颜色显示“Game Over”和最终分数。用户体验优化点速度控制随着分数增加可以逐步减少每帧之间的Sleep时间让蛇移动更快增加难度。输入缓冲可以设置一个“下一方向”变量在处理输入时只记录按键在移动前再更新方向。这样可以解决快速连续按键时因帧率限制导致的输入丢失或逻辑错误。画面平滑可以尝试用圆角矩形或图片来绘制蛇身和食物让画面更美观。通过完成《贪吃蛇》你已经掌握了EasyX游戏开发的核心流程数据建模、输入处理、逻辑更新、碰撞检测、图形渲染。这个模式可以扩展到《俄罗斯方块》管理下落的方块和静态的堆叠区域、《打砖块》球的运动反射、砖块消除甚至简单的《飞机大战》敌机队列、子弹管理、碰撞检测等游戏。关键在于将游戏对象抽象为数据并在循环中不断地根据规则更新这些数据并将其绘制出来。6. 进阶技巧透明贴图、双缓冲优化与性能调优当你完成基础游戏后可能会追求更精美的效果和更流畅的性能。这里分享几个进阶技巧。6.1 实现透明贴图去背景EasyX的putimage函数支持三元光栅操作码ROP这是实现透明效果的关键。常见的方法是使用“掩码图法”需要两张图一张是原图带颜色另一张是掩码图黑白图其中需要透明的地方为黑色不透明的地方为白色。步骤先将掩码图以SRCAND方式绘制到目标位置。SRCAND是“与”操作白色部分全1会保留目标背景黑色部分全0会将目标背景对应位置置为黑色。这相当于在背景上“挖”出了一个黑色形状的洞。再将原图以SRCPAINT方式绘制到同一位置。SRCPAINT是“或”操作原图中黑色的部分值为0不会改变背景有颜色的部分会“填充”到上一步挖出的黑色洞中。// 假设 maskImg 是黑白掩码图 srcImg 是彩色原图 putimage(x, y, maskImg, SRCAND); // 第一步与操作制作掩码 putimage(x, y, srcImg, SRCPAINT); // 第二步或操作贴上颜色现在很多工具如TexturePacker可以帮你生成精灵图和对应的坐标文件但对于EasyX你可能需要自己用图像处理软件如Photoshop制作配套的掩码图。6.2 局部刷新与脏矩形优化在复杂的游戏中如果每一帧都清空整个屏幕并重绘所有元素全屏刷新当图形元素很多时会消耗大量CPU资源。一个优化策略是“脏矩形”更新只重绘屏幕上发生变化的那一小块区域。实现思路为每个会移动或改变的游戏对象如角色、子弹维护一个“上一帧的位置”和“当前帧的位置”。在渲染前计算这两个位置所覆盖的矩形区域的并集这个区域就是需要被重绘的“脏矩形”。不清屏而是用背景色填充这个“脏矩形”区域以擦除旧图像。只在这个“脏矩形”区域内或与其相交的游戏对象进行绘制。这能显著提升性能但实现起来比全屏刷新复杂需要精细管理每个对象的边界和绘制顺序。对于初期的简单游戏全屏刷新完全够用当感觉到明显卡顿时再考虑引入此优化。6.3 资源管理与内存优化对于使用大量图片、音效的游戏良好的资源管理是必须的。加载时机不要在游戏主循环中频繁加载和释放图片loadimage。应在游戏初始化时或进入某个关卡时一次性加载所有需要的资源到IMAGE对象数组中游戏中只进行putimage操作。释放资源当资源不再需要时如游戏结束、切换关卡应调用IMAGE对象的析构函数或重新赋值来释放内存。虽然小程序可能感觉不到但养成好习惯很重要。使用合适尺寸的图片不要加载一个1000x1000的图片然后缩放到50x50使用。尽量在制作资源时就调整到游戏中实际需要的大小可以节省内存和加载时间。7. 常见问题排查与调试心得在开发过程中你肯定会遇到各种奇怪的问题。这里记录一些我踩过的坑和解决方法。7.1 图形窗口闪烁或卡顿问题动画不流畅有明显闪烁。排查确认双缓冲已开启initgraph后默认开启。确保你的绘制循环结构是cleardevice()- 所有绘制 -FlushBatchDraw()。检查帧率控制是否没有使用Sleep或帧率控制逻辑导致循环过快CPU满载但实际刷新跟不上或者Sleep时间太长绘制操作过多是否在一帧内绘制了太多比如上千个微小图形尝试优化绘制逻辑或使用BeginBatchDraw()和EndBatchDraw()将多个绘制命令批量提交但FlushBatchDraw通常已足够。解决规范游戏循环结构加入精确的帧率控制如固定每秒60帧。7.2 键盘或鼠标输入无响应或响应异常问题按键没反应或者按一次触发多次。排查输入检测代码位置确保输入检测代码在游戏主循环的“处理输入”阶段且每帧只执行一次。_getch()的阻塞性在循环中直接使用_getch()它会等待输入导致游戏卡住。应配合_kbhit()使用。扩展键处理对于方向键记得处理两次_getch()。消息堆积对于鼠标使用while(MouseHit())来清空消息队列避免一帧处理多个累积的鼠标事件。解决使用标准的非阻塞输入检测模式。7.3 图像无法加载或显示黑色方块问题loadimage成功但putimage出来是黑的或者直接加载失败。排查文件路径这是最常见的原因。使用相对路径并确认程序的工作目录是否正确。可以在程序开头用_wgetcwd打印当前目录检查。文件名和编码确保文件名正确特别是大小写在Windows上可能不敏感但最好一致。如果路径包含中文确保源文件编码与项目字符集设置匹配。图像格式EasyX支持bmp,jpg,png,gif等。但某些特殊编码的jpg或png可能不支持。尝试用画图工具另存为一个标准的24位位图(.bmp)再测试。内存不足加载的图片尺寸过大。检查图片尺寸是否合理。解决将资源文件放在项目exe文件同级目录的res文件夹下使用_T(“./res/hero.png”)这样的相对路径加载。对于复杂情况可以在代码中添加加载失败的判断和错误信息输出。7.4 程序退出时崩溃或窗口关闭缓慢问题点击关闭按钮或执行到closegraph()时程序崩溃。排查资源未释放在closegraph之前是否有一些动态分配的IMAGE对象没有正确释放确保所有loadimage加载的IMAGE都走出了其作用域或被妥善管理。多线程问题如果你不慎在其它线程中调用了EasyX的绘图函数这本身是不安全的可能导致崩溃。EasyX绘图应在主线程进行。消息循环冲突如果你混用了EasyX和传统的WinMain消息循环可能需要更小心地处理窗口关闭消息。解决对于简单项目尽量将所有绘图相关操作集中在主循环中。在程序退出前确保所有IMAGE对象都已析构例如将其放入局部作用域或使用智能指针管理。调试EasyX程序一个非常实用的方法是使用输出调试信息。虽然它是图形程序但你仍然可以打开控制台窗口在VS项目属性中将“链接器-系统-子系统”改为“控制台(/SUBSYSTEM:CONSOLE)”然后使用printf或std::cout输出变量值、状态信息到控制台这对于跟踪逻辑错误、查看坐标值等非常有帮助。当调试完成后再改回“窗口(/SUBSYSTEM:WINDOWS)”即可。