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