VC6.0 MFC 模拟弹簧运动

模拟弹簧运动

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

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

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

4、接下来就是绘制弹簧和重物,操作步骤如下。
①绘制弹簧
在这里插入图片描述

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); //画矩形
}

②绘制重物
在这里插入图片描述

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、为了更好的显示重物挂在弹簧上面,我们画个线将两者连起来(当然也可以在顶端画两条线将弹簧挂起)。我们现在来绘制弹簧和重物之间的连线,操作步骤如下。
在这里插入图片描述

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、此时我们就将静态效果做出来了,如下图所示。
在这里插入图片描述
9、为了让弹簧能够动起来,首先需要做的就是添加一个菜单来控制弹簧的运动。建立菜单并添加消息响应函数,如下图所示。
在这里插入图片描述
在这里插入图片描述

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、添加时钟函数,操作步骤如下。
在这里插入图片描述
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”如下。
在这里插入图片描述
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”,并编码,如下图所示。
在这里插入图片描述

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

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值