欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

VC6.0 MFC 模拟弹簧运动

程序员文章站 2024-03-16 13:47:34
...

模拟弹簧运动

一、内容描述
运用VC6.0新建工程MFC AppWizard(exe),创建单文档应用程序,画一个弹簧(用矩形代替),下面挂有重物(用圆球代替),设定重物质量和弹簧的弹性系数,模拟弹簧运动。
二、最终实现效果图(静态展示)
VC6.0 MFC 模拟弹簧运动
VC6.0 MFC 模拟弹簧运动
三、实现步骤及相关代码说明(详细到每一步)
1、新建一个工程,并取名为“MoveSpring”,步骤如下。
VC6.0 MFC 模拟弹簧运动
2、选择“单文档(S)”,点击“完成”,“确定”即可,如下图所示。
VC6.0 MFC 模拟弹簧运动
3、首先,定义两个结构体(弹簧结构体和重物结构体),如下图所示。
VC6.0 MFC 模拟弹簧运动

typedef struct
{
	POINT posL; //定义弹簧左上角顶点(用矩形模拟弹簧)
	float len,width; //定义弹簧的长度和宽度
	float k; //定义弹簧的弹性系数
	float s; //定义弹簧被拉长的长度(拉长为正,压缩为负)
}MySpring; //定义弹簧结构体

typedef struct
{
	POINT center; //定义重物(小球)中心
	int radius; //定义重物(小球)半径
	float m,v,a; //定义重物(小球)质量,速度,加速度
}MyObject; //定义重物结构体

4、接下来就是绘制弹簧和重物,操作步骤如下。
①绘制弹簧
VC6.0 MFC 模拟弹簧运动

void CMoveSpringView::DrawSpring(CDC *pDC, MySpring spring)
{
	pDC->Rectangle(spring.posL.x,spring.posL.y,spring.posL.x + spring.width,spring.posL.y + spring.len); //画矩形
}

②绘制重物
VC6.0 MFC 模拟弹簧运动

void CMoveSpringView::DrawObject(CDC *pDC, MyObject object)
{
	CBrush brush;
	brush.CreateSolidBrush(RGB(0,0,0)); //画黑色物体
	pDC->SelectObject(&brush);
	pDC->BeginPath();
	pDC->Ellipse(object.center.x - object.radius,object.center.y - object.radius,object.center.x + object.radius,object.center.y + object.radius); //画圆
	pDC->EndPath();
	pDC->FillPath();
}

5、为了更好的显示重物挂在弹簧上面,我们画个线将两者连起来(当然也可以在顶端画两条线将弹簧挂起)。我们现在来绘制弹簧和重物之间的连线,操作步骤如下。
VC6.0 MFC 模拟弹簧运动

void CMoveSpringView::DrawLine(CDC *pDC, MySpring spring, MyObject object)
{
	pDC->MoveTo(spring.posL.x + spring.width/2,spring.posL.y + spring.len); //线的起点
	pDC->LineTo(object.center); //线的终点
}

6、通过OnDraw()函数调用上面的函数,代码如下。

void CMoveSpringView::OnDraw(CDC* pDC)
{
	CMoveSpringDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
	//画悬挂弹簧的两条牵引线
	pDC->MoveTo(m_spring.posL.x - 50,0);
	pDC->LineTo(m_spring.posL.x,m_spring.posL.y);
	pDC->MoveTo(m_spring.posL.x + m_spring.width + 50,0);
	pDC->LineTo(m_spring.posL.x + m_spring.width,m_spring.posL.y);

	DrawSpring(pDC,m_spring); //画弹簧
	DrawObject(pDC,m_object); //画重物
	DrawLine(pDC,m_spring,m_object);//画弹簧和重物之间的连线
}

7、在构造函数里对弹簧、重物、连线进行初始化(当然你也可以在OnInitialUpdate()里初始化),代码如下。

CMoveSpringView::CMoveSpringView()
{
	// TODO: add construction code here
	 m_spring.posL.x = 200;
	 m_spring.posL.y = 100;
	 m_spring.width = 100.0;
  	 m_spring.len = 200.0; //初始化弹簧形状大小
	 m_spring.s = 0.0; //弹簧拉伸的位移,向下为正,向上为负
	 m_spring.k = 100.0; //设定弹性系数(牛顿/米)
	 m_object.center.x = m_spring.posL.x + m_spring.width/2;
m_object.center.y = m_spring.posL.y + m_spring.len + m_spring.s + 50; //初始化重物
	 m_object.radius = 30;
	 m_object.m = 3.0; //重物质量(千克)
	 m_object.v = 30.0; //重物初速度
}

8、此时我们就将静态效果做出来了,如下图所示。
VC6.0 MFC 模拟弹簧运动
9、为了让弹簧能够动起来,首先需要做的就是添加一个菜单来控制弹簧的运动。建立菜单并添加消息响应函数,如下图所示。
VC6.0 MFC 模拟弹簧运动
VC6.0 MFC 模拟弹簧运动

void CMoveSpringView::OnMStart() 
{
	// TODO: Add your command handler code here
	SetTimer(1,30,NULL);
}
void CMoveSpringView::OnMStop() 
{
	// TODO: Add your command handler code here
	KillTimer(1);
}

10、最核心的是通过OnTimer事件改变物体的位置。物体是按照重力和弹簧的拉力来决定它的运动规律,是一个变加速直线运动。(这里我们简单处理,按匀加速直线运动来考虑)。

11、添加时钟函数,操作步骤如下。
VC6.0 MFC 模拟弹簧运动
12、在OnTimer()里编程,代码如下。

void CMoveSpringView::OnTimer(UINT nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default
	float delta_s; //在0.01秒的时间段里,物体产生的位移的增量(有正有负)
	float delta_t = 0.01; 
	m_object.a = (m_object.m*9.8 - m_spring.k*m_spring.s)/m_object.m; //m*g - k*s = m*a
	delta_s = 0.5*(m_object.v + m_object.v + m_object.a*delta_t)*delta_t; //s = v*t + (a*t*t)/2
	m_spring.s += delta_s;
	m_spring.len += m_spring.s; //计算弹簧长度的变化
	m_object.center.y = m_spring.posL.y + m_spring.len + 50;
	m_object.v = (m_object.v + m_object.a*delta_t); //v = v0 + a*t
	Invalidate(true);
	CView::OnTimer(nIDEvent);
}

13、为方便控制,可添加一个键盘响应。比如,当按下键盘中的空格键(space)时,开始运动。添加消息句柄“WM_ KEYDOWN”如下。
VC6.0 MFC 模拟弹簧运动
14、在OnKeyDown()里添加如下代码。

void CMoveSpringView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	bool m_pause; //键盘空格键控制
	if(32 == nChar) //键盘上的空格键ASCII码为32
	{
		m_pause = !m_pause;
		if(m_pause)
			KillTimer(1);
		else
			SetTimer(1,30,NULL);
	}
	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

15、为了消除在运动过程中的闪动,可设置双缓存。先添加一个消息句柄“WM_ERASEBKGND”,并编码,如下图所示。
VC6.0 MFC 模拟弹簧运动

BOOL CMoveSpringView::OnEraseBkgnd(CDC* pDC) 
{
	// TODO: Add your message handler code here and/or call default
	return true;
	return CView::OnEraseBkgnd(pDC);
}

16、最后只需在OnDraw()里做如下改动即可。

void CMoveSpringView::OnDraw(CDC* pDC)
{
	CMoveSpringDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
/*
	//画悬挂弹簧的两条牵引线
	pDC->MoveTo(m_spring.posL.x - 50,0);
	pDC->LineTo(m_spring.posL.x,m_spring.posL.y);
	pDC->MoveTo(m_spring.posL.x + m_spring.width + 50,0);
	pDC->LineTo(m_spring.posL.x + m_spring.width,m_spring.posL.y);
	DrawSpring(pDC,m_spring); //画弹簧
	DrawObject(pDC,m_object); //画重物
	DrawLine(pDC,m_spring,m_object);//画弹簧和重物之间的连线
*/

	CDC MemDC; //定义内存DC
	int width,height; //定义屏幕宽度、高度
	CRect rect; //建立rect对象
	CBitmap MemBitmap; //缓冲的内存位图
	GetWindowRect(&rect); //获取当前视图的大小
	width = rect.Width();
	height = rect.Height(); //记录当前屏幕大小
	MemDC.CreateCompatibleDC(NULL); //建立兼容内存DC(设备上下文),NULL为系统默认模式
	MemBitmap.CreateCompatibleBitmap(pDC,width,height);
	CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap); //保存之前的内存位图
	MemDC.FillSolidRect(0,0,width,height,RGB(240,255,255)); //设置背景颜色
	MemDC.SetBkMode(TRANSPARENT); //设置缓冲DC参数,为双缓存机制做准备

//================================================
	DrawSpring(&MemDC,m_spring);
	DrawObject(&MemDC,m_object);
	DrawLine(&MemDC,m_spring,m_object);//在缓冲DC中画图
	pDC->BitBlt(0,0,width,height,&MemDC,0,0,SRCCOPY);
	MemBitmap.DeleteObject();
	MemDC.DeleteDC();

	//画悬挂弹簧的两条牵引线
	pDC->MoveTo(m_spring.posL.x - 50,0);
	pDC->LineTo(m_spring.posL.x,m_spring.posL.y);
	pDC->MoveTo(m_spring.posL.x + m_spring.width + 50,0);
	pDC->LineTo(m_spring.posL.x + m_spring.width,m_spring.posL.y);
}

17、运行结果如下图所示。
VC6.0 MFC 模拟弹簧运动
VC6.0 MFC 模拟弹簧运动
四、总结
实现弹簧运动最关键的就是OnTimer()函数里的设置。首先要想明白的一点就是:弹簧因何而动?要牢牢抓住在整个运动过程中,弹簧(矩形)右下角的竖坐标点位置一直在改变即可。还有一点就是,只需要通过弹簧(矩形)左上角位置、长度、宽度就能确定重物以及连接弹簧和重物之间连线的位置。

相关标签: VC6.0 MFC