Welcome

首页 / 软件开发 / VC.NET / MFC游戏开发笔记六 图像双缓冲技术:实现一个流畅的动画

MFC游戏开发笔记六 图像双缓冲技术:实现一个流畅的动画2014-11-03在前几节的笔记里,大家肯定会为一个问题感到心烦:画面怎么老是一闪一闪的啊,太难受了。确实是的,如果玩这样的游戏简直就是一种折磨。但是大家玩游戏的时候,从来没有遇到过这种情况吧?那么游戏开发者是怎么解决这个问题的呢?雾央在这一节笔记里给大家讲解一种简单通用的方法——图像双缓冲。

一、闪烁原因

为了解决问题,我们得首先搞清楚闪烁的原因是什么,然后才能对症下药。能够导致游戏画面闪烁的原因非常多,但是对于我们做游戏开发的同学来说,最主要的就是一种:贴图贴的太频繁。

如果大家是一个细心人的话,那么应该可以发现,在笔记三讲解贴图的时候,当我们贴出背景图的时候,是根本不会闪烁的,但是当我们贴出人物后,闪烁就出来了,而当我们移动人物的时候,闪烁的画面简直惨不忍睹啊。

想弄清楚真正的原因就得要理解GDI绘图的原理:GDI绘图的时候是先绘制到显存里面,然后显存每隔一段时间就需要把里面的内容输出到屏幕上,这个时间就是刷新周期。在绘图的时候,系统会先用一种背景色擦除掉原来的图像,然后再绘制新的图像。如果这几次绘制不在同一个刷新周期中,那么我们看到的就是先看到背景色,再看到内容出来,就会有闪烁的感觉,而绘制的次数越多,看到这种现象的可能性就越大,就闪烁的越厉害。

二、图像双缓冲技术

大家清楚了闪烁的原因后,再结合我们只贴出背景的时候并没有闪烁的事实,那么或许大家就可以想到一种解决方法了:我们事先将要画的所有东西画在一张图片上,然后将这张图直接贴出来,不就解决了吗?

如果你想到这里,那么恭喜你,你已经想到了图像双缓冲技术。其实看起来很高端的这个名词其实非常简单。我们之前画图的时候都是直接画在窗口DC上,在之前我们可以自己先创建一个内存DC,然后把画图都画在内存DC中,最后再一次性的将内存DC输出到窗口DC中,就可以解决画面闪烁的问题了。

下面我们讲述写代码的方法

1.定义变量

首先在CChildView.h中定义两个变量

CDC m_cacheDC; //缓冲DCCBitmap m_cacheCBitmap;//缓冲位图
2.创建缓冲DC

然后呢,在CChildView.cpp中OnPaint中创建缓冲DC

//创建缓冲DCm_cacheDC.CreateCompatibleDC(NULL);m_cacheCBitmap.CreateCompatibleBitmap(cDC,m_client.Width(),m_client.Height());m_cacheDC.SelectObject(&m_cacheCBitmap);
3.在缓冲DC上绘图

后面贴图都贴在缓冲DC上就可以了,如

m_bg.Draw(m_cacheDC,m_client);  

4.缓冲DC输出到窗口DC

最后一次性的将缓冲DC中的内容输出到窗口DC中去,函数都是之前笔记二介绍过的,不熟悉的同学请阅读笔记二。

cDC->BitBlt(0,0,m_client.Width(),m_client.Height(),&m_cacheDC,0,0,SRCCOPY);  

此时OnPaint函数中的内容就如同下面这样

void CChildView::OnPaint() {//获取窗口DC指针CDC *cDC=this->GetDC();//获取窗口大小GetClientRect(&m_client);//创建缓冲DCm_cacheDC.CreateCompatibleDC(NULL);m_cacheCBitmap.CreateCompatibleBitmap(cDC,m_client.Width(),m_client.Height());m_cacheDC.SelectObject(&m_cacheCBitmap);//————————————————————开始绘制——————————————————————//贴背景,现在贴图就是贴在缓冲DC:m_cache中了m_bg.Draw(m_cacheDC,m_client);//贴英雄MyHero.hero.Draw(m_cacheDC,MyHero.x,MyHero.y,80,80,MyHero.frame*80,MyHero.direct*80,80,80);//最后将缓冲DC内容输出到窗口DC中cDC->BitBlt(0,0,m_client.Width(),m_client.Height(),&m_cacheDC,0,0,SRCCOPY);//————————————————————绘制结束—————————————————————//在绘制完图后,使窗口区有效ValidateRect(&m_client);//释放缓冲DCm_cacheDC.DeleteDC();//释放对象m_cacheCBitmap.DeleteObject();//释放窗口DCReleaseDC(cDC);}