这是14年六月份的课程设计,虽说是公共选修课,但真正着手写的时候,一连写了三天,期间参考了不少经典的书籍,还在一些论坛里找到了不少问题的答案。既然来于网络,自然也应该将最后的成果放回网络,也好有点意义,为后来人提供借鉴。
程序要求:窗口上有10个颜色各异大小相同的气球(填充的椭圆实现),气球可在窗口上随机向4个方向漂动(上下左右),每次移动N个象素,(N通过对话框输入,在【30,100】之间,用微调按钮)当气球碰到边界时,该球破裂(即消失),同时在屏幕上某个位置(随机产生)再产生一个同样颜色的气球,永远保持窗口上有10个气球。
【提高部分】窗口上可用一副表示天空的位图来填充。
最终效果:
1、设计步骤
(1) 工程建立
使用向导建立单文档的MFC应用程序,采用设置默认。
(2) 类中新增变量及作用
在View类中增加了如下变量:
int j;用来储存rand函数产生的随机数,通过j=(rand()%2)*2-1;获得-1或1的值用于气球位置坐标的随机上下或左右改变。
int i; 通过i =rand()%2;获得0或1的值,通过判断语句随机确定下一次气球的移动是上下方向还是左右方向。
int Distance;用来保存一次移动的像素
int PosX[10];储存10个气球的x坐标
int PosY[10];储存10个气球的y坐标
int flag[10];鸡肋,没有用到,删除后运行正常但退出时弹出编译错误,没有弄明白为什么。
CBrush m_brushBackground;定义了一个背景画笔,用于填充背景
在创建的一个对话框SetDia中对编辑框和微调按钮定义了两个关联的变量:
CSpinButtonCtrl m_spim;//微调按钮相关变量
int m_edit;//用于储存编辑框的值
(3) 需处理的消息的详细代码
1.在View类的OnCreate消息中添加生成计时器的代码:
intCMy913104210222View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
SetTimer(1,500,NULL);
return 0;
}
2.在计时器消息下添加强制重绘语句,使得图形可以在设定的时间间隔后刷新。具体代码如下:
voidCMy913104210222View::OnTimer(UINT nIDEvent)
{
Invalidate();//强制重绘
// TODO: Add your message handler code hereand/or call default
CView::OnTimer(nIDEvent);
}
3.View类的Ondraw消息下进行绘画,这也是最主要的部分。为了表述明了,以第一个椭圆为例,坐标是PosX[1],PosY[1].
思路是用两个随机数i和j,进行两次随机。先判断i的值是1还是0,若果是1,改变PosX[1]的值,即左右移动,否则改变PosY[1],即上下移动。再通过
PosX[1]=PosX[1]+j*Distance;
或者 PosY[1]=PosY[1]+j*Distance;
对x坐标或y坐标(由前一次随机已经确定)随机增加或减少Distance个单位。两个平均概率的随机数作用后使得气球在上下左右四个方向随机运动。
同时还要判断通过这样的随机方法产生的坐标位置是否已经超出客户区域。所以要通过一下两个语句得到窗口大小,储存在Crect 类的一个对象rect中。如果符合要求则继续,否则坐标变为客户区内的随机位置。这一过程代码如下。
CRectrect;/*得到窗口的大小*/
this->GetClientRect(&rect);
if(60+PosX[1]>rect.Width()
||20+PosX[1]<0||100+PosY[1]>rect.Height()||54+PosY[1]<0)
{
PosX[1]=int(rect.Width()*(rand()%100)/100);
PosY[1]=int(rect.Height()*(rand()%100)/100) ;
}
然后画椭圆,并标序号。
pDC->Ellipse(20+PosX[1],54+PosY[1],60+PosX[1],100+PosY[1]);
pDC->TextOut(20+PosX[1]+10,54+PosY[1]+10,"1");
----------------------------------------------------------------------
完整地画第一个椭圆的代码如下:
pDC->FillRect(rect,&m_brushBackground);
CBrush brush1(RGB(50,60,25));
CBrush *oldbrush;
oldbrush =pDC->SelectObject(&brush1);//选新的画刷
srand( (unsigned)time( NULL) );
i = rand()%2;
j=(rand()%2)*2-1;
if (i==1)
PosX[1]=PosX[1]+j*Distance ;
else
PosY[1]=PosY[1]+j*Distance;
if(60+PosX[1]>rect.Width() ||20+PosX[1]<0||100+PosY[1]>rect.Height()||54+PosY[1]<0)
{
PosX[1]=int(rect.Width()*(rand()%100)/100);
PosY[1]=int(rect.Height()*(rand()%100)/100);
}
pDC->Ellipse(20+PosX[1],54+PosY[1],60+PosX[1],100+PosY[1]);
pDC->TextOut(20+PosX[1]+10,54+PosY[1]+10,"1");
pDC->SelectObject(oldbrush);//将原来的画刷选回去,
老师一定认为接下来我要用一个循环画出十个圆。不好意思让您失望了!
事实上,为了编写时候思路清晰(其实是我实在弄不清楚了),剩下的九个圆用相同的方法成功得到了。画椭圆的完整代码太长,不好意思在此全部列出。
Ondraw中还有一个部分是用位图笔刷填充背景:
pDC->FillRect(rect,&m_brushBackground);
CBrushbrush1(RGB(50,60,25));
在对话框的类中,设置微调按钮的范围和初始值:
BOOLSetDia::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
this->m_spim.SetRange(30,100);
m_spim.SetPos(50);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
2、遇到的问题及解决的办法
1.开始时上下左右随机移动的实现代码是这样的:
srand( (unsigned)time( NULL) );
int i = rand()%2;
int j=2*rand()%2-1;
if (i==1)
PosX+=j*30 ;
else
PosY+=j*30;
if(20+PosX>rect.Width() || 20+PosX<0||54+PosY>rect.Height()||54+PosY<0)
{
PosX=int(rect.Width()*(rand()%101)/100);
PosY=int(rect.Height()*(rand()%101)/100) ;
}
运行发现,气球只能往左上移动。反复检查代码没有逻辑错误。说明j产生的值不是预期的-1或1,而是-1,后来将其改成
j=(rand()%2)*2-1; 后解决了,原因是运算符优先级的问题。
2.按照第一个气球的代码写出剩下的九个气球后,运行时发现所有的气球在同一时刻都朝一个方向运动。由于每一个气球都是单独的代码产生的,经过仔细观察,认为应该是十个rand()函数产生了相同的数。用srand()时间初始化后还是不能解决,所以在时间后面又加了十个不同的整数。最终产生了不一样的rand()序列。
3.设置移动像素的对话框在获取编辑框的值之后要把这个值传递到View类中的Distanc.原本的想法是在View中定义一个对话框类的对象,然后得到编辑框的相关变量的值。结果失败了,原因可能是强制重绘的时候都调用一次OnDraw下面的代码,使得一直在重新定义对象,所以第二次重绘的时候又回到了初始值。此外还想了利用全局变量的方法,最终没有成功。智力穷尽,到最后也没有想到方法,所以现在的程序设置完之后是没有用上新的值的。
参考文献
MSDN March 29, 2000版本
《可视化程序实际VisualC++》杨喜林等 北京理工大学出版社
《C++程序设计》朱红等清华大学出版社
《MFCWindows应用程序设计(第二版)》任哲 清华大学出版社
《VC++深入详解(修订版)》孙鑫 电子工业出版社
CSDN社区、博客主页