为何我用双缓冲 不能避免 图形的闪烁?
我是一个新手,刚学习MFC,看了一些图形的知识做了一个简单的贪吃蛇游戏。
游戏设置分三级别:
简单: 0.5s 刷新一次图,即调用OnDraw(CDC* pDC)函数;
中等: 0.2s 刷新一次图,即调用OnDraw(CDC* pDC)函数;
难: 0.05s 刷新一次图,即调用OnDraw(CDC* pDC)函数;
但是选择玩中等和难的界面的闪烁效果很明显啊!
后来才知道可以利用双缓冲来避免图形的闪烁,在网上看了一些双缓冲的资料,
就自己弄了下,但是还是闪烁 不知道为啥。
我绘制图形在veiw中画的,先贴出这部分重要的代码,希望前辈能帮我看下,到底要如何
来利用双缓冲来避免图形闪烁。
代码:
void CMfc_snackeView::OnDraw(CDC* pDC) //画界面
{
CMfc_snackeDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
/*
static CBrush brush(RGB(255, 255, 255));
SetClassLong(this->m_hWnd, GCL_HBRBACKGROUND,
(LONG)(HBRUSH)brush);
*/
//////////////////////////////////////////////////////////////////////////
CDC MemDC; //首先定义一个显示设备对象
CBitmap MemBitmap; //定义一个位图对象
//随后建立与屏幕显示兼容的内存显示设备
MemDC.CreateCompatibleDC(pDC);
//这时还不能绘图,因为没有地方画
//下面建立一个于屏幕显示兼容的位图,至于位图的大小嘛
//可以用窗口的大小
CRect rect;
GetClientRect(&rect);
MemBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
//将位图选入到内存显示设备中
//只要有选入了位图的内存显示设备才有地方地方绘图,画到指定的位图上
CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap);
//先用背景色将位图清除干净,这里我用的是白色作为背景
//你也可以用自己应该用的颜色
MemDC.FillSolidRect(0, 0, rect.Width(), rect.Height(), RGB(255, 255, 255));
//绘图//////////////////////////////////////////////////////////////////////////
//draw border of game
MemDC.Rectangle(CRect(39, 39, 601, 401));
//Records cstring show
CString s;
s.Format("当前用时: %d", m_UseTime/(1000/m_speed));
MemDC.TextOut(620, 60, s);
s.Format("当前得分: %d", m_Count);
MemDC.TextOut(620, 100, s);
s.Format("版权所有: [菜菜]咸鱼");
MemDC.TextOut(620, 140, s);
s.Format("时间: 2011-10-22");
MemDC.TextOut(620, 180, s);
//draw snake
//draw head of snake
CPoint h;
h = m_body.GetAt(0);
MemDC.SelectStockObject(DKGRAY_BRUSH);
switch(m_NowDir)
{
case 4: MemDC.Pie(CRect(h.x, h.y, h.x+20, h.y+20), CPoint(h.x+5, h.y), CPoint(h.x+15, h.y));
MemDC.SelectStockObject(WHITE_BRUSH ); //蛇的眼睛
MemDC.Ellipse(h.x+13, h.y+5, h.x+18, h.y+10);
break;
case 3: MemDC.Pie(CRect(h.x, h.y, h.x+20, h.y+20), CPoint(h.x+20, h.y+5), CPoint(h.x+20, h.y+15));
MemDC.SelectStockObject(WHITE_BRUSH );
MemDC.Ellipse(h.x+10, h.y+2, h.x+15, h.y+7);
break;
case 2: MemDC.Pie(CRect(h.x, h.y, h.x+20, h.y+20), CPoint(h.x+15, h.y+20), CPoint(h.x+5, h.y+20));
MemDC.SelectStockObject(WHITE_BRUSH );
MemDC.Ellipse(h.x+13, h.y+10, h.x+18, h.y+15);
break;
case 1: MemDC.Pie(CRect(h.x, h.y, h.x+20, h.y+20), CPoint(h.x, h.y+15), CPoint(h.x, h.y+5));
MemDC.SelectStockObject(WHITE_BRUSH );
MemDC.Ellipse(h.x+5, h.y+2, h.x+10, h.y+7);
break;
}
//draw body of snake
CPoint b;
MemDC.SelectStockObject(GRAY_BRUSH);
for(int i = 1; i <= m_body.GetUpperBound(); i++)
{
b = m_body.GetAt(i);
MemDC.Rectangle(CRect(b.x, b.y, b.x+20, b.y+20));
}
//draw a food
CBrush br;
br.CreateSolidBrush(RGB(0, 255, 0));
MemDC.SelectObject(br);
MemDC.FillRect(CRect(m_food.x, m_food.y, m_food.x+20, m_food.y+20), &br);
MemDC.Rectangle(CRect(m_food.x, m_food.y, m_food.x+20, m_food.y+20));
//将内存中图拷贝到屏幕上进行显示
pDC->BitBlt(0, 0, 650, 450, &MemDC, 0, 0, SRCCOPY);
//绘图完成后的清理
MemBitmap.DeleteObject();
MemDC.DeleteDC();
}
/////////////////////////////////////////////////////////////////////////////
// CMfc_snackeView printing
BOOL CMfc_snackeView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
return DoPreparePrinting(pInfo);
}
void CMfc_snackeView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add extra initialization before printing
}
void CMfc_snackeView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add cleanup after printing
}
/////////////////////////////////////////////////////////////////////////////
// CMfc_snackeView diagnostics
#ifdef _DEBUG
void CMfc_snackeView::AssertValid() const
{
CView::AssertValid();
}
void CMfc_snackeView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CMfc_snackeDoc* CMfc_snackeView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMfc_snackeDoc)));
return (CMfc_snackeDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CMfc_snackeView message handlers
void CMfc_snackeView::Initial_Game() //游戏地图的初始化
{
m_body.RemoveAll();
//initial direciton
m_NowDir = m_PreDir = 3;
//inital head of snake
CPoint head;
head.x = 100; head.y = 100;
m_body.Add(head);
//create rand food
CMfc_snackeView::Create_food();
}
void CMfc_snackeView::Create_food()
{
CPoint t;
int x, y, prove;
while(1)
{
x = rand()%L + 4;
y = rand()%H + 4;
if(x%2 == 0 && y%2 == 0) //其实坐标是x=(40,580), y=(40,380)
{ //每一个图标如蛇头的high和length是占20, 20
prove = 0;
x *= 10; y *= 10;
//食物不能是蛇的地方
for(int i = 0; i <= m_body.GetUpperBound(); i++)
{
t = m_body.GetAt(i);
if(t.x == x && t.y == y)
{
prove = 1;
break;
}
}
if(!prove)
break;
}
}
m_food = CPoint(x, y);
}
void CMfc_snackeView::GameStart() //开始游戏哦
{ //玩家时间和得分数的初始化
m_Count = m_UseTime = 0;
m_start++;
if(m_start > 1)
CMfc_snackeView::Initial_Game();
//每隔m_speed时间发送WM_TIMER消息
Timer = SetTimer(1, m_speed, NULL);
}
void CMfc_snackeView::OnTimer(UINT nIDEvent) //WM_TIMER消息
{
// TODO: Add your message handler code here and/or call default
m_UseTime++; //时间记录
//蛇移动
CMfc_snackeView::Move();
CView::OnTimer(nIDEvent);
}
void CMfc_snackeView::Move() //蛇移动函数
{
if(m_PreDir == m_NowDir || abs(m_PreDir-m_NowDir) == 2)
m_NowDir = m_PreDir;
else
m_PreDir = m_NowDir;
CPoint head;
head = m_body.GetAt(0);
//4个方向
switch(m_NowDir)
{
case 4: head.y -= 20;
break;
case 3: head.x += 20;
break;
case 2: head.y += 20;
break;
case 1: head.x -= 20;
break;
}
int over = 0;
CPoint t;
//蛇头是否吃到自己
for(int i = 0; i <= m_body.GetUpperBound(); i++)
{
t = m_body.GetAt(i);
if(head.x == t.x && head.y == t.y)
{
over = 1;
break;
}
}
if(!Check(head) || over)
{ //结束Timer,并弹出一个结束的对话框
KillTimer(Timer);
MessageBox("Game is over! You are fail!");
}
else
{
m_body.InsertAt(0, head);
//蛇头吃到食物
if(head.x == m_food.x && head.y == m_food.y)
{
m_Count++;
Create_food();
}
else
m_body.RemoveAt(m_body.GetUpperBound());
}
//使整个窗口客户区无效,意味着需要重绘,即调用OnDraw函数
Invalidate();
}
//键盘按键判断
void CMfc_snackeView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if(m_start)
{
switch(nChar)
{
case 38: //up
m_NowDir = 4;
break;
case 39: //right
m_NowDir = 3;
break;
case 40: //down
m_NowDir = 2;
break;
case 37: //left
m_NowDir = 1;
break;
default:
m_NowDir = m_PreDir;
break;
}
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
//判断是否出界
int CMfc_snackeView::Check(CPoint head)
{
if(head.x < 40 || head.x > 580
|| head.y < 40 || head.y > 380)
return 0;
else
return 1;
}
void CMfc_snackeView::OnEasy()
{
// TODO: Add your command handler code here
m_speed = 500;
CMfc_snackeView::GameStart();
}
void CMfc_snackeView::OnMedium()
{
// TODO: Add your command handler code here
m_speed = 200;
CMfc_snackeView::GameStart();
}
void CMfc_snackeView::OnHard()
{
// TODO: Add your command handler code here
m_speed = 50;
CMfc_snackeView::GameStart();
}
////////////////////////////////////////////////////////////////
由于刚接触到MFC和双缓冲,所以希望前辈们帮下我,像我这种多少秒刷新一次
图的程序,怎么来避免图形的闪烁呢,给一些资料和建议都可以,再次非常的3Q!