ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Visual C++重现连连看:老工程编译、算法与MFC实战

Visual C++重现连连看:老工程编译、算法与MFC实战 简介一套基于MFC框架的连连看游戏完整工程使用Visual C编写面向初学C或Windows桌面应用开发的学习者适合作为课程设计或入门游戏编程的参考。压缩包共26个文件主体为C源文件与头文件涵盖主程序、文档类、视图类和主框架类另含位图、图标与资源脚本用于界面与画面素材整体大小仅183KB结构紧凑。已有139人学习浏览该实例覆盖图形界面搭建、图像显示、相同图片匹配消除、鼠标事件响应等关键环节并演示MFC文档视图架构的分工。研读源码可理解C类设计、消息映射与资源管理方式也能看到预编译头文件、项目工程文件等实际组织形式通过动手修改游戏逻辑或界面能进一步掌握从窗口创建到游戏交互实现的基础流程是一份便于上手的实践案例。1. 老游戏压缩包没人能打开问题多半出在环境而不是代码当你下载到一份名叫lianliankan.rar的压缩包解压后发现里面是一堆.cpp、.h、.rc和.dsw很容易以为这是“一个可直接运行的 Visual C 游戏”。实际上这类老连连看项目十有八九卡在环境上编译器版本不对、MFC 运行库缺失、字符集不匹配代码本身根本没坏。这篇文章按我平时接手老游戏工程的顺序讲清楚怎么用 Visual C 把这套连连看编译、跑通、改得能玩也把最容易翻车的地方列出来。适合正在做课程设计、想复现连连看、或者刚拿到别人源码却无从下手的从业者。2. 先确认 lianliankan.rar 的底细解包、工程类型与 VC 版本匹配老游戏压缩包和现在 GitHub 上 clone 下来的工程最大的区别是没有一个统一的“包管理器”来告诉你依赖什么。我拿到这类 rar 的第一步永远是解压后先看根目录而不是急着开 IDE。因为很多老工程用的是相对路径解压路径带中文、带空格都会导致资源文件加载失败这一步不做好后面找问题全是玄学。2.1 解压后先看什么识别工程文件类型解压后先不要动目录结构直接看有哪些工程文件。常见的后缀对应的工具链差别很大具体可以对照下面这张表文件扩展名常见工具链说明.dsw/.dspVisual C 6.0.dsw是工作区.dsp是工程文件.vcprojVS2002 到 VS2008老式 XML 工程.sln/.vcxprojVS2008 之后现代解决方案VS2019/2022 都在用.rc/.rc2所有 VS 版本资源脚本对话框、位图、图标、菜单判断这个连连看是不是 MFC 程序不需要读完代码直接搜特征符号就行。常见做法是用命令行工具定位CWinApp和BEGIN_MESSAGE_MAP。如果这两个符号大量出现那这基本是 MFC 工程如果只有WinMain那就是 Win32 SDK 写的普通窗口程序。# 在源码目录里快速搜 MFC 特征 find . -name *.cpp -o -name *.h | xargs grep -l CWinApp find . -name *.cpp -o -name *.h | xargs grep -l BEGIN_MESSAGE_MAP这两条命令在 Git Bash 或 WSL 里可以直接跑。如果你是纯 Windows 环境也可以用 PowerShell 的Select-String效果一样。命中的文件越多越说明工程依赖 MFC。这一步直接影响你后面要不要装 MFC 运行库、要不要改项目属性里的“使用 MFC”。2.2 VC6 工程转到 VS2019/2022 的三步路线很多老连连看源码是拿 Visual C 6.0 写的。你直接用 VS2022 打开.dsw会弹一个“安全升级”的向导点确定后它会生成.sln和.vcxproj。但升级完编译大概率报错原因不是代码错而是老 VC6 的语法习惯和高版本编译器冲突。我一般走三条路按项目情况选一是“原地升级”。用 VS2019 或 VS2022 打开.dsw接受转换向导然后进“项目属性 - 常规 - 平台工具集”改成当前 VS 对应的版本比如v142对应 VS2019v143对应 VS2022。Windows SDK 版本选本机已安装的即可。二是“保旧环境”。如果这个老项目用了大量 VC6 特有的语法或者你只是想跑通看看效果那最省心的办法是装 Visual C 6.0。它确实能跑在老 Windows 虚拟机里Win11 上偶尔能装上但调试体验很差我不太推荐。三是“重写结构”。把算法和界面分离棋盘逻辑抽成纯 C 类界面用 MFC 或 Win32 重写。对做课程设计的人来说这条路收益最大后面扩展功能和换界面都不折腾。转换后最容易遇到的一个编译错误是C2065: i : undeclared identifier。原因是 VC6 里for循环的计数器作用域是整个函数而 VS2008 之后是循环体内。解决方法也很机械把for (int i 0; ...改成int i; for (i 0; ...。这种错误一次会有几十个但都属于机械修改不要慌。2.3 Redistributable 对不上时先查缺哪个 DLL编译过了不代表能跑。老连连看 exe 最经典的报错是“缺少 MFC42.dll”或者“缺少 msvcp140.dll”。前者是 VC6 时代的 MFC 运行库后者是 VS2015-2022 的 C 运行库两者完全不是一回事。很多人直接把最新的 microsoft visual c redistributable 装一遍结果还是缺 MFC42.dll因为新版运行库不向上兼容 VC6。在动手装运行库之前我建议先看一下系统里到底装了哪些版本。可以用 PowerShell 查注册表# 列出本机已安装的 Visual C Redistributable Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like *Visual C*Redistributable* } | Select-Object DisplayName, DisplayVersion | Sort-Object DisplayName这条命令会把已安装的 2010、2013、2015-2022 运行库全部列出来。注意 x86 和 x64 是分开安装的32 位游戏在 64 位系统上也需要 x86 版运行库。如果查下来确实缺某个版本去微软官网下载对应年份的 redistributable 装上就行。如果你的工程允许也可以选择“静态链接”。在项目属性里把“MFC 的使用”改成“在静态库中使用 MFC”把“运行库”改成“多线程 /MT”这样生成的 exe 自带运行库不需要目标机器装任何 VC 运行库但 exe 体积会大不少。这种发布方式在老代码里很常见也最省心。3. 把连连看核心算法搬进 Visual C连通判断、棋盘数据与 MFC 绘制连连看这个游戏界面只是皮核心是一套“两点连通判断”算法。很多老工程写得乱就是因为把算法和画图混在一个OnPaint里面。我建议先把算法独立出来用纯 C 函数实现再用 MFC 去调它。这样即使你后面不做 MFC改成 Qt 或者控制台也能复用。3.1 连通判断的三种情况0 折、1 折与 2 折连连看的规则是两个格子图案相同且路径最多只能拐两次弯路径上除了起点和终点以外不能碰到任何棋子。按拐点数分三种情况处理最直观。0 折就是两个点在同一行或同一列中间全部为空。1 折是经过一个拐点比如先横着走再竖着走。2 折最常见路径上包含两个拐点整体呈一个“U 形”或“Z 形”。下面这段代码是我常用的判断逻辑用IsLineEmpty检查直线段用CanConnect枚举拐点// 检查 (r1,c1) 到 (r2,c2) 是否在同一条直线上且中间格子全部为空 bool IsLineEmpty(int board[ROW][COL], int r1, int c1, int r2, int c2) { if (r1 r2) { int step (c1 c2) ? 1 : -1; for (int c c1 step; c ! c2; c step) if (board[r1][c] ! 0) return false; return true; } if (c1 c2) { int step (r1 r2) ? 1 : -1; for (int r r1 step; r ! r2; r step) if (board[r][c1] ! 0) return false; return true; } return false; } // 两个点能消除的主入口 bool CanConnect(int board[ROW][COL], int r1, int c1, int r2, int c2) { if (board[r1][c1] ! board[r2][c2]) return false; if (r1 r2 c1 c2) return false; // 0 折同一直线直接通 if (IsLineEmpty(board, r1, c1, r2, c2)) return true; // 1 折拐点在 (r1,c2) 或 (r2,c1) if (board[r1][c2] 0 IsLineEmpty(board, r1, c1, r1, c2) IsLineEmpty(board, r1, c2, r2, c2)) return true; if (board[r2][c1] 0 IsLineEmpty(board, r1, c1, r2, c1) IsLineEmpty(board, r2, c1, r2, c2)) return true; // 2 折先垂直移动枚举中间行 for (int r 0; r ROW; r) { if (r r1 || r r2) continue; if (board[r][c1] 0 board[r][c2] 0 IsLineEmpty(board, r1, c1, r, c1) IsLineEmpty(board, r, c1, r, c2) IsLineEmpty(board, r, c2, r2, c2)) return true; } // 2 折先水平移动枚举中间列 for (int c 0; c COL; c) { if (c c1 || c c2) continue; if (board[r1][c] 0 board[r2][c] 0 IsLineEmpty(board, r1, c1, r1, c) IsLineEmpty(board, r1, c, r2, c) IsLineEmpty(board, r2, c, r2, c2)) return true; } return false; }这里有几个参数要注意。ROW和COL是包含外围空边界的棋盘尺寸所以0会被当成“空格”1到12表示不同的图案类型。IsLineEmpty的判断是左闭右开也就是不检查起点和终点本身只检查中间所有格子。这个约定必须全局一致否则 0 折路径会误判。2 折枚举中间行和中间列时如果board[r][c1]或board[r][c2]是角落边界它们本来就是 0所以没问题。如果是两个边界上的点也无所谓因为边界是空地。这套代码不管是从中间消除还是从外圈消除都通用。3.2 棋盘数据结构用二维数组加一圈空边界连连看的棋盘本质是一张二维表。常见做法是在有效棋区外围再包一圈0目的是简化边界判断。如果不加这一圈当两个棋子都在最外边时拐点坐标会落到-1或ROW这些越界位置判断逻辑会写出一堆if。加一圈空边之后所有路径判断都在数组范围内代码干净很多。我习惯用固定大小的二维数组而不是std::vector因为老版 Visual C 6.0 对 STL 的支持不好而且二维数组在调试时一眼能看到完整布局。const int ROW 10; // 含上下空边 const int COL 14; // 含左右空边 int board[ROW][COL]; void InitBoard() { // 先用 0 铺满外圈自动就是空边 for (int r 0; r ROW; r) for (int c 0; c COL; c) board[r][c] 0; int innerRows ROW - 2; // 内部有效行数8 int innerCols COL - 2; // 内部有效列数12 int total innerRows * innerCols; // 必须是偶数否则无法成对 if (total % 2 ! 0) return; int pairs total / 2; int *values new int[pairs * 2]; // 图案类型从 1 到 12轮流分配保证每种数量成对 for (int i 0; i pairs; i) { int type (i % 12) 1; values[i * 2] type; values[i * 2 1] type; } // 用 rand 洗牌简单直接 // 调用 InitBoard 前记得 srand((unsigned)time(NULL)) for (int i pairs * 2 - 1; i 0; i--) { int j rand() % (i 1); int tmp values[i]; values[i] values[j]; values[j] tmp; } int idx 0; for (int r 1; r ROW - 2; r) for (int c 1; c COL - 2; c) board[r][c] values[idx]; delete[] values; }这段代码只保证图案数量成对不保证“一定有解”。真实游戏里如果出现死局需要做一次重排。我后面第 6 章会讲自动提示和洗牌的做法。在这个初始化里rand()比较弱但足够让课程设计跑起来。如果你想更顺滑可以用std::shuffle前提是你用的是 VS2010 以上的编译器。3.3 用 MFC 把棋盘画出来从 CBitmap 到 BitBlt棋盘画法有很多种最简单的是在OnPaint里用CClientDC拿到设备上下文然后对每个有棋子的格子调用BitBlt贴位图。位图资源可以在资源编辑器里导入ID 分别取IDB_TILE1到IDB_TILE12对应 12 种图案。我这里给一个示例片段重点是贴图坐标的计算。void CGameDlg::DrawBoard(CDC* pDC) { CRect rc; GetClientRect(rc); int marginLeft 20; int marginTop 20; int cell min((rc.Width() - 40) / COL, (rc.Height() - 40) / ROW); // 每张棋子位图建议做成 32x32用 BitBlt 拉伸会有锯齿 for (int r 1; r ROW - 2; r) { for (int c 1; c COL - 2; c) { if (board[r][c] 0) continue; CBitmap bmp; // 实际使用时要为每种图案准备独立资源 // 这里按图案类型加载不同位图 bmp.LoadBitmap(IDB_TILE1 board[r][c] - 1); CDC memDC; memDC.CreateCompatibleDC(pDC); CBitmap* pOld memDC.SelectObject(bmp); int x marginLeft (c - 1) * cell; int y marginTop (r - 1) * cell; pDC-BitBlt(x, y, cell, cell, memDC, 0, 0, SRCCOPY); memDC.SelectObject(pOld); bmp.DeleteObject(); } } }这段代码里最关键的是坐标换算棋盘下标c从 1 开始而绘制坐标从 0 开始所以用(c - 1) * cell。如果直接写c * cell第一列棋子会顶到客户区左边而且整个棋盘会向右上偏移。这个错位很隐蔽点击判断和绘制必须用同一套公式否则永远对不上。BitBlt的第四个和第五个参数cell, cell是目标矩形宽高。位图资源如果本身是 32x32而cell是 50直接拉伸会糊。我一般把位图做成和cell一样大的资源或者用StretchBlt做缩放但缩放会带来性能损耗。老机器上贴图多的时候StretchBlt会明显掉帧所以能用BitBlt就不要用StretchBlt。4. 让游戏真正能玩鼠标消息、消除动画与游戏状态管理算法和绘制都有了程序还只是一张静态图。要变成能玩的游戏得把鼠标事件、选中状态、消除动画和胜负判定串起来。这一层最容易写乱因为 MFC 的消息映射是事件驱动的状态变量多一个少一个都会导致逻辑错位。4.1 鼠标点击判定与选中状态MFC 对话框程序里只需要在消息映射里加一行ON_WM_LBUTTONDOWN()再实现OnLButtonDown成员函数。点击和绘制的坐标换算必须一致否则会出现“明明点到了这个格子消除的却是另一个格子”的怪问题。点击后要做的事分三步第一把像素坐标换算成棋盘坐标第二判断是不是有效棋子第三处理选中状态。第一次点击记录起点第二次点击做消除判断然后清空选中状态。void CGameDlg::OnLButtonDown(UINT nFlags, CPoint point) { int col (point.x - m_marginLeft) / m_cellSize 1; int row (point.y - m_marginTop) / m_cellSize 1; if (row 1 || row ROW - 2 || col 1 || col COL - 2) return; if (board[row][col] 0) return; if (m_firstRow 0) { // 第一次选中 m_firstRow row; m_firstCol col; Invalidate(); // 刷新高亮 } else { // 第二次选中判断能否消除 if (CanConnect(board, m_firstRow, m_firstCol, row, col)) { board[m_firstRow][m_firstCol] 0; board[row][col] 0; m_score 10; } m_firstRow -1; m_firstCol -1; Invalidate(); } CDialogEx::OnLButtonDown(nFlags, point); }这里的m_firstRow和m_firstCol初始值必须设成-1不能设成0因为棋盘合法下标从 1 开始。如果你把0当“未选中”那么选第一行第一列的棋子时会出现状态错乱这是老代码里很典型的边界坑。我还会在选中第一个棋子时记录m_selectedRow用于绘制高亮边框。高亮不一定非要用位图最简单的做法是在OnPaint里画一个FrameRect颜色用亮黄色效果已经很直接。4.2 消除动画不要用 Sleep用定时器很多新手第一次写消除会这样判断连通后画一条线然后Sleep(300)再刷新。这在单线程 MFC 里是灾难因为Sleep会把整个界面消息循环卡住窗口会变成“无响应”。常见做法是把消除动作拆成两步先保存要消除的两个点和路径开启一个定时器定时器触发后再真正把棋盘置空并刷新。这样玩家能看到短暂的连线反馈界面也不会卡。void CGameDlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent TIMER_ANIMATE) { KillTimer(TIMER_ANIMATE); // 定时器到期正式消除 board[m_animR1][m_animC1] 0; board[m_animR2][m_animC2] 0; m_animR1 m_animR2 m_animC1 m_animC2 -1; Invalidate(); } CDialogEx::OnTimer(nIDEvent); }在主线程里开启定时器不是高精度方案但连连看这种慢速游戏完全够用。如果要做漂亮的消除动画比如棋子渐隐、飞入飞出那就要开一个后台线程或者用 GDI 的动画定时器复杂度一下子就上来了。做课程设计的话用定时器已经能拿一个不错的效果。4.3 关卡、得分与重排的状态机状态机不写复杂但一定要有。最简单的游戏状态可以这样枚举准备中、已选中一个、动画播放中、游戏结束。很多 bug 都出在“动画播放中还能点击下一个棋子”这种状态混乱上。enum GameState { STATE_READY, STATE_SELECTED, STATE_ANIMATING, STATE_OVER };状态切换逻辑可以集中写在SetState里不要散落在各个函数中。比如动画期间OnLButtonDown直接 return当第二次点击消除失败时状态回到STATE_READY当棋盘上剩余棋子数为 0 时进入STATE_OVER并弹出胜利对话框。得分可以放在一个独立的CScore类里虽然简单但方便后面加连续消除加分、时间奖励等规则。我这里特别强调一点不要在一个按钮的OnBnClicked里写完整局流程。老代码最大的问题就是把洗牌、判断、刷新、计分全部塞进一个函数后来想加“提示”功能时完全没法下手。把状态机分出来后面所有功能都会清晰很多。5. Visual C 连连看运行排查5 个踩过的坑与解决办法下面这 5 个坑是我在跑老 Visual C 游戏项目时反复遇到的。每一条都按“现象 - 原因 - 解决”写方便你对着排查。5.1 Debug 能跑、Release 就崩现象在 Visual Studio 里按 F5 用 Debug 编译游戏能玩改成 Release 再跑进游戏点几下就崩溃或者一启动直接闪退。原因最常见的因素是变量未初始化。Debug 版编译器会把未初始化的内存自动填成特殊值看起来是“默认的 0”但 Release 版不会做这种处理于是随机值导致数组越界或空指针解引用。另一个常见因素是 MFC 的ASSERT宏在 Release 下被移除原本靠断言挡住的错误直接暴露出来。解决把所有成员变量放在构造函数里手动初始化尤其是m_firstRow、m_firstCol、m_animR1这类状态变量。棋盘数组也要memset或循环置 0不要依赖全局变量默认值。如果你代码里用了rand()且没有srand在 Release 里表现也会不一样因为每次运行的内存布局不同。5.2 点击棋盘没反应现象窗口能正常显示棋子也能看到但鼠标点上去没有高亮也没有消除。原因第一消息映射没挂上第二坐标换算错位。MFC 里如果类声明了OnLButtonDown但没有在BEGIN_MESSAGE_MAP里加ON_WM_LBUTTONDOWN()这个函数永远不会被调用。坐标错位则是绘制和点击用了不同的格子尺寸或边距。解决先在OnLButtonDown第一行用OutputDebugString输出点击位置确认事件有没有进函数。如果事件进了再输出换算后的行列值。你可以临时把点击坐标填充到棋盘上画出来这样立刻看出偏移量。我习惯把cellSize、marginLeft、marginTop做成成员变量绘制和点击共用同一套值不要一个写在OnPaint里一个写在消息函数里。5.3 中文乱码现象对话框按钮、菜单、游戏标题里的中文全部变成乱码但英文正常。老源码在旧电脑上没问题换到新电脑就乱。原因VC6 时代源文件和资源文件默认是 ANSI 编码Windows 中文系统用 GBK。现代 Visual Studio 默认会把.cpp当 UTF-8如果你把源文件另存为 UTF-8 无 BOM里面的中文字符串就解释错了。资源文件.rc里的中文字符串也可能因为系统代码页不一样而显示异常。解决首先把.cpp和.h文件另存为“UTF-8 with BOM”然后在项目属性 - 配置属性 - C/C - 命令行里加上/utf-8。如果还乱把.rc文件也用 UTF-8 with BOM 保存。注意老工程如果坚持用 ANSI在新系统上不一定稳定最省心的方式还是把源码统一转成带 BOM 的 UTF-8。转换后要重新编译确保没有引入编译错误。5.4 缺 MFC42.dll、msvcp140.dll现象双击生成的 exe 弹出“找不到 MFC42.dll”或者“找不到 msvcp140.dll”游戏起不来。原因这是运行库缺失不是代码问题。MFC42.dll 是 Visual C 6.0 时代的东西msvcp140.dll 是 VS2015 到 VS2022 时代的 C 运行库。两个版本跨度很大不能用新版运行库替代老版。另外32 位程序在 64 位 Windows 上运行时需要安装 x86 版本的运行库很多人只装了 x64于是 dll 还是找不到。解决先去控制面板确认已安装的 microsoft visual c redistributable 版本再补装对应版本。如果不想让用户装一堆运行库项目属性里把 MFC 改成“在静态库中使用 MFC”运行库改成“多线程 /MT”这样 exe 体积会大但换来了“双击就能跑”。我发布课程设计作品时一般给两个版本一个静态链接版一个动态链接版动态版附上运行库安装包。5.5 窗口花屏或尺寸不对现象棋盘没有完整铺满窗口或者拖动窗口后图形变成一团黑块、残留痕迹。原因多半是OnPaint只处理了部分绘制区域或者直接把绘制逻辑写在OnEraseBkgnd里。频繁Invalidate会触发背景擦除再重绘造成闪烁和残影。另一个常见原因是窗口客户区大小和棋盘大小不匹配导致最后一列棋子画到窗口外面。解决不要依赖默认背景擦除干脆用双缓冲重写OnPaint。先画到一个内存位图再一次BitBlt到窗口。这样既解决花屏也解决闪烁。窗口尺寸方面可以在对话框初始化时调用MoveWindow或者调整InitDialog里的布局参数保证cell (客户区尺寸 - margin) / 棋盘列数能整除。6. 老代码改造双缓冲重绘、自动提示与发布最小依赖把老连连看跑通只是第一步真正让人愿意把这个工程交上去或者放到简历里的是几个看起来不大但体验提升明显的改造点。我挑三个性价比最高的讲双缓冲、自动提示、发布依赖清理。6.1 双缓冲重绘解决闪烁花屏和闪烁的根源是多次擦除再绘制。双缓冲的做法很简单在内存里建一块和客户区一样大的位图把棋盘全部画到这块内存位图上最后一次画到窗口。void CGameDlg::OnPaint() { CPaintDC dc(this); CRect rc; GetClientRect(rc); CDC memDC; memDC.CreateCompatibleDC(dc); CBitmap memBmp; memBmp.CreateCompatibleBitmap(dc, rc.Width(), rc.Height()); CBitmap* pOld memDC.SelectObject(memBmp); // 背景 memDC.FillSolidRect(rc, RGB(240, 240, 240)); // 画棋盘 DrawBoard(memDC); // 一次到位 dc.BitBlt(0, 0, rc.Width(), rc.Height(), memDC, 0, 0, SRCCOPY); memDC.SelectObject(pOld); memBmp.DeleteObject(); }注意DrawBoard里不要再自己创建CClientDC否则会画到窗口而不是内存位图。我一般把DrawBoard设计成接收一个CDC*参数这样不管是窗口 DC 还是内存 DC 都能用这也是老 MFC 工程重构时最值得做的一个改动。6.2 自动提示与洗牌的实现思路没有提示的连连看卡死时只能靠重开。加提示的逻辑不复杂遍历棋盘所有非空点找任意一对能连接的点把它们的坐标保存下来然后在高亮变量里标记。点击“提示”按钮时直接高亮这两个点。洗牌就是重新打乱棋盘上剩余的非空格子但要保证打乱后依然成对。实现时可以先把所有非 0 值取出来用rand乱序后再填回去。洗牌后要重新检查有没有可消除的对如果没有就再洗一次直到有解。用 do-while 包一层最多洗几十次就能出结果。6.3 发布时别只发一个 exe我见过太多人交付课程设计时只扔一个 exe结果在别人电脑上缺这个缺那个。哪怕你静态链接了 MFC资源里的位图、图标也可能因为路径问题加载失败。老工程用相对路径加载图片时exe 必须和资源目录保持压缩包里的原始结构不要只拷贝 exe。正确做法是打包成 zip保留原目录结构并在压缩包内附一份README.txt写清楚操作系统要求、是否需要安装 microsoft visual c redistributable、以及 exe 的运行路径。如果你是动态编译直接把 VC 运行库安装包也放进去避免对方装错版本。这个习惯能省掉大半“在我电脑上能跑”的尴尬。我现在每拿到一个 lianliankan.rar 这类老工程都会先按“算法、绘制、状态、发布”四步重新拆一遍而不是急着编译跑。跑通之后再做双缓冲和提示功能整个工程的可读性和稳定性会明显提升。希望帮到你。本文还有配套的精品资源点击获取
返回列表