• 画面消失,但功能还在正常实现,窗口绘制出了问题


    关注 码龄 粉丝数 原力等级 -- 被采纳 被点赞 采纳率 hello_world34 2024-05-31 21:29 采纳率: 0% 浏览 0 首页/ 微软技术 / 画面消失,但功能还在正常实现,窗口绘制出了问题 windowsc++ Windows编程做一个小游戏,但是运行到一半,画面突然消失。但是!但是游戏还在运行,键盘鼠标还能操控,只是画面看不见了(加了一个控制台打印输出) 跑着跑着就变这样了,在左边的控制台可以看到程序还在跑,并没有卡,猜测应该是窗口绘制出了问题,但也不知道为什么,具体哪里的问题 下面是跟绘制有关的代码 //游戏界面 void Mypaint(HDC hdc, RECT rect) { if (mdc0 == NULL){ mdc0 = CreateCompatibleDC(hdc); fullmap0 = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); oldmap0 = (HBITMAP)SelectObject(mdc0, fullmap0); } hb = CreateSolidBrush(RGB(32, 33, 37)); FillRect(mdc0, &rect, hb); //地图 gridmap.draw_map(mdc0, gridmap.l, gridmap.l.size()); ///////////////////////////////////////////////////////////////////////////////////// //视线 draw_view(mdc0, gridmap.l, gridmap.l.size()); //人物 draw_direction(mdc0); if (a.isatk) a.attack(mdc0); //最后贴mdc到hdc上 BitBlt(hdc, 0, 0, rect.right, rect.bottom, mdc0, 0, 0, SRCCOPY); _cprintf("g"); //记录时间 tPre = GetTickCount64(); } //创造模式绘制 void Mypaint1(HDC hdc, RECT rect) { if (mdc == NULL) { mdc = CreateCompatibleDC(hdc); fullmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); oldmap = (HBITMAP)SelectObject(mdc, fullmap); } //背景 hb = CreateSolidBrush(RGB(32, 33, 37)); FillRect(mdc, &rect, hb); //可动区域 hb = CreateSolidBrush(RGB(56, 58, 70)); SelectObject(mdc, hb); Rectangle(mdc, drawRect.left, drawRect.top, drawRect.right, drawRect.bottom); //按钮区域 hb = CreateSolidBrush(RGB(255, 255, 255)); SelectObject(mdc, hb); Rectangle(mdc, revokeRect.left, revokeRect.top, revokeRect.right, revokeRect.bottom); Rectangle(mdc, saveRect.left, saveRect.top, saveRect.right, saveRect.bottom); DrawText(mdc, "撤销", strlen("撤销"), &revokeRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); DrawText(mdc, "保存", strlen("保存"), &saveRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); //绘制 hb = CreateSolidBrush(RGB(32, 33, 37)); SelectObject(mdc, hb); hp = CreatePen(PS_SOLID, 3, RGB(78, 87, 100)); SelectObject(mdc, hp); for (int i = 0; i < l.size(); i++) { if (l[i].isline) {//线段 MoveToEx(mdc, l[i].st.x, l[i].st.y, NULL); LineTo(mdc, l[i].ed.x, l[i].ed.y); } else//矩形块 Rectangle(mdc, l[i].st.x, l[i].st.y, l[i].ed.x, l[i].ed.y); } if (draw_l) { MoveToEx(mdc, l_start.x, l_start.y, NULL); LineTo(mdc, l_end.x, l_end.y); } if (draw_r) { Rectangle(mdc, l_start.x, l_start.y, l_end.x, l_end.y); } //最后贴mdc到hdc上 BitBlt(hdc, 0, 0, rect.right, rect.bottom, mdc, 0, 0, SRCCOPY); _cprintf("c"); //记录时间 tPre = GetTickCount64(); } //主界面绘制 void Mypaint2(HDC hdc, RECT rect) { if (mdc2 == NULL) { mdc2 = CreateCompatibleDC(hdc); fullmap2 = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); oldmap2 = (HBITMAP)SelectObject(mdc2, fullmap2); } //背景 hb = CreateSolidBrush(RGB(32, 33, 37)); FillRect(mdc2, &rect, hb); //按钮区域 hb = CreateSolidBrush(RGB(255, 255, 255)); SelectObject(mdc2, hb); Rectangle(mdc2, titleRect.left, titleRect.top, titleRect.right, titleRect.bottom); Rectangle(mdc2, gameRect.left, gameRect.top, gameRect.right, gameRect.bottom); Rectangle(mdc2, createRect.left, createRect.top, createRect.right, createRect.bottom); Rectangle(mdc2, quitRect.left, quitRect.top, quitRect.right, quitRect.bottom); DrawText(mdc2, "标题", strlen("标题"), &titleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); DrawText(mdc2, "进入游戏", strlen("进入游戏"), &gameRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); DrawText(mdc2, "创造地图", strlen("创造地图"), &createRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); DrawText(mdc2, "退出游戏", strlen("退出游戏"), &quitRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); BitBlt(hdc, 0, 0, rect.right, rect.bottom, mdc2, 0, 0, SRCCOPY); _cprintf("m"); //记录时间 tPre = GetTickCount64(); } 然后是窗口处理函数 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lparam) { PAINTSTRUCT ps; RECT rect; GetClientRect(hwnd, &rect); bool flag; POINT1 oldp; POINT1 exp; xNow = LOWORD(lparam); yNow = HIWORD(lparam); switch (message) { case WM_LBUTTONDOWN: if (isgame) { a.isatk = true; InvalidateRect(hwnd, NULL, false); } else if (isdraw) { if (PtInRect(&drawRect, { xNow,yNow })) {//在可动区域,进行画图 draw_l = true; l_start = { xNow,yNow }; } } break; case WM_LBUTTONUP: if (isgame) {//在游戏界面 a.isatk = false; } else if (ismain) {//在主界面 if (PtInRect(&gameRect, { xNow,yNow })) { ismain = false; isgame = true; isdraw = false; ///////////////////////////////////////////////////////////////// //进入游戏,加载地图 ///////////////////////////////////////////////////////////////// } else if (PtInRect(&createRect, { xNow,yNow })) { isdraw = true; ismain = false; isgame = false; draw_l = false; } else if (PtInRect(&quitRect, { xNow,yNow })) { PostMessage(hwnd, WM_QUIT, 0, 0); } InvalidateRect(hwnd, NULL, false); } else if (isdraw) {//在创造界面 if (PtInRect(&drawRect, { xNow,yNow })) { draw_l = false; l.push_back({ l_start,l_end,true }); } else if (PtInRect(&revokeRect, { xNow,yNow })) {//在撤销区域,删除元素 if(!l.empty()) l.pop_back();//删除元素 InvalidateRect(hwnd, NULL, false); } else if (PtInRect(&saveRect, { xNow,yNow })) {//在保存(区域),进入游戏 isdraw = false; isgame = true; ismain = false; ///////////////////////////////////////////////////////////////// //保存到文件 gridmap.setMap(l); _cprintf("\n-------------------\n"); for (int i = 0; i < gridmap.l.size(); i++) { _cprintf("(%d,%d)->(%d,%d)\n", gridmap.l[i].st.x, gridmap.l[i].st.y, gridmap.l[i].ed.x, gridmap.l[i].ed.y); } _cprintf("\n-------------------\n"); ///////////////////////////////////////////////////////////////// InvalidateRect(hwnd, NULL, false); } } break; case WM_RBUTTONDOWN: if (isdraw) { if (PtInRect(&drawRect, { xNow,yNow })) {//在可动区域,进行画图 draw_r = true; l_start = { xNow,yNow }; } } InvalidateRect(hwnd, NULL, false); break; case WM_RBUTTONUP: if (isdraw) {//在创造界面 if (PtInRect(&drawRect, { xNow,yNow })) { draw_r = false; l.push_back({ l_start,l_end,false }); } } break; case WM_MOUSEMOVE: l_end = { xNow,yNow }; if (isgame) { //鼠标代表人物朝向 //获取鼠标和人物连线和水平向右的角度 //这就是人物朝向 face = -atan2(yNow - psrc.y, xNow - psrc.x);/////////返回弧度制 face = face * 180.0 / 3.14159; } InvalidateRect(hwnd, NULL, false); UpdateWindow(hwnd); break; case WM_KEYDOWN://人物移动时,更新人物(定点)坐标 if(isgame){ oldp = psrc; change_x = 0; change_y = 0; if (GetAsyncKeyState(VK_ESCAPE)) {//按下esc键,回到主界面 isgame = false; ismain = true; InvalidateRect(hwnd, NULL, false); } else if (GetAsyncKeyState(VK_RIGHT) && GetAsyncKeyState(VK_UP)) { change_x = -1; change_y = 1; } else if (GetAsyncKeyState(VK_RIGHT) && GetAsyncKeyState(VK_DOWN)) { change_x = -1; change_y = -1; } else if (GetAsyncKeyState(VK_LEFT) && GetAsyncKeyState(VK_UP)) { change_x = 1; change_y = 1; } else if (GetAsyncKeyState(VK_LEFT) && GetAsyncKeyState(VK_DOWN)) { change_x = 1; change_y = -1; } //四向 else if (GetAsyncKeyState(VK_UP)) { change_x = 0; change_y = 1; } else if (GetAsyncKeyState(VK_DOWN)) { change_x = 0; change_y = -1; } else if (GetAsyncKeyState(VK_LEFT)) { change_x = 1; change_y = 0; } else if (GetAsyncKeyState(VK_RIGHT)) { change_x = -1; change_y = 0; } //预期点 exp = psrc; exp.x -= change_x * step_distance; exp.y -= change_y * step_distance; //碰撞检测 flag = false; for (int i = 0; i < gridmap.l.size(); i++) if (judge_cross(oldp, exp, gridmap.l[i])) { //有碰撞,更新人物在交点上 flag = true; exp = cal_p(oldp, exp, gridmap.l[i].st, gridmap.l[i].ed); } if (!flag) psrc = exp; //最终更新 a.updatePos(psrc); InvalidateRect(hwnd, NULL, false); } break; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); //绘制 if (ismain) Mypaint2(hdc, rect); else if (isgame) Mypaint(hdc, rect); else if (isdraw) Mypaint1(hdc, rect); EndPaint(hwnd, &ps); break; case WM_DESTROY: FreeConsole(); l.clear(); DeleteObject(mdc0); DeleteObject(mdc); DeleteObject(mdc2); ReleaseDC(hwnd, hdc); DeleteObject(hp); DeleteObject(hb); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lparam); } 展开全部 收起 写回答 好问题 0 提建议 追加酬金 关注问题 微信扫一扫 点击复制链接 分享 邀请回答 编辑 收藏 删除 结题 收藏 举报 追加酬金 (90%的用户在追加酬金后获得了解决方案) 当前问题酬金 ¥ 0 (可追加 ¥500) ¥ 15¥ 20¥ 50¥ 100¥ 200 支付方式 扫码支付 二维码出错 点击刷新 支付金额 15 元 提供问题酬金的用户不参与问题酬金结算和分配 支付即为同意 《付费问题酬金结算规则》 结题 再想想 删除 再等等

    3条回答 默认 最新

    • Kwan的解忧杂货铺 Java领域优质创作者 2024-05-31 21:30
      关注

      晚上好🌙🌙🌙
      本答案参考ChatGPT-3.5

      问题分析: 画面消失可能与以下因素有关:

      1. HDC、HBITMAP、HDC等资源管理不当
      2. 重绘区域超出屏幕范围或错误地绘制
      3. 错误的位图拷贝(如SRCCOPY)
      4. 更新窗口时没有正确调用PAINT消息

      解决方案:

      1. 检查和释放资源: 确保在不再需要时正确释放创建的所有图形资源(HDC、HBITMAP、HBRUSH等)。在MypaintMypaint1Mypaint2函数中,添加资源释放代码。
      // 在合适的位置添加资源释放代码
      ...
      // 清除资源
      if (hb) {
          DeleteObject(hb);
      }
      if (hp) {
          DeleteObject(hp);
      }
      if (oldmap) {
          SelectObject(mdc, oldmap);
          DeleteObject(fullmap);
      }
      if (oldmap0) {
          SelectObject(mdc0, oldmap0);
          DeleteObject(fullmap0);
      }
      ...
      
      1. 调整绘制区域: 确保绘制的矩形在窗口范围内。例如,在GetClientRect(hwnd, &rect);之后,可以添加一个条件判断:
      if (!IsRectEmpty(&rect)) {
          // 绘制代码
          ...
      }
      
      1. 更新位图拷贝: 使用BitBlt时,确认源和目标矩形的大小匹配。将SRCCOPY更改为SRCCOPY,或者确保在位图拷贝前正确设置目标矩形:
      BitBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, mdc, 0, 0, SRCCOPY);
      
      1. 处理PAINT消息:WndProc中正确处理WM_PAINT消息,确保当窗口需要重新绘制时,调用BeginPaintEndPaint
      case WM_PAINT:
      {
          BeginPaint(hwnd, &ps);
          // 调用你的绘画函数
          Mypaint(hwnd, ps.rcPaint);
          EndPaint(hwnd, &ps);
          break;
      }
      
      1. 检查内存泄漏: 使用调试工具(如Visual Studio的内存诊断工具)检查是否有内存泄漏,这可能导致资源耗尽,导致绘制失败。

      如果以上步骤都无法解决问题,可能需要进一步分析代码逻辑,查找潜在的bug。

      展开全部

      评论 编辑记录
    • 阿里嘎多学长 2024-05-31 21:30
      关注

      以下内容由CHATGPT及阿里嘎多学长共同生成、有用望采纳:


      根据你提供的信息和代码,画面消失但功能正常的问题可能与以下几个方面有关:

      1. 双缓冲问题:如果画面闪烁或消失,可能是由于没有正确实现双缓冲。双缓冲是一种技术,用于减少或消除屏幕撕裂和闪烁现象。在你的代码中,你创建了兼容的DC(Device Context)和位图,但没有明确实现双缓冲的代码。

      2. 资源释放问题:如果资源(如位图、画笔等)没有正确释放,可能会导致资源泄漏,从而影响窗口的正常绘制。在你的代码中,DeleteObject 应该在 WM_DESTROY 消息处理中被调用,以确保所有资源都被正确释放。

      3. 绘图代码逻辑:检查你的绘图逻辑,确保在绘制过程中没有覆盖或错误地清除了画面上的内容。

      4. 窗口重绘问题InvalidateRect 用于指示指定的矩形区域需要重绘。如果调用了 InvalidateRect 但没有正确处理 WM_PAINT 消息,或者 BeginPaintEndPaint 没有正确配对使用,可能会导致绘制问题。

      5. 线程问题:如果你的游戏逻辑和绘制在不同的线程中运行,需要确保线程间的同步和通信正确无误,否则可能会导致画面更新不及时或不一致。

      为了解决这个问题,你可以尝试以下步骤:

      • 实现双缓冲:确保你的绘图逻辑使用了双缓冲技术,这样可以减少画面闪烁和消失的问题。
      • 检查资源释放:确保所有创建的资源(如位图、画笔等)在不再需要时被正确释放。
      • 审查绘制逻辑:仔细检查你的绘制逻辑,确保没有逻辑错误导致画面被错误地清除或覆盖。
      • 正确处理 WM_PAINT 消息:确保 WM_PAINT 消息被正确处理,BeginPaintEndPaint 正确配对使用。
      • 线程同步:如果使用了多线程,确保线程间的同步机制正确,避免竞态条件。

      如果你需要具体的代码实现,我可以提供一个简单的双缓冲实现示例。但请注意,这只是一个示例,你可能需要根据自己的具体需求进行调整。

      // 在 WM_CREATE 消息中初始化双缓冲
      case WM_CREATE:
      {
          PIXELFORMATDESCRIPTOR pfd;
          int iPixelFormatIndex;
      
          // 获取当前窗口的设备上下文
          HDC hdc = GetDC(hwnd);
          // 零出PIXELFORMATDESCRIPTOR结构体
          ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
          // 设置结构体参数
          pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
          pfd.nVersion = 1;
          pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_WGL | PFD_DOUBLEBUFFER;
          pfd.iPixelType = PFD_TYPE_RGBA;
          pfd.cColorBits = 32;
          pfd.iLayerType = PFD_MAIN_PLANE;
      
          // 设置像素格式
          iPixelFormatIndex = ChoosePixelFormat(hdc, &pfd);
          SetPixelFormat(hdc, iPixelFormatIndex, &pfd);
      
          // 创建渲染上下文
          hglrc = wglCreateContext(hdc);
          wglMakeCurrent(hdc, hglrc);
      }
      break;
      
      // 在 WM_PAINT 消息中进行双缓冲绘图
      case WM_PAINT:
      {
          PAINTSTRUCT ps;
          HDC hdc = BeginPaint(hwnd, &ps);
          HDC hdcMem = CreateCompatibleDC(hdc);
          HBITMAP hbmMem = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
          HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMem);
      
          // 在内存DC上进行绘图
          // ... 你的绘图代码 ...
      
          // 将内存DC的内容复制到窗口DC
          BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
      
          // 清理资源
          SelectObject(hdcMem, hbmOld);
          DeleteObject(hbmMem);
          DeleteDC(hdcMem);
      
          EndPaint(hwnd, &ps);
      }
      break;
      
      // 在 WM_DESTROY 消息中清理资源
      case WM_DESTROY:
      {
          wglMakeCurrent(NULL, NULL);
          wglDeleteContext(hglrc);
          ReleaseDC(hwnd, GetDC(hwnd));
          PostQuitMessage(0);
      }
      break;
      

      请注意,这个示例使用了OpenGL的上下文,如果你的项目中没有使用OpenGL,你可能需要调整这部分代码以适应你的项目。

      至于参考资料,你可以查看微软的官方文档,特别是关于双缓冲窗口绘制的部分。这些文档提供了详细的信息和示例,可以帮助你更好地理解和实现窗口绘制。

      展开全部

      评论 编辑记录
    • 专家-赤兔[在线] 全栈领域优质创作者 2024-05-31 21:30
      关注

      引自免费微信小程序:皆我百晓生

      根据您提供的代码和描述,问题可能出在窗口重绘部分。当游戏界面中的画面消失后,控制台依然可以接收到输入并执行相应的操作,这表明程序逻辑仍在继续运行,只是窗口上的图形渲染出现了问题。

      首先,检查一下几个关键点:

      1. 窗口是否正确初始化?确保你在创建窗口时指定了正确的窗口类。
      2. 你是否有正确设置窗口过程(WndProc)?
      3. WM_CREATE消息处理中,你是否创建了必要的设备上下文(Device Context, DC)和位图(Bitmap)?

      从代码上看,你在WM_PAINT处理函数中为不同状态的游戏、创造模式和主界面分别创建了不同的DC和位图。然而,如果窗口大小发生变化,这些资源可能需要重新创建。因此,你需要在WM_SIZE消息处理中添加相应的清理和创建操作。

      以下是针对WM_SIZE消息处理的建议:

      LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
          ...
          case WM_SIZE:
              // 清理旧的DC和位图
              if (mdc0) {
                  DeleteObject(mdc0);
                  DeleteObject(fullmap0);
                  mdc0 = NULL;
                  fullmap0 = NULL;
              }
              if (mdc) {
                  DeleteObject(mdc);
                  DeleteObject(fullmap);
                  mdc = NULL;
                  fullmap = NULL;
              }
              if (mdc2) {
                  DeleteObject(mdc2);
                  DeleteObject(fullmap2);
                  mdc2 = NULL;
                  fullmap2 = NULL;
              }
      
              // 依据新的窗口大小重新创建DC和位图
              PAINTSTRUCT ps;
              HDC hdc = BeginPaint(hwnd, &ps);
              RECT rect;
              GetClientRect(hwnd, &rect);
              
              // 创建游戏界面的DC和位图
              if (ismain || isgame)
                  Mypaint(hdc, rect);
              // 创建创造模式的DC和位图
              else if (isdraw)
                  Mypaint1(hdc, rect);
              // 创建主界面的DC和位图
              else
                  Mypaint2(hdc, rect);
      
              EndPaint(hwnd, &ps);
              break;
          ...
      }
      

      这个修改将确保在窗口大小改变时,相关的DC和位图会被正确地清理和重建。这样应该能解决画面消失的问题。不过,为了完全确定问题所在,你也可以考虑在代码的关键位置加入一些调试信息,比如在WM_SIZEWM_PAINT等消息处理函数开始时打印一些日志,以便更好地了解窗口和DC的状态变化。

      另外,考虑到代码的复杂性,建议对内存管理进行优化,确保每次创建的新对象都被正确释放,以防止内存泄漏。例如,在窗口关闭时,确保所有创建的对象都被释放。

      如果你仍然遇到问题,可以尝试使用调试工具(如Visual Studio的调试器)来逐步执行代码,查看是否有未捕获的异常或错误。同时,确保所有的GDI对象(如DC、位图、刷子和笔)在不再需要时被正确地释放。

      展开全部

      评论 编辑记录
    编辑
    预览

    报告相同问题?

  • 相关阅读:
    使用devcpp遇到的常见错误解决方法
    Kubernetes Dashboard 部署应用以及访问
    ML之PDP:基于titanic泰坦尼克是否获救二分类预测数据集利用PDP部分依赖图对RF随机森林和LightGBM模型实现可解释性案例
    Vulkan 创建 PhysicalDevice 和 Surface 的包装思路
    暑期留校——区间DP-板子题石子合并
    Three.js后期处理简明教程
    信号处理之巴特沃斯滤波器的理解----2022/11/30
    记一次springboot项目结合arthas排查ClassNotFoundException问题
    APR学习失败问题定位排查
    J2EE基础-自定义MVC(下)
  • 原文地址:https://ask.csdn.net/questions/8112326