第二章:MFC绘图基础

基于MFC三维图形开发

提纲:
01.MFC上机操作步骤
02.MFC绘图方法
03.设备上下文的调用与释放
04.双缓冲机制

01.MFC上机操作步骤

在这里插入图片描述
在这里插入图片描述
以后在学习中会重点操作view类:
在这里插入图片描述

最常用到的就是里面的OnDraw()函数,以后对图形的操作就是在里面进行:
在这里插入图片描述

02.MFC绘图方法

在微软基类库MFC中,CDC类是定义设备上下文对象的基类,封装了绘图所需的所有函数。当输出文字或图形时,就需要调用CDC类的成员函数,这些成员函数具备绘制和打印图形的功能。
(CDC就相当于画布)

1.CDC类

在这里插入图片描述其中CClientDC类常用,被称为客户区,设备上下文…

  • CClientDC只能在窗口的客户区(不包括边框,标题栏,菜单栏以及状态栏的空白区域)进行绘图。

  • 点(0,0)是客户区的左上角。
    在这里插入图片描述

2.简单数据类型

在这里插入图片描述

  • CPoint类:存放点的坐标(x,y);
  • CRect类:存放矩形左上角顶点和右下角的顶点(left,top,right,bottom);
  • CSize类:存放矩形宽度和高度的坐标(cx,cy),其中cx是矩形的宽度,cy是矩形的高度。
    在这里插入图片描述

3.绘图工具类

在这里插入图片描述其中最常用的就是CBrush(画刷)和CPen(画笔):
在这里插入图片描述在这里插入图片描述

4.映射模式

  • 把图形显示在屏幕坐标系的过程称之为映射;

  • 根据映射模式不同可以分为逻辑坐标和设备坐标:
    默认坐标系是下面这样:
    在这里插入图片描述上面坐标系中的就是设备坐标。
    我们实际生活中使用的坐标系:
    在这里插入图片描述上面坐标系中的就是逻辑坐标。

  • 映射模式
    在这里插入图片描述

  • 相关函数:
    在这里插入图片描述

在这里插入图片描述
示例1:

CRect rect;//声明客户区矩形
GetClientRect(&rect);//获得客户区坐标
pDC->SetMapMode(MM_ANISOTROPIC);//设置映射模式
pDC->SetWindowExt(rect.Width(), rect.Height());//设置窗口
pDC->SetViewportExt(rect.Width(), -rect.Height());//x轴水平向右,y轴垂直向上
pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);//客户区中心为坐标系原点
rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);//将rect移回到客户区内

上面的代码只是将坐标平移好了,并没有显示出下面的坐标轴。
在这里插入图片描述

在这里插入图片描述

但是上面示例1的代码只是将坐标系调整了下,并没有显示出坐标轴和原点,那我们该怎么显示出来呢?

//画x轴
	pDC->MoveTo(-rect.Width()/2, 0);
	pDC->LineTo(rect.Width() / 2, 0);

	//画x轴箭头
	pDC->MoveTo(rect.Width()/ 2, 0);
	pDC->LineTo(rect.Width() / 2 - 20, 20);

	pDC->MoveTo(rect.Width() / 2, 0);
	pDC->LineTo(rect.Width() / 2 - 20, -20);

	//画y轴
	pDC->MoveTo(0, -rect.Height ()/ 2);
	pDC->LineTo(0, rect.Height() / 2);

	//画y轴箭头
	pDC->MoveTo(0, rect.Height() / 2);
	pDC->LineTo(-20, rect.Height() / 2 - 20);

	pDC->MoveTo(0, rect.Height() / 2);
	pDC->LineTo(20, rect.Height() / 2 - 20);

	pDC->TextOutW(100, 100, CString("第一象限"));
	pDC->TextOutW(-100, 100, CString("第二象限"));
	pDC->TextOutW(-100, -100, CString("第三象限"));
	pDC->TextOutW(100, -100, CString("第四象限"));
	pDC->TextOutW(20, 30, CString("O"));

效果图:
在这里插入图片描述

5.使用GDI对象

5.1 画笔函数

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

5.2 画刷函数

在这里插入图片描述
注意:创建了画笔画刷后并不是直接起作用,我们需要用到SelectObject()函数把画笔画刷的地址传进去,同时该函数会返回原先的画笔和画刷

5.3 选入GDI对象

在这里插入图片描述

示例:

//画一个矩形框
	CPen pen1;
	pen1.CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
	pDC->SelectObject(&pen1);

	CBrush b1;
	b1.CreateSolidBrush(RGB(0, 255, 0));
	pDC->SelectObject(&b1);

	pDC->Rectangle(10, 20, 300, 200);

图示:
在这里插入图片描述


5.4 绘制像素点函数

在这里插入图片描述

5.5 获取像素点颜色函数

在这里插入图片描述
示例:

COLORREF clr;
	int x = 20, y = 20;
	int i;
	for (i = 0; i < 20; i++)
	{
		pDC->SetPixel(x + i, y, RGB(0, 255, 0));
	}

	for (i = 0; i < 20; i++)
	{
		clr = pDC->GetPixel(x + i, y);
		pDC->SetPixel(x + 100 + i, y, clr);
	}

图示:
在这里插入图片描述


5.6 绘制直线段函数

在这里插入图片描述
示例:

CPen newPen, * oldPen;
	newPen.CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
	oldPen = pDC->SelectObject(&newPen);
	pDC->MoveTo(100, 50);
	pDC->LineTo(200, 300);
	pDC->TextOutW(70, 50, CString("p1"));
	pDC->TextOutW(230, 300, CString("p2"));

	pDC->SelectObject(oldPen);
	newPen.DeleteObject();

图示:
在这里插入图片描述


5.7 绘制矩形函数

在这里插入图片描述
示例:

CPen p2;
	p2.CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
	pDC->SelectObject(&p2);

	CBrush b2;
	b2.CreateSolidBrush(RGB(0, 0, 255));
	pDC->SelectObject(&b2);

	pDC->RoundRect(10, 20, 300, 200, 30, 30);

图示:
在这里插入图片描述
示例:

CPen p3, *oldpen;//声明新画笔对象和旧画笔指针
	p3.CreatePen(PS_SOLID, 1, RGB(0, 0, 255));//创建1像素宽的蓝色实线画笔
	oldpen = pDC->SelectObject(&p3);

	CBrush b3, * oldbrush;//声明新画刷对象和旧画刷指针
	b3.CreateSolidBrush(RGB(0, 255, 0));//创建绿色实体画刷
	oldbrush = pDC->SelectObject(&b3);//将新画刷选入设备上下文

	pDC->Rectangle(100, 100, 600, 300);//绘制矩形
	pDC->TextOutW(70, 100, CString("p0"));
	pDC->TextOutW(630, 300, CString("p1"));

	pDC->SelectObject(&oldpen);//恢复旧画笔
	pDC->SelectObject(&oldbrush);//恢复旧画刷

	p3.DeleteObject();// 删除已成自由状态的新画刷
	b3.DeleteObject();//删除已成自由状态的新画刷

图示:
在这里插入图片描述


5.8 绘制椭圆函数

在这里插入图片描述

	绘制圆的话就把他描述成一个正方形。

示例:

CPen p2;
	p2.CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
	pDC->SelectObject(&p2);

	CBrush b2;
	b2.CreateSolidBrush(RGB(0, 0, 255));
	pDC->SelectObject(&b2);

	pDC->Ellipse(10, 20, 300, 200);

	pDC->Ellipse(-200, 300, -100, 200);

图示:
在这里插入图片描述


5.9 绘制多边形函数

在这里插入图片描述

在描述多边形各顶点时,应该顺时针或逆时针来描绘各顶点,不能交叉描述。

示例:

//绘制三角形
	CPoint p[3];
	p[0].x = 50;
	p[0].y = 50;
	p[1].x = 100;
	p[1].y = 100;
	p[2].x = 150;
	p[2].y = 50;

	pDC->Polygon(p, 3);

图示:
在这里插入图片描述

作业:用多边形函数绘制一个风车
	CRect rect;
	GetClientRect(&rect);
	pDC->SetMapMode(MM_ANISOTROPIC);
	pDC->SetWindowExt(rect.Width(), rect.Height());
	pDC->SetViewportExt(rect.Width(), -rect.Height());
	pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);
	rect.OffsetRect(-rect.Width()/2, -rect.Height()/2);

	pDC->TextOutW(30, 20, CString("O"));
	
	CPoint p[3];
	p[0] = CPoint(0, 0);
	p[1] = CPoint(100,-100);
	p[2] = CPoint(300, 0);
	pDC->Polygon(p, 3);

	CPoint a[3];
	a[0] = CPoint(0, 0);
	a[1] = CPoint(100, 100);
	a[2] = CPoint(0, 300);
	pDC->Polygon(a, 3);

	CPoint b[3];
	b[0] = CPoint(0, 0);
	b[1] = CPoint(-100, 100);
	b[2] = CPoint(-300, 0);
	pDC->Polygon(b, 3);

	CPoint c[3];
	c[0] = CPoint(0, 0);
	c[1] = CPoint(-100, -100);
	c[2] = CPoint(0,-300);
	pDC->Polygon(c, 3);

图示:

在这里插入图片描述


5.10 填充矩形函数

在这里插入图片描述

pDC->FillSolidRect(&rect,RGB(0,0,0));

5.11 路径层函数

在这里插入图片描述
在这里插入图片描述

//绘制第一个多边形,用FillPath()函数填充
	CPoint p[7];//声明多边形顶点数组
	p[0] = CPoint(220, 140);
	p[1] = CPoint(140, 60);
	p[2] = CPoint(100, 160);
	p[3] = CPoint(140, 270);
	p[4] = CPoint(200, 200);
	p[5] = CPoint(240, 270);
	p[6] = CPoint(320, 120);

	pDC->TextOutW(220, 100, CString("P0"));
	pDC->TextOutW(140, 50, CString("P1"));
	pDC->TextOutW(60, 160, CString("P2"));
	pDC->TextOutW(140, 300, CString("P3"));
	pDC->TextOutW(200, 260, CString("P4"));
	pDC->TextOutW(240, 300, CString("P5"));
	pDC->TextOutW(320, 120, CString("P6"));


	CPen p1,*p2;
	p1.CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
	p2=pDC->SelectObject(&p1);
	CBrush b1,*b2;
	b1.CreateSolidBrush(RGB(0, 255, 0));
	b2=pDC->SelectObject(&b1);

	pDC->BeginPath();
	pDC->MoveTo(p[0]);
	for (int i =1; i < 7; i++)
	{
		pDC->LineTo(p[i]);
	}
	pDC->LineTo(p[0]);

	pDC->EndPath();
	pDC->FillPath();

	//绘制第二个多边形,用StrokeAndFillPath()函数填充
	CPoint c[7];//声明多边形顶点数组
	c[0] = CPoint(-220, 140);
	c[1] = CPoint(-140, 60);
	c[2] = CPoint(-100, 160);
	c[3] = CPoint(-140, 270);
	c[4] = CPoint(-200, 200);
	c[5] = CPoint(-240, 270);
	c[6] = CPoint(-320, 120);

	pDC->TextOutW(-220, 100, CString("P0"));
	pDC->TextOutW(-140, 50, CString("P1"));
	pDC->TextOutW(-60, 160, CString("P2"));
	pDC->TextOutW(-140, 300, CString("P3"));
	pDC->TextOutW(-200, 260, CString("P4"));
	pDC->TextOutW(-240, 300, CString("P5"));
	pDC->TextOutW(-320, 120, CString("P6"));

	pDC->BeginPath();
	pDC->MoveTo(c[0]);
	for (int i = 1; i < 7; i++)
	{
		pDC->LineTo(c[i]);
	}
	pDC->LineTo(c[0]);
	pDC->EndPath();
	pDC->StrokeAndFillPath();

	//绘制第三个多边形,用画刷填充
	CPoint a[7];//声明多边形顶点数组
	a[0] = CPoint(-220, 140);
	a[1] = CPoint(-140, 60);
	a[2] = CPoint(-100, 160);
	a[3] = CPoint(-140, 270);
	a[4] = CPoint(-200, 200);
	a[5] = CPoint(-240, 270);
	a[6] = CPoint(-320, 120);

	pDC->TextOutW(-220, -100, CString("P0"));
	pDC->TextOutW(-140, -50, CString("P1"));
	pDC->TextOutW(-60, -160, CString("P2"));
	pDC->TextOutW(-140, -300, CString("P3"));
	pDC->TextOutW(-200, -260, CString("P4"));
	pDC->TextOutW(-240, -300, CString("P5"));
	pDC->TextOutW(-320, -120, CString("P6"));

	pDC->Polygon(a, 7);

	pDC->SelectObject(b2);
	b1.DeleteObject();
	pDC->SelectObject(p2);
	p1.DeleteObject();

图示:
在这里插入图片描述


例题:
在这里插入图片描述
代码:

//第一种
	CPoint p[7];//声明多边形顶点数组
	p[0] = CPoint(-580, 140);
	p[1] = CPoint(-660, 60);
	p[2] = CPoint(-700, 160);
	p[3] = CPoint(-660, 270);
	p[4] = CPoint(-600, 200);
	p[5] = CPoint(-560, 270);
	p[6] = CPoint(-480, 120);

	pDC->TextOutW(-580, 100, CString("P0"));
	pDC->TextOutW(-660, 50, CString("P1"));
	pDC->TextOutW(-740, 160, CString("P2"));
	pDC->TextOutW(-660, 300, CString("P3"));
	pDC->TextOutW(-600, 260, CString("P4"));
	pDC->TextOutW(-560, 300, CString("P5"));
	pDC->TextOutW(-480, 120, CString("P6"));

	CPen p1, * p2;
	p1.CreatePen(PS_SOLID, 4, RGB(0, 0, 255));
	p2 = pDC->SelectObject(&p1);

	CBrush b1, * b2;
	b1.CreateSolidBrush(RGB(0, 255, 0));
	b2 = pDC->SelectObject(&b1);

	pDC->BeginPath();
	pDC->MoveTo(p[0]);
	for (int i = 1; i < 7; i++)
	{
		pDC->LineTo(p[i]);
	}
	pDC->LineTo(p[0]);
	pDC->EndPath();
	pDC->FillPath();

	//第二种
	CPoint d[7];//声明多边形顶点数组
	d[0] = CPoint(-280, 140);
	d[1] = CPoint(-360, 60);
	d[2] = CPoint(-400, 160);
	d[3] = CPoint(-360, 270);
	d[4] = CPoint(-300, 200);
	d[5] = CPoint(-260, 270);
	d[6] = CPoint(-180, 120);

	pDC->TextOutW(-280, 100, CString("P0"));
	pDC->TextOutW(-360, 50, CString("P1"));
	pDC->TextOutW(-440, 160, CString("P2"));
	pDC->TextOutW(-360, 300, CString("P3"));
	pDC->TextOutW(-300, 260, CString("P4"));
	pDC->TextOutW(-260, 300, CString("P5"));
	pDC->TextOutW(-180, 120, CString("P6"));


	pDC->BeginPath();
	pDC->MoveTo(d[0]);
	for (int i = 1; i < 7; i++)
	{
		pDC->LineTo(d[i]);
	}
	pDC->LineTo(d[0]);
	pDC->EndPath();
	pDC->StrokeAndFillPath();


	//第三种
	CPoint e[7];//声明多边形顶点数组
	e[0] = CPoint(20, 140);
	e[1] = CPoint(-60, 60);
	e[2] = CPoint(-100, 160);
	e[3] = CPoint(-60, 270);
	e[4] = CPoint(0, 200);
	e[5] = CPoint(40, 270);
	e[6] = CPoint(120, 120);

	pDC->TextOutW(20, 100, CString("P0"));
	pDC->TextOutW(-60, 50, CString("P1"));
	pDC->TextOutW(-130, 160, CString("P2"));
	pDC->TextOutW(-60, 300, CString("P3"));
	pDC->TextOutW(0, 260, CString("P4"));
	pDC->TextOutW(40, 300, CString("P5"));
	pDC->TextOutW(120, 120, CString("P6"));

	pDC->Polygon(e, 7);

图示:
在这里插入图片描述


5.12 绘制Bezier样条函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
例题:
在这里插入图片描述代码:

CPoint p[7];
	p[0] = CPoint(100, 400);
	p[1] = CPoint(200, 200);
	p[2] = CPoint(500, 100);
	p[3] = CPoint(550, 300);

	double k = (p[3].y - p[2].y) / (p[3].x - p[2].x);
	double x = 600;
	double y = k * (x - p[3].x) + p[3].y;

	p[4] = CPoint(ROUND(x), ROUND(y));
	p[5] = CPoint(900, 400);
	p[6] = CPoint(800, 120);

	for (int i = 0; i < 7; i++)
	{
		if (i == 0)
			pDC->MoveTo(p[i]);
		else
			pDC->LineTo(p[i]);
			
		//绘制控制多边形顶点的小圆点
		pDC->Ellipse(p[i].x - 5, p[i].y - 5, p[i].x + 5, p[i].y + 5);
	}

	CPen p1, * p2;
	p1.CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
	p2 = pDC->SelectObject(&p1);
	pDC->PolyBezier(p, 7);
	p1.DeleteObject();
这里的ROUND()是四舍五入的作用,需要加上宏定义:#define ROUND(d) int(d+0.5)

图示:
在这里插入图片描述


6.显示位图

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

示例:
在这里插入图片描述

CRect rect;//声明客户区
	GetClientRect(&rect);//获得客户区坐标
	CDC memDC;//声明一个内存设备上下文对象
	memDC.CreateCompatibleDC(pDC);//创建与显示设备上下文pDC兼容的内存设备上下文memDC
	CBitmap newBitmap, * oldBitmap;//定义一个CBitmap的对象和指针
	newBitmap.LoadBitmap(IDB_BITMAP1);//用LoadBitmap为newBitmap对象加载资源位图
	oldBitmap = memDC.SelectObject(&newBitmap);//再将位图图像选入内存设备上下文中
	BITMAP bmp;//声明位图结构体对象
	newBitmap.GetBitmap(&bmp);//获得位图数据

	//计算位图在窗口客户区居中显示的左上角坐标
	int nX = rect.left + (rect.Width() - bmp.bmWidth) / 2;
	int nY = rect.top + (rect.Height() - bmp.bmHeight) / 2;

	//将内存DC中的位图拷贝到设备DC
	pDC->BitBlt(nX, nY, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);

//如果想要位图充满整个客户区,要用到StretchBlt函数
//pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY);

	//恢复设备上下文
	memDC.SelectObject(oldBitmap);
如果出现“LoadBitmap(IDB_BITMAP1)----未定义标志符IDB_BITMAP1”这个报错,就需要加上#include"resource.h"的头文件

nX,nY计算详解:
在这里插入图片描述

图示:
在这里插入图片描述


7. 设备上下文的调用与释放

在这里插入图片描述


8.双缓冲动画

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

8.1 火焰动画示例:

在这里插入图片描述
首先要将位图加载到资源中,并修改名称为IDM_FLAME;
然后打开Test_2View.h文件,找到操作,在其中声明双缓冲函数:
在这里插入图片描述
打开Test_2View.cpp,在里面定义双缓冲函数:
其中小位图的总数m_TotalBmps需要在Test_2View.h文件中进行声明,并在Test_2View.cpp文件中的构造函数里面进行初始化。
在这里插入图片描述

在这里插入图片描述

若要形成动画,在BitBlt函数中还需要一个参数,让这个函数按照一定的时间间隔拷贝位图,并且还要在Test_2View.h中声明这个参数,并在Test_2View.cpp文件中构造函数中初始化为0.

在这里插入图片描述

在这里插入图片描述

void CTest2View::DoubleBuffer(CDC* pDC)
{
	CRect rect;//声明客户区矩形
	GetClientRect(&rect);//获得客户区坐标
	pDC->FillSolidRect(rect,RGB(0, 0, 0));//将客户区背景色填充成黑色
	CDC memDC;//声明一个内存设备上下文对象
	memDC.CreateCompatibleDC(pDC);//创建与显示设备上下文对象pDC兼容的内存设备上下文对象
	CBitmap newBitmap, * oldBitmap;
	newBitmap.LoadBitmap(IDB_FLAME);//为newBitmap对象加载位图资源
	oldBitmap = memDC.SelectObject(&newBitmap);//在内存设备上下文中选入位图图像
	BITMAP bmp;//声明位图结构体对象
	newBitmap.GetBitmap(&bmp);//将newBitmap对象中的位图信息存放到结构体变量中

	//计算小位图宽度=刚导入的位图总宽/小位图总数
	//m_TotalBmps需要在view.h中进行声明
	int nbmpWidth = bmp.bmWidth / m_TotalBmps;
	
	//计算位图居中显示时左上角点的坐标
	int nX = rect.left + (rect.Width() - nbmpWidth) / 2;
	int nY = rect.top + (rect.Height() - bmp.bmHeight) / 2;

	//用位块传送函数依次将小位图从内存设备上下文拷贝到显示设备上下文中
	//若要形成动画,这需要一个参数,让这个函数按照一定的时间间隔拷贝位图
	//并且还要在Test_2View.h中声明这个参数,并在构造函数中初始化为0
	pDC->BitBlt(nX, nY, nbmpWidth, rect.Height(), &memDC, m_Num * bmp.bmWidth / m_TotalBmps, 0, SRCCOPY);
	
	//最后恢复设备上下文
	memDC.SelectObject(oldBitmap);
}

最后如果我们想要m_Num这个参数起作用,还要设置定时器,那我们如何在MFC中添加呢?
在这里插入图片描述

在这里插入图片描述

在ondraw函数中设置定时器:
在这里插入图片描述
如果没有关闭定时器,会是闪动的动画:

flame动画

如果关闭了定时器,会是下面这样静止的画面:
在这里插入图片描述


8.2 双缓冲动画(小球碰撞:红色球与客户区碰撞)

在菜单项中进行一系列操作后,还要在view.h的头文件中对一些变量和函数进行声明:

	protected:
	BOOL bPlay;//用来控制动画的暂停和播放
	int nWidth, nHeight;//客户区的宽,高
	CPoint direction;//小球的运动方向
	//用圆来代替球
	int r;//半径
	CPoint pt;//圆的中心点



	// 操作
	public:
	void DoubleBuffer(CDC* pDC);//双缓冲函数
	void DrawObject(CDC* pDC);//绘制函数
	void BorderTest();//遇到边界要有碰撞检测

在veiw.cpp文件的构造函数中进行初始化:

CTest2FootballTestView::CTest2FootballTestView() noexcept
{
	// TODO: 在此处添加构造代码
	bPlay = FALSE;//开关设为关
	pt = CPoint(200, 200);//中心点赋值
	direction = CPoint(1, 1);//运动方向:x,y方向各一步长

}

在ondraw函数中调用双缓冲:

void CTest2FootballTestView::OnDraw(CDC* pDC)
{
	CTest2FootballTestDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码
		
		//调用双缓冲
	DoubleBuffer(pDC);


}

定义双缓冲:

//定义双缓冲
void CTest2FootballTestView::DoubleBuffer(CDC* pDC)
{
	//该案例不需要自定义坐标系,直接来写双缓冲即可

	CRect rect;//声明客户区矩形
	GetClientRect(&rect);//获得客户区坐标
	
	//设置客户区的宽,高
	nWidth = rect.Width();
	nHeight = rect.Height();

	//定义内存DC
	CDC memDC;
	//创建一个与显示DC兼容的内存DC
	memDC.CreateCompatibleDC(pDC);
	
	CBitmap newBitmap, * oldBitmap;
	
	//创建兼容内存位图
	newBitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());

	//将兼容位图选入内存DC
	oldBitmap = memDC.SelectObject(&newBitmap);

	//绘制
	DrawObject(&memDC);

	//碰撞检测
	BorderTest();

	//显示内存位图
	pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0,0, SRCCOPY);

	//恢复
	memDC.SelectObject(oldBitmap);
	newBitmap.DeleteObject();

}

定义绘制函数:

//定义绘制函数
void CTest2FootballTestView::DrawObject(CDC* pDC)
{
	//不绘制小球的边界线
	CPen *oldPen = (CPen *)pDC->SelectStockObject(NULL_PEN);

	CBrush newBrush(RGB(255, 0, 0));
	CBrush* oldBrush = pDC->SelectObject(&newBrush);

	//设置半径
	r = nWidth / 10;

	//绘制圆(左上角坐标:圆心坐标-r;右下角坐标:圆心坐标+r)
	pDC->Ellipse(pt.x - r, pt.y - r, pt.x + r, pt.y + r);

	pDC->SelectObject(oldBrush);
	pDC->SelectObject(oldPen);
	
}

定义边界检测函数:

//定义边界检测函数
void CTest2FootballTestView::BorderTest()
{
	if (pt.x + r >= nWidth)//说明与右边界进行了碰撞
		direction.x = -1;//那么x方向变为朝左
	if (pt.x - r <=0)//说明与左边界进行了碰撞
		direction.x = 1;//那么方向应该每次+1
	if (pt.y + r >= nHeight)//与下边界碰撞
		direction.y = -1;
	if (pt.y - r <=0)//与上边界碰撞
		direction.y = 1;
}


在OnTimer函数中根据球体的运动方向改变小球的中心位置:

void CTest2FootballTestView::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

		//根据球体的运动方向改变小球的中心位置
		pt += direction;
		Invalidate(FALSE);

	CView::OnTimer(nIDEvent);
}

在菜单响应函数OnGraph()中播放或停止动画:

void CTest2FootballTestView::OnGraph()
{
	// TODO: 在此添加命令处理程序代码

		//在菜单相应栏中播放或停止动画
	    bPlay = !bPlay;
		if (bPlay)
		{
			//如果播放,每隔10ms发送一个时钟脉冲来触发动画
			SetTimer(1, 10, NULL);
		}
		else
		{
			//暂停就关闭定时器
			KillTimer(1);
		}
}

在OnUpdateGraph(CCmdUI* pCmdUI)函数中添加对菜单项显示状态的控制代码:

//添加对菜单项显示状态的控制代码
void CTest2FootballTestView::OnUpdateGraph(CCmdUI* pCmdUI)
{
	// TODO: 在此添加命令更新用户界面处理程序代码

		//设置菜单项选中时图标是暗下状态
	if (bPlay)
	{
		pCmdUI->SetCheck(TRUE);

		//菜单提示下一次按下会停止
		pCmdUI->SetText(_T("停止"));
	}
	else
	{
		//设置菜单项未选中时图标是弹起状态
		pCmdUI->SetCheck(FALSE);

		//菜单提示下一次按下会播放
		pCmdUI->SetText(_T("播放"));
	}
		

}


运行结果如下:

双缓冲动画-小球碰撞

  • 27
    点赞
  • 210
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
《深入浅出mfc 第2版》是一本介绍使用MFC(Microsoft Foundation Class)框架进行Windows程序开发的书籍。以下是该书第2版的目录: 第一部分:MFC基础知识 第1章:MFC概述 本章主要介绍了MFC框架的基本概念和结构,包括应用程序、文档、视图、框架窗口等核心组件的作用和关系。 第2章:MFC应用程序框架 该章详细介绍了MFC应用程序框架的创建和使用方法,包括应用程序类的创建、初始化、消息循环等内容。 第3章:MFC文档和视图 本章介绍了MFC中的文档和视图类的概念和使用方法,以及如何在框架中创建多文档和单文档应用程序。 第4章:MFC对话框和控件 该章讲解了MFC中对话框的创建和使用,包括控件的添加、消息响应等内容。 第二部分:MFC高级应用 第5章:MFC菜单和工具栏 本章介绍了如何在MFC应用程序中创建和使用菜单和工具栏,以及如何添加图标和快捷键等。 第6章:MFC绘图和图像处理 该章详细介绍了MFC中的绘图和图像处理功能,包括直线、曲线、矩形、椭圆等的绘制方法。 第7章:MFC文件和目录操作 本章讲解了在MFC应用程序中如何进行文件和目录操作,包括文件的打开、读写、保存等功能。 第8章:MFC多线程编程 该章介绍了MFC中的多线程编程方法,包括创建和使用线程、线程同步等内容。 第三部分:MFC实战开发 第9章:MFC数据库编程 本章详细介绍了MFC中的数据库编程,包括连接数据库、查询、插入、更新、删除等操作。 第10章:MFC网络编程 该章讲解了MFC中的网络编程方法,包括创建和使用套接字、实现客户端和服务器端的通信等内容。 第11章:MFC图形用户界面设计 本章介绍了如何使用MFC框架进行图形用户界面的设计,包括窗口布局、控件属性设置、菜单和工具栏的使用等。 第12章:MFC应用程序发布与部署 该章讲解了如何将开发完成的MFC应用程序进行发布和部署,以便在其他计算机上运行。 通过阅读《深入浅出mfc 第2版》的目录,读者可以全面了解MFC框架和其在Windows程序开发中的应用,从基础知识到高级应用再到实战开发,逐步掌握MFC的使用技巧和开发经验。这本书对于想要学习和掌握MFC框架的开发者来说是一本很有价值的参考资料。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值