画面抖动不停!该如何解决
画面抖动不停!
我在作一个windows的小游戏,现在有一个很严重的问题,就是屏幕在刷新时会不停的抖动以至于严重影响游戏,几乎没办法看清楚窗口里面的内容,请问哪位知道这是为什么?是不是因为使用GDI来绘图的原因?高分答谢!!!
------解决方案--------------------
嘿嘿,例子没有,你目前的改动并不大的。
你现在的流程应该是这样的吧:
-画面需要更改
-刷新窗体
-onPaint
-绘图到窗体dc
-endpaint
那你只需要把你的流程修改成这样即可:
-建立一个全局dc,用createcompatibleDC和createcmopatibleBitmap并把二者关联
-画面需要更改
-刷新窗体
-onPaint
-绘图到全局dc,这里肯定有多次绘图
-拷贝全局dc到窗体dc
-endpaint
另外,如果你是在鼠标的每个move消息里都重绘整个窗体,可能还会有闪烁或者窗体都没法看了,这就需要优化你的思路了
------解决方案--------------------
计算机屏幕是有个刷新率,就是每秒刷新多少次,尤其是crt显示器,场频是很重要的。
而你绘图,如果直接在窗口dc上绘制,每绘制一次就是写一次显存,多次绘制就是写多次显存,而这些操作会被分割到数个场频中,人的肉眼看上去就是闪烁的效果。
双缓冲的作用就是尽量把显示屏内容的变化在一个刷新周期内完成,这样看上去就不闪了。
至于双缓冲的运用,我在自绘窗体说着控件的时候都用,更别说做游戏了,我敢保证,无论是ddraw做的游戏,还是gdi做的游戏,没人不用双缓冲。
另外,我说的onPaint,就是响应wm_paint的函数。也就是相当于你的drawGame()。
还有,你其实没必要调用invalidate和updatewindow,直接getdc(),draw(),releasedc()就行了。
------解决方案--------------------
不要全部刷!
------解决方案--------------------
刷新时,只刷变化的那个地方,不要全窗口刷。
------解决方案--------------------
UpdateWindow(hwnd)发送的是不进队消息,调用时会立即进行刷新,程序中有时会起到很好的效果。
------解决方案--------------------
双缓冲更新
尽量用局部更新
------解决方案--------------------
1,黑白图像是因为你建立bitmap时传入的是memdc,改成窗体的dc肯定解决!
2,你的程序可能会造成gdi资源泄露,死锁不知是否于此有关。
你的代码还可以优化,把memdc建立成全局的,不用每次都建立,这样可以节约不少资源。
令,比较标准的使用memdc的方法示例:
HDC hdc = GetDC(hwnd);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmp = CreateCompatibleBitmap(hdc, 100, 100); //***注意这里传入的dc
HBITMAP hOldBmp = (HBITMAP)SelectObject(hdcMem, hbmp);
COLORREF crOld = SetTextColor(xxxx); //如果设置了文本颜色,以后最好也恢复原状
int nOldMode = SetBkMode(xxx); //同样,修改dc的任何属性以后都最好恢复原状
////绘图
//释放:
ReleaseDC(hwnd, hdc);
//**先恢复,再删除
SetBkMode(hdcMem, nOldMode);
SetTextColor(hdcMem, crOld);
SelectObject(hdcMem, hOldBmp);
DeleteDC(hdcMem);
DeleteObject(hbmp);
------解决方案--------------------
要提醒你一下,你的代码实际是这样的:
for(;;)
{
drawGame();
}
系统有99.9%以上的时间都是执行drawGame这个函数中的代码,所以对这个函数要重点优化,能做出全局的变量尽量做成全局的,不要每次都建立再删除。利用空间来换取时间。
------解决方案--------------------
死机,看你的情况应该是程序死掉而不是整个系统,那么你有没有用到多线程呢?如果有,并且采取了同步措施,那请检查一下这些地方,有可能是主线程和某个工作线程之间死锁了。
------解决方案--------------------
使用双缓充和局部刷新就可以了。
------解决方案--------------------
关于闪烁
可以将InvalidateRect(hwnd, NULL, TRUE);改成InvalidateRect(hwnd, NULL, FALSE);再试试,看看是不能解决闪屏问题。第三个参数代表是否重绘背景,一般闪烁都是由背景重绘引起的,关掉它即可。
关于局部刷新
你用的是API编程吧,在响应WM_PAINT的时候,是不是要调用BeginPaint(),一般都是这么写的Begin(hwnd,&ps),这个ps是一个PAINTSTRUCT结构,它有一个成员 rcPaint,当执行完这个函数后ps.rcPaint存的就是无效矩形,也就是你要刷新的局部区域,想要局部刷新只要把InvalidateRect()的第二个参数设成它就可以,即
InvalidateRect(hwnd, ps.rcPaint, TRUE);
你前面把第二个参数设成NULL就是全屏刷新。
关于死锁
if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))这句话可能有问题,你把hwnd换成NULL试试。
------解决方案--------------------
另外我觉得你的主循环结构写得有点烦琐,当然不是有说错误。D3D的demo程序中给的主循环是这么写的,你可以参考一下。
MSG msg;
我在作一个windows的小游戏,现在有一个很严重的问题,就是屏幕在刷新时会不停的抖动以至于严重影响游戏,几乎没办法看清楚窗口里面的内容,请问哪位知道这是为什么?是不是因为使用GDI来绘图的原因?高分答谢!!!
------解决方案--------------------
嘿嘿,例子没有,你目前的改动并不大的。
你现在的流程应该是这样的吧:
-画面需要更改
-刷新窗体
-onPaint
-绘图到窗体dc
-endpaint
那你只需要把你的流程修改成这样即可:
-建立一个全局dc,用createcompatibleDC和createcmopatibleBitmap并把二者关联
-画面需要更改
-刷新窗体
-onPaint
-绘图到全局dc,这里肯定有多次绘图
-拷贝全局dc到窗体dc
-endpaint
另外,如果你是在鼠标的每个move消息里都重绘整个窗体,可能还会有闪烁或者窗体都没法看了,这就需要优化你的思路了
------解决方案--------------------
计算机屏幕是有个刷新率,就是每秒刷新多少次,尤其是crt显示器,场频是很重要的。
而你绘图,如果直接在窗口dc上绘制,每绘制一次就是写一次显存,多次绘制就是写多次显存,而这些操作会被分割到数个场频中,人的肉眼看上去就是闪烁的效果。
双缓冲的作用就是尽量把显示屏内容的变化在一个刷新周期内完成,这样看上去就不闪了。
至于双缓冲的运用,我在自绘窗体说着控件的时候都用,更别说做游戏了,我敢保证,无论是ddraw做的游戏,还是gdi做的游戏,没人不用双缓冲。
另外,我说的onPaint,就是响应wm_paint的函数。也就是相当于你的drawGame()。
还有,你其实没必要调用invalidate和updatewindow,直接getdc(),draw(),releasedc()就行了。
------解决方案--------------------
不要全部刷!
------解决方案--------------------
刷新时,只刷变化的那个地方,不要全窗口刷。
------解决方案--------------------
UpdateWindow(hwnd)发送的是不进队消息,调用时会立即进行刷新,程序中有时会起到很好的效果。
------解决方案--------------------
双缓冲更新
尽量用局部更新
------解决方案--------------------
1,黑白图像是因为你建立bitmap时传入的是memdc,改成窗体的dc肯定解决!
2,你的程序可能会造成gdi资源泄露,死锁不知是否于此有关。
你的代码还可以优化,把memdc建立成全局的,不用每次都建立,这样可以节约不少资源。
令,比较标准的使用memdc的方法示例:
HDC hdc = GetDC(hwnd);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmp = CreateCompatibleBitmap(hdc, 100, 100); //***注意这里传入的dc
HBITMAP hOldBmp = (HBITMAP)SelectObject(hdcMem, hbmp);
COLORREF crOld = SetTextColor(xxxx); //如果设置了文本颜色,以后最好也恢复原状
int nOldMode = SetBkMode(xxx); //同样,修改dc的任何属性以后都最好恢复原状
////绘图
//释放:
ReleaseDC(hwnd, hdc);
//**先恢复,再删除
SetBkMode(hdcMem, nOldMode);
SetTextColor(hdcMem, crOld);
SelectObject(hdcMem, hOldBmp);
DeleteDC(hdcMem);
DeleteObject(hbmp);
------解决方案--------------------
要提醒你一下,你的代码实际是这样的:
for(;;)
{
drawGame();
}
系统有99.9%以上的时间都是执行drawGame这个函数中的代码,所以对这个函数要重点优化,能做出全局的变量尽量做成全局的,不要每次都建立再删除。利用空间来换取时间。
------解决方案--------------------
死机,看你的情况应该是程序死掉而不是整个系统,那么你有没有用到多线程呢?如果有,并且采取了同步措施,那请检查一下这些地方,有可能是主线程和某个工作线程之间死锁了。
------解决方案--------------------
使用双缓充和局部刷新就可以了。
------解决方案--------------------
关于闪烁
可以将InvalidateRect(hwnd, NULL, TRUE);改成InvalidateRect(hwnd, NULL, FALSE);再试试,看看是不能解决闪屏问题。第三个参数代表是否重绘背景,一般闪烁都是由背景重绘引起的,关掉它即可。
关于局部刷新
你用的是API编程吧,在响应WM_PAINT的时候,是不是要调用BeginPaint(),一般都是这么写的Begin(hwnd,&ps),这个ps是一个PAINTSTRUCT结构,它有一个成员 rcPaint,当执行完这个函数后ps.rcPaint存的就是无效矩形,也就是你要刷新的局部区域,想要局部刷新只要把InvalidateRect()的第二个参数设成它就可以,即
InvalidateRect(hwnd, ps.rcPaint, TRUE);
你前面把第二个参数设成NULL就是全屏刷新。
关于死锁
if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))这句话可能有问题,你把hwnd换成NULL试试。
------解决方案--------------------
另外我觉得你的主循环结构写得有点烦琐,当然不是有说错误。D3D的demo程序中给的主循环是这么写的,你可以参考一下。
MSG msg;