炮弹仿真运动
一、步骤
1、新建一个MFC单文档应用程序,如下图所示。
2、新建一个类,命名为“CPaoDan”,如下图所示。
3、在“CPaoDan”类里添加一些成员变量(有些后面会用到),如下图所示。
代码如下:
class CPaoDan
{
public:
void Move();
void Draw(CDC *p);
CPaoDan();
virtual ~CPaoDan();
CDC *pDC;
CPoint m_YD;//原点(只要涉及到坐标必定有坐标原点)
float m_kx,m_ky;//x,y轴方向上的比例尺
float m_x;
float m_y;//以“米”为单位
float m_r;//炮弹坐标位置和半径
float m_m;//炮弹质量
float m_vx,m_vy;//沿x,y轴的速度
float m_ax,m_ay;//沿x,y轴的加速度
float DeltaT;
float m_S;//炮弹横截面积
float v,zl;//速度,阻力
float Rho;//空气密度
float c;//外形阻力系数
};
4、紧接着在构造函数里进行初始化,如下图所示。
5、在CPaoDan里添加成员函数Draw(CDC *p),画炮弹。
代码如下:
void CPaoDan::Draw(CDC *p)
{
int x,y,r;
pDC = p;
//画炮弹
x = m_YD.x + m_x*m_kx;
y = m_YD.y + m_y*m_ky;
r = m_r*m_kx;//半径先以x轴方向上的比例尺为准
pDC->Ellipse(x-r,y-r,x+r,y+r);
//画坐标轴
x = m_YD.x;
y = m_YD.y;
pDC->MoveTo(x,y);
x += 800;
pDC->LineTo(x,y);
x = m_YD.x;
y = m_YD.y;
pDC->MoveTo(x,y);
y -= 500;
pDC->LineTo(x,y);
//画地面打击目标(假设是矩形)
x = m_YD.x + 12000*m_kx;//在12公里处画一目标
y = m_YD.y + 100*m_ky;//高度是100米
r = 200*m_kx;
pDC->Rectangle(x,y,x+r,y+r);
//实时动态显示各量的变化
CString str;
str.Format("阻力f:%f 速度v:%f",zl,v);
pDC->TextOut(200,30,str);
CString str1;
str1.Format("x轴速度:%f y轴速度:%f",m_vx,m_vy);
pDC->TextOut(200,60,str1);
CString str2;
str2.Format("x轴坐标:%f y轴坐标:%f",m_x,m_y);
pDC->TextOut(200,90,str2);
}
6、为了让炮弹动起来,添加“Move()”函数,如下图所示。
代码如下:
void CPaoDan::Move()
{
v = sqrt(m_vx*m_vx + m_vy*m_vy);
zl = 0.5*c*m_S*Rho*v*v;//阻力公式
m_vx += (-zl*m_vx/v/m_m)*DeltaT;
m_vy += (-9.8 - zl*m_vy/v/m_m)*DeltaT;
m_x += m_vx*DeltaT;
m_y += m_vy*DeltaT;
m_ax = -zl*m_vx/v/m_m;//x轴阻力加速度
m_ay = -9.8 - zl*m_vy/v/m_m;//y轴阻力加速度
}
7、在CFaSheView里将“PaoDan.h”嵌入,并在OnDraw(CDC* pDC)里调用,如下图所示。
8、添加菜单,并建立类向导,如下图所示。
代码如下:
void CFaSheView::OnMStart()
{
// TODO: Add your command handler code here
SetTimer(1,100,NULL);
}
void CFaSheView::OnMStop()
{
// TODO: Add your command handler code here
KillTimer(1);
}
9、实现滚轴的放大缩小,只需要把比例尺改一下就行,如下图所示。
代码如下:
BOOL CFaSheView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
// TODO: Add your message handler code here and/or call default
//由于坐标轴用的是像素,没用比例尺,所以不会跟着放大缩小
if (zDelta>0)
{
m_PD.m_kx *= 1.1;
m_PD.m_ky *= 1.1;
}
if (zDelta<0)
{
m_PD.m_kx *= 0.9;
m_PD.m_ky *= 0.9;
}
Invalidate(TRUE);
return CView::OnMouseWheel(nFlags, zDelta, pt);
}
10、接下来就是实现拖动场景中的所有东西。我们只用改变原点就行,因为其他所有东西的计算都是依据原点来的。具体如下图所示。
相关代码如下:
void CFaSheView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_LBD = point;
m_YDTemp = m_PD.m_YD;
m_FlagLBD = 1;
CView::OnLButtonDown(nFlags, point);
}
void CFaSheView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_FlagLBD = 0;
CView::OnLButtonUp(nFlags, point);
}
void CFaSheView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (1 == m_FlagLBD)
{
m_PD.m_YD.x = m_YDTemp.x + (point.x - m_LBD.x);
m_PD.m_YD.y = m_YDTemp.y + (point.y - m_LBD.y);
Invalidate(TRUE);
}
CView::OnMouseMove(nFlags, point);
}
11、运行结果部分截图,如下图所示。
(1)开始运动
(2)放大物体
(3)缩小物体
(4)拖动所有物体
(5)炮弹运动一段时间后
12、为了做到更加完美,可以添加对话框,前面已经实现,方法一样,可自行尝试,不再赘述。
//================================================================================================================================================================================================================================//
续下一节(火箭弹)
火箭弹比炮弹多了一个推力。推力是测出来的,可以将提前测好的数据读出来。下图展示的是每隔50毫秒测试的推力(66个数据)。
二、步骤
1、新建一个CRocket类,可以从CPaoDan类中继承(由CPaoDan类作为一个基类,派生出CRocket类),如下图所示。
2、在CFaSheView里嵌入头文件,如下图所示。
注:在构造函数里,先调用基类,再调用派生类;在析构函数里,先调用派生类,再调用基类。
3、添加需要的成员变量,如下图所示。
4、在CRocket()里做一些初始化改变,有些成员变量不需要改变,就可以直接从炮弹类里继承,如下图所示。
5、对于推力,首先需要去读取,添加成员函数,如下图所示。
代码如下:
//读出推力数据(在构造函数里调用)
void CRocket::ReadTuiLi()
{
FILE * fp;
int i = 0;
fp = fopen("G:\\TuiLiQuXian.txt","r");//从G盘根目录下读数据
while(1)
{
if( fscanf(fp,"%f",&m_TL[i]) != EOF)
i++;
else
break;
}
m_nTL = i;
}
6、当然,我们也可以把推力曲线给画出来,如下图所示。
代码如下:
void CRocket::DrawTuiLi()//里面不需要参数"pDC",因为继承了基类(炮弹类)的
{
int x,y,i;
//画坐标轴
x = m_YDTL.x;
y = m_YDTL.y;
pDC->MoveTo(x,y);
x += 250;
pDC->LineTo(x,y);
x = m_YDTL.x;
y = m_YDTL.y;
pDC->MoveTo(x,y);
y -= 200;
pDC->LineTo(x,y);
//画推力曲线
x = m_YDTL.x;
y = m_YDTL.y;
pDC->MoveTo(x,y);
for(i = 0; i < m_nTL; i++)
{
x += 100*m_kxtl;
y = m_YDTL.y + m_TL[i]*m_kytl;
pDC->LineTo(x,y);
}
}
7、在CFaSheView的OnDraw(CDC *pDC)里调用,如下图所示。
8、画出推力曲线后,编译运行结果如下图所示。
9、给派生类CRocket添加Move函数(CPaoDan里的Move函数就不起作用了),如下图所示。
代码如下:
void CRocket::Move(float deltaT)
{
v = sqrt(m_vx*m_vx + m_vy*m_vy);
zl = 0.5*c*m_S*Rho*v*v;//阻力公式
if (m_iTL < m_nTL)
{
m_vx += (-zl*m_vx/v/m_m)*deltaT + m_TL[m_iTL]*m_vx/v/m_m*deltaT;
m_vy += -(9.8 + zl*m_vy/v/m_m)*deltaT + m_TL[m_iTL]*m_vy/v/m_m*deltaT;
m_iTL++;
}
else
{
m_vx += (-zl*m_vx/v/m_m)*DeltaT;
m_vy += -(9.8 + zl*m_vy/v/m_m)*DeltaT;
}
m_x += m_vx*DeltaT;
m_y += m_vy*DeltaT;
m_ax = -zl*m_vx/v/m_m;//x轴阻力加速度
m_ay = -9.8 - zl*m_vy/v/m_m;//y轴阻力加速度
}
10、添加“火箭”菜单,建立类向导,另设置一个时钟,如下图所示。
相关代码:
void CFaSheView::OnMStartRocket()
{
// TODO: Add your command handler code here
SetTimer(2,50,NULL);//50毫秒
}
11、然后在OnTimer(UINT nIDEvent)里调用。
代码如下:
void CFaSheView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
switch(nIDEvent)
{
case 1:
m_PD.Move();
break;
case 2:
m_Rocket.Move(0.05);
break;
}
Invalidate(true);
CView::OnTimer(nIDEvent);
}
12、运行结果如下图所示。