在做俄罗斯方块游戏的时候,遇到游戏过程中屏幕闪烁问题,解决办法如下:
使用双缓冲技术解决屏幕闪烁
- 运行程序,会发现程序有些闪烁,这是因为程序受WM_TIMER 消息触发,调用OnTimer
函数,OnTimer 函数中调用Invalidate 函数的结果。Invalidate 的调用会触发对OnDraw 函数
的调用,从而不停地重绘窗口的结果。 - 在VC++的文档、视图结构中,CView 的OnDraw 函数用于实现绝大部分图形绘制的工
作。如果用户改变窗口尺寸,或者显示隐藏的区域,OnDraw 函数都将被调用来重画窗口。
并且,当程序文档中的数据发生改变时,一般必须通过调用视图的Invalidate(或InvalidateRect)
成员函数来通知Windows 所发生的改变,对Invalidate 的调用也会触发对OnDraw 函数的调
用。正因为OnDraw 函数被频繁调用,所以在其执行时,每次都刷新填充一次视图客户区域,
便会使屏幕不稳定,产生闪烁现象。 - 这里介绍采用双缓冲方式消除屏幕闪烁的方法。普通绘图方式与双缓冲绘图方式的区别
在于:普通绘图方式可以看做是在屏幕上直接绘制图形,双缓冲绘图方式是现在内存中创建
的“虚拟屏幕”上绘制,然后将绘制完成的图形一次性“拷贝”到屏幕上。
修改视图类的OnDraw 函数:
void CMyTetrisView::OnDraw(CDC* pDC)
{
CMyTetrisDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
int m_nWidth, m_nHeight;
CDC m_memDC;
CBitmap m_memBmp;
//1.用于映射屏幕的内存设备环境
//获取游戏窗口的大小用于下面设置内存位图的尺寸
CRect windowRect;
GetClientRect(&windowRect);
m_nWidth = windowRect.Width();
m_nHeight = windowRect.Height();
//内存设备环境与屏幕设备环境关联(兼容)
m_memDC.CreateCompatibleDC(pDC);
//内存位图与与屏幕关联(兼容),大小为游戏窗口的尺寸
m_memBmp.CreateCompatibleBitmap(pDC, m_nWidth, m_nHeight);
m_memDC.FillSolidRect(windowRect, RGB(0, 0, 0));
//内存设备环境与内存位图关联,以便通过m_memDC 在内存位图上作画
m_memDC.SelectObject(&m_memBmp);
DrawImage(bin, outputImage, &m_memDC, bin2, outputImage2, binN, outputImageN, binN2, outputImageN2);
//把内存DC 上的图形拷贝到电脑屏幕
pDC->BitBlt(0, 0, m_nWidth, m_nHeight, &m_memDC, 0, 0, SRCCOPY);
m_memDC.DeleteDC(); //删除DC
m_memBmp.DeleteObject(); //删除位图
}
修改视图类的DrawImage 函数
// CMyTetrisView 消息处理程序
void CMyTetrisView::DrawImage(CBin *bin, unsigned char** image, CDC *pDC, CBin *bin2, unsigned char** image2, CBin *binN, unsigned char** outputImageN, CBin *binN2, unsigned char** outputImageN2)
{
unsigned int width, i, j;
unsigned int height;
width = bin->getWidth();
height = bin->getHeight();
int nSize = 20;
CRect rect;
GetClientRect(&rect);
pDC->FillSolidRect(rect, RGB(137, 137, 137)); //绘制背景色
CRect rc;
CRect rc2;
CRect rcN;
CRect rcN2;
COLORREF BrickColor[8] = { 0xFFFFFF, 0xFF0000, 0x00FF00, 0x0000FF,
0x00FFFF, 0xFFFF00, 0x800000, 0x800080 };
for (i = 0; i<height; i++)
{
for (j = 0; j<width; j++)
{
rc = CRect(j*nSize, i*nSize, (j + 1)*nSize, (i + 1)*nSize);
rc2 = CRect((j + 24)*nSize, i*nSize, (j + 25)*nSize, (i + 1)*nSize);
//绘制面板
if (image[i][j] != 0)
{
pDC->FillRect(rc, &CBrush(BrickColor[image[i][j]]));
pDC->Draw3dRect(rc, GetLightColor(BrickColor[image[i][j]]),
GetDarkColor(BrickColor[image[i][j]]));
}
if (image2[i][j] != 0)
{
pDC->FillRect(rc2, &CBrush(BrickColor[image2[i][j]]));
pDC->Draw3dRect(rc2, GetLightColor(BrickColor[image2[i][j]]),
GetDarkColor(BrickColor[image2[i][j]]));
}
}
}
for (i = 0; i<4; i++)
{
for (j = 0; j<8; j++)
{
rcN = CRect((j + 11)*nSize, (i + 5)*nSize, (j + 12)*nSize, (i + 6)*nSize);
rcN2 = CRect((j + 37)*nSize, (i + 5)*nSize, (j + 38)*nSize, (i + 6)*nSize);
if (outputImageN[i][j] != 0)
{
pDC->FillRect(rcN, &CBrush(BrickColor[outputImageN[i][j]]));
pDC->Draw3dRect(rcN, GetLightColor(BrickColor[outputImageN[i][j]]),
GetDarkColor(BrickColor[outputImageN[i][j]]));
}
if (outputImageN2[i][j] != 0)
{
pDC->FillRect(rcN2, &CBrush(BrickColor[outputImageN2[i][j]]));
pDC->Draw3dRect(rcN2, GetLightColor(BrickColor[outputImageN2[i][j]]),
GetDarkColor(BrickColor[outputImageN2[i][j]]));
}
}
}
}