“鼠标拖动游戏窗口,消息循环会卡住,导致游戏不能更新不能渲染”,解决办法在这里

编程入门 行业动态 更新时间:2024-10-26 01:34:08

“鼠标拖动游戏窗口,消息循环会卡住,导致游戏不能更新不能渲染”,解决办法<a href=https://www.elefans.com/category/jswz/34/1770260.html style=在这里"/>

“鼠标拖动游戏窗口,消息循环会卡住,导致游戏不能更新不能渲染”,解决办法在这里

开发端游的同学都知道,鼠标按在窗口标题栏上进行拖动,游戏就会卡住不能更新不能渲染。

还有另外一种情况也会导致游戏卡住,就是弹出窗口的系统菜单时,例如下图:



“弹出系统菜单导致游戏卡住”,这种情况比较容易解决,我先讨论这种情况。

有两种操作会弹出系统菜单:

1,鼠标左击标题栏左侧的图标

2,鼠标右击标题栏

当用户发生上面两种操作时,我们拦截掉Windows消息,不要通知给默认的消息处理函数即可。

LRESULT CALLBACK WindowMsgProcess(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{switch (uMsg){case WM_SYSCOMMAND:{switch (wParam & 0xfff0){case SC_KEYMENU:case SC_MOUSEMENU://拦截第一种操作,不要通知给默认的消息处理函数return 0;}}break;case WM_NCRBUTTONDOWN:{//拦截第二种操作,不要通知给默认的消息处理函数return 0;}break;}//执行默认的消息处理函数return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

接下来讨论“鼠标拖动标题栏导致游戏卡住”。

当鼠标在标题栏上按住不放时,系统会发送 WM_SYSCOMMAND 消息,携带的参数为 SC_MOVE ,当DefWindowProc收到 SC_MOVE 后,会发送 WM_ENTERSIZEMOVE ,这个时候整个消息循环就会卡止,直到DefWindowProc处理完成返回。

解决办法也是拦截相应的Windows消息,不要通知给默认的消息处理函数。仅仅拦截消息还不够,用户的目的是移动窗口,我们要写代码跟随鼠标的移动来移动窗口;否则的话,用户无论怎样拖动标题栏,窗口都一动不动。

定义下面几个全局变量,用于我们的“鼠标拖动窗口标题栏,不要阻塞消息循环”逻辑。

一旦 g_bNcLButtonDown 的值为true,表示要跟随鼠标的移动来移动窗口。

bool g_bNcLButtonDown = false;
POINT g_kLastMousePos;
POINT g_kCurMousePos;
RECT g_kCurWindowRect;

响应Windows消息,来更改 g_bNcLButtonDown 的值

LRESULT CALLBACK WindowMsgProcess(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{switch (uMsg){case WM_SYSCOMMAND:{switch (wParam & 0xfff0){case SC_MOVE:{if (g_bNcLButtonDown == false){//鼠标开始拖动标题栏,我们的逻辑开始执行g_bNcLButtonDown = true;::GetCursorPos(&g_kLastMousePos);::GetWindowRect(hWnd, &g_kCurWindowRect);}//拦截,不要通知给默认的消息处理函数return 0;}break;}}break;case WM_NCLBUTTONUP:{//在窗口的非客户区(主要是标题栏),鼠标抬起了,表示不再拖动窗口,我们的逻辑停止执行//不要拦截,交给DefWindowProc去处理。g_bNcLButtonDown = false;} break;}//执行默认的消息处理函数return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

在游戏循环里面,做“跟随鼠标的移动来移动窗口”的逻辑

	//“鼠标拖动游戏窗口,不要卡住游戏循环”逻辑。if (g_bNcLButtonDown_NoBlockLoop){//有时候客户端会卡顿,一旦不能收到窗口消息,g_bNcLButtonDown_NoBlockLoop可能会一直为true,导致bug。//解决办法是,这里总是判断鼠标左键是否按下。从系统层面获取鼠标左键是否按下。if (GetAsyncKeyState(VK_LBUTTON) & 0x8000){::GetCursorPos(&g_kCurMousePos_NoBlockLoop);const int nDeltaX = g_kCurMousePos_NoBlockLoop.x - g_kLastMousePos_NoBlockLoop.x;const int nDeltaY = g_kCurMousePos_NoBlockLoop.y - g_kLastMousePos_NoBlockLoop.y;if (nDeltaX < -1 || nDeltaX > 1 || nDeltaY < -1 || nDeltaY > 1){g_kCurWindowRect_NoBlockLoop.left += nDeltaX;g_kCurWindowRect_NoBlockLoop.top += nDeltaY;g_kLastMousePos_NoBlockLoop = g_kCurMousePos_NoBlockLoop;//执行移动操作的时候不要更改窗口size。::SetWindowPos(g_hWnd_NoBlockLoop, HWND_NOTOPMOST, g_kCurWindowRect_NoBlockLoop.left, g_kCurWindowRect_NoBlockLoop.top, g_kCurWindowRect_NoBlockLoop.right, g_kCurWindowRect_NoBlockLoop.bottom, SWP_NOSIZE);}}else{g_bNcLButtonDown_NoBlockLoop = false;}}


至此,大功告成。

-----------------------------------------------------------------------------

回复留言

回复supercolin:我最近在做“渲染模块和更新模块分拆成两个线程”的工作,感悟到你的说法不对。鼠标拖动游戏窗口,消息循环就会卡住,虽然渲染线程不会卡住,但是渲染数据都是旧的,渲染出来的画面都是过时的画面;当鼠标不再拖动,更新模块会执行一个时间跨度比较大的更新,渲染出来的画面会“瞬移”,“抖动”一下。





更多推荐

“鼠标拖动游戏窗口,消息循环会卡住,导致游戏不能更新不能渲染”,解决办法在这里

本文发布于:2024-02-12 06:24:37,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1686600.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:在这里   鼠标   游戏   拖动   解决办法

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!