编程语言
首页 > 编程语言> > CG第一天——双缓冲绘图算法

CG第一天——双缓冲绘图算法

作者:互联网

2022-09-11

双缓冲算法的一个实例即为如下动画:

 

 

 

 

 由结果图可以看出,

(1)其中有一个白色的小球,因此需要创建一个小球类。在创建小球类之前,先创建一个MFC项目。

  创建一个MFC项目的步骤:

    首先打开VS,点击新建;点击MFC应用;更改存放路径,更改项目名称,例如:“DoubleBufferes”,点击下一步;在弹出的“MFC应用程序”窗口中,找到“应用程序类型”,点击下拉菜单,选择“单个文档”;在这一页旁边的“项目样式”中选择“MFC standard”样式,之后点击完成。即创建好了一个MFC应用项目。

创建好项目后,添加一个小球类,步骤如下:

    在右侧的“解决方案”中,选中项目名称“DoubleBufferes”,右击点击“添加”,选择“类”;在弹出的类的设置中,填写类名,例如:“CSphere”

CSphere.h文件

 1 #pragma once
 2 class CSphere
 3 {
 4 public:
 5     CSphere(void);//构造函数
 6     virtual ~CSphere(void);//析构函数
 7     void SetParameter(int R,CPoint CenterPoint);
 8     void Draw(CDC* pDC);
 9 public:
10     int R;//球体半径
11     CPoint CenterPoint;
12 
13 };

  说明:

    ①一直以来,我不太明白,构造函数明明是 “CSphere()”,那为什么第5行内部要加一个void,很不理解。后来我查了一下,给出的解释是这样的,如果将void 作为参数传给构造函数或者其他函数,则说明该函数不支持参数的传入,就相当于CSphere(),可能就是一个习惯写法吧。

    ②代码第6行:是一个析构函数的声明,那为啥析构函数前面要加上“virtual”虚函数的关键字呢?这里要特别加入“virtual”关键字是为了防止内存泄露。那为啥不使用“virtual”关键字修饰就会发生内存泄露呢?这里有一种这样的情况,如果该类作为父类,创建了子类后,创建子类对象后,子类对象占用了一定的内存空间后,当删除子类对象时,要调用子类析构函数,由于没有使用“virtual”虚关键字,就构不成动态绑定,实际上只调用了父类的析构函数,但没有调用子类的析构函数,那么子类占用的那部分内存空间就会得不到释放,造成内存泄露。

    ③代码第7行:设置球心的坐标,以及半径。

    ④代码第8行:表示使用CDC绘制小球。

  

  注意:

    在上面的定义,在第7行代码中,写到"CPoint”和第8行“CDC”时,发现飘红了,就很奇怪。这后又重新看了一下讲课视频,发现了这个问题产生的原因是还未写完全,没有声明。需要点击“DoubleBufferesView.h”文件,打开后,从上往下找到第三个“protected:”,在里面添加要声明的内容,如下:

protected:
    int nWidth, nHeight;
    CSphere sphere;
    CPoint direction;
    BOOL bPlay;

  注意:

    在定义“CSphere”类的变量时,需要添加一个头文件。

#include"CSphere.h"

CSphere.cpp文件:

 1 #include "pch.h"
 2 #include "CSphere.h"
 3 CSphere::CSphere(void){
 4 }
 5 CSphere::~CSphere(void){
 6 }
 7 void CSphere::SetParameter(int R, CPoint CenterPoint) {
 8     this->R = R;
 9     this->CenterPoint = CenterPoint;
10 }
11 
12 void CSphere::Draw(CDC* pDC) {
13     CPoint TopLet(CenterPoint.x - R, CenterPoint.y - R);
14     CPoint BottomRight(CenterPoint.x + R, CenterPoint.y + R);
15     CRect Square(TopLet, BottomRight);
16     pDC->Ellipse(Square);
17 }

  说明:

    在绘制小球的函数中(第12行代码),先设置了两个点,一个是左上角点,另一个是右下角点。需要说明的一点是,客户区内的x-y轴中的圆点初始是设置在左上角点,x轴是用上到下,y轴是从左到右。设置两个点后,将他们作为“CRect”类的两个参数进行传入。最后使用绘图指针“pDC”进行绘制椭圆,参入的参数即为上面设置好的矩形。这里表示的是在给定的矩形内绘制它的内切椭圆。

(2)由运行结果图中可以看出,

  菜单栏中有“文件”、“帮助”、“图形(G)”,其中在图形的下拉菜单中还有一个“绘图(A)”的选项。而且在第二行中有一个“播放的红色的按钮”。在这一步中需要设置菜单栏的选项。

  ①首先,点击菜单栏中的“视图”,找到“其他窗口”,点击“资源视图”。

  ②在资源视图中,找到“Menu”文件夹,点击其文件夹下的“IDR_MAINFRAME”,打开后会看到一横排“文件”、“编辑”...工具栏。现在可以将不用的选项删除,只留下“文件(F)”,“帮助(H)”;在这一排的后面会有一个文本框,显示“请在此处键入”,可以输入“图形(G)”;后选中“图形(G)”,在它的内部设置一个选项“绘图(A)”。

  选中“绘图(A)”,右击属性,找到“ID”,将“ID”设置为“ID_ANIMATION”,ctrl+s保存。这一步一定要记住设置,因为后面会设置响应函数,需要用到。

  ③设置“播放按钮图标”,点击“资源视图”中的“Toolbar”,"Toolbar"是设置图标的地方。点击“Toolbar”内部的“IDR_MAINFRAME”,会看到第一排是一些常用的图标,此处将这些图标删除,留下最后一个就可以。如果这里直接选中图标然后点击“Delete”会发现里面的图标是删除了,但是会遗留下很多白色的小方块。so如果要删除,那么要将第一排的小方块拖到第二排左边就删除了。

  在最后一个小方块中,可以先用上面工具栏中的放大镜放大,之后使用画笔工具画一个“播放按钮”。此时,点击第一排绘制好的“播放按钮”,右击,选择属性,将ID仍然设置为“ID_ANIMATION”。

  此时,运行结果图中的菜单栏已设置完成。

 

 (3)有一个小的细节,结果图中的名称是“双缓冲算法”。那么它是如何设置的呢?

    找到“解决方案资源管理器”,点击“DoubleBufferes.cpp”,找到第125行代码,在它下面加入一条语句,用于设置标题的名称,如下

m_pMainWnd->SetWindowTextW(CString("双缓冲算法"));

 

 (4)由结果图可以看出小球有规律地在运动,so里面使用了一个定时器,那么就设置一个定时器

  首先,点击"DoubleBufferesView.cpp"文件,在该文件中的构造函数中,写入如下代码,设置小球

1 CDoubleBufferesView::CDoubleBufferesView() noexcept
2 {
3     // TODO: 在此处添加构造代码
4     bPlay = FALSE;
5     sphere.R = GetSystemMetrics(SM_CXFULLSCREEN) / 20;
6     sphere.CenterPoint.x = 200, sphere.CenterPoint.y = 200;
7     direction.x = 1, direction.y = 1;
8 
9 }

    说明:

      在第5行代码中,“GetSystemMetrics(SM_CXFULLSCREEN)”语句表示“客户区的宽度1280”。第4行先设置了一个动画开始按钮的标志,初始值设为“FALSE”。第5,6行设置小球对象的球心与半径,第7行设置x轴与y轴移动的步长。

   

  绘制小球(DrawObject):

    添加函数的步骤:

      点击“项目”,点击“类向导”,在类名中,定位到“CDoubleBufferesView”。点击“方法”,添加方法,设置好函数名,返回值,参数。点击确定。代码如下:

 

1 void CDoubleBufferesView::DrawObject(CDC* pDC)//绘制图形
2 {
3     // TODO: 在此处添加实现代码.
4     sphere.Draw(pDC);
5 }

 

  

  其次,设置定时器。

    步骤:

      ①点击"项目",之后点击“类向导”。在弹出的“类向导”中,找到“类名(N)”,在下拉菜单中,定位到“CDoubleBufferesView”;之后点击“消息”,在输入框中输入“WM——TIMER”,点击它,右边有一个“添加处理程序”点击。

      ②同时设置映射函数,此时设置映射函数是为了在里面设置控制定时器的内容。点击“命令”找到刚刚设置的“ID_ANIMATION”,点击右边“消息”中的“COMMAND”,点击“添加处理程序”;统一的操作再来一次,这回“消息”设置为“UPDATE_COMMAND_UI”。

      ③最后,点击“确定”。

    

    定时器内部的代码:

1 void CDoubleBufferesView::OnTimer(UINT_PTR nIDEvent)
2 {
3     // TODO: 在此添加消息处理程序代码和/或调用默认值
4     sphere.CenterPoint += direction;
5     Invalidate(FALSE);
6 
7     CView::OnTimer(nIDEvent);
8 }

        说明:

          在第4行代码中,每次移动一次,则将小球中心点的位置增加一个步长的单位。第5行代码表示停止画面一直刷新。

    

    OnAnimation函数中的代码:

 1 void CDoubleBufferesView::OnAnimation()
 2 {
 3     // TODO: 在此添加命令处理程序代码
 4     bPlay = !bPlay;
 5     if (bPlay)
 6         SetTimer(1, 10, NULL);
 7     else
 8         KillTimer(1);
 9 
10 }

        说明:

          该函数表明,如果动画按钮标志“bPlay”为“True”,即点击了单数次按钮,那么动画开始播放;如果动画按钮标志“bPlay”为“False”,即点击了双数次按钮,那么动画停止播放。

 

    OnUpdateAnimation函数:

1 void CDoubleBufferesView::OnUpdateAnimation(CCmdUI *pCmdUI)
2 {
3     // TODO: 在此添加命令更新用户界面处理程序代码
4     if (bPlay)
5         pCmdUI->SetCheck(TRUE);
6     else
7         pCmdUI->SetCheck(FALSE);
8 }

 

(4)从运行结果图,可以看到当小球碰到客户区四周后,会反弹,那么当碰到后,会有一个检查函数,用来改变方向。碰撞函数“CollisionDetection”的添加,点击“项目”,点击“类向导”,在弹出的“类向导”中,定位“类名”为“CDoubleBufferesView”。点击“方法”,点击“添加方法”,设置“函数名”、“返回类型”、“参数”等,点击确定。

  CollisionDetection函数

 1 void CDoubleBufferesView::CollisionDetection(void)
 2 {
 3     // TODO: 在此处添加实现代码.
 4     if (sphere.CenterPoint.x - sphere.R < 0)
 5         direction.x = 1;
 6     if (sphere.CenterPoint.x + sphere.R > nWidth)
 7         direction.x = -1;
 8     if (sphere.CenterPoint.y - sphere.R < 0)
 9         direction.y = 1;
10     if (sphere.CenterPoint.y + sphere.R > nHeight)
11         direction.y = -1;
12 }

    说明:

      第4行是碰到左边框、第6行是碰到右边框、第8行是碰到上边框、第10行是碰到下边框。

      当碰到左边框时,小球的x坐标向右增加。其他的同理。

 

(5)从运行结果图可以看出,小球的运动图像是平滑的,不会一帧一阵的闪,那是因为设置了双缓冲函数。

    添加函数步骤添同添加碰撞函数类似。代码如下:

 1 void CDoubleBufferesView::DoubleBuffer(CDC* pDC)//双缓冲绘图
 2 {
 3     // TODO: 在此处添加实现代码.
 4     CRect rect;
 5     GetClientRect(&rect);
 6     nWidth = rect.Width(), nHeight = rect.Height();
 7     CDC memDC;
 8     memDC.CreateCompatibleDC(pDC);
 9     CBitmap NewBitmap, *poldBitmap;
10     NewBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
11     memDC.FillSolidRect(rect, RGB(0, 0, 0));
12     poldBitmap = memDC.SelectObject(&NewBitmap);
13     DrawObject(&memDC);
14     CollisionDetection();
15     pDC->BitBlt(0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);
16     memDC.SelectObject(poldBitmap);
17     NewBitmap.DeleteObject();
18     memDC.DeleteDC();
19 }

      说明:

        第4,5,6行代码是设置了一个客户区矩形。

        第7行:定义内存DC。

        第8行:创建一个与显示DC兼容的内存DC。

        第9行设置一个新的矢量图和一个旧的矢量图指针。

        第10行创建兼容内存位图。

        第11行使用黑色填充客户区。

        第12行将兼容位图选入内存DC。

        第13行绘制小球。

        第14行碰撞检测。

        第15行显示内存位图。

        第16,17,18恢复内存缓冲区,并删除新位图和缓冲区。

 

(6)最后在OnDraw函数中调用双缓冲函数,完成。

  注意:此处参数中“pDC”是注释的,要先将注释去掉。

 1 void CDoubleBufferesView::OnDraw(CDC* pDC)
 2 {
 3     CDoubleBufferesDoc* pDoc = GetDocument();
 4     ASSERT_VALID(pDoc);
 5     if (!pDoc)
 6         return;
 7 
 8     // TODO: 在此处为本机数据添加绘制代码
 9     DoubleBuffer(pDC);
10 }

 

 

 

   

    

  

标签:CSphere,函数,缓冲,void,CG,点击,绘图,设置,CenterPoint
来源: https://www.cnblogs.com/isDaHua/p/16683442.html