ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2022-09-11 18:33:52  阅读:147  来源: 互联网

标签:CSphere 函数 缓冲 void CG 点击 绘图 设置 CenterPoint


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有