打气球游戏
1.
程序设计
目为“打气球”游戏设计与实现,主要实现以下功能:
(1) 使用面向对象的
,设计并实现“打气球”游戏。
(2) 气球由地平线随机产生,并向上运动;
(3) 用户使用鼠标点击气球,点中气球,气球消失,分数增加100分; (4) 游戏时间为60秒,时间采用倒计时方式显示,游戏时间为0时,提示用户是否重新开始;
(5) “开始/暂停”按钮。当点击“开始”按钮,游戏开始,按钮文字变为“暂定”;点
击“暂停”按钮,游戏处于暂停状态,按钮文字变为“开始”; (6) “退出”按钮。点击“退出”按钮,提示是否退出信息,若确定退出,关闭程序;
若取消,则回到软件运行界面。
2.
1
综合考虑绘图和控件功能的实现,使用AppWizard建立一个基于对话窗口的MFC应用程序框架,并通过控件工具栏选取绘图区域,创建“暂停/开始”按钮、“退出”按钮以及分别显示时间和分数的两个编辑框。
2
在OnPaint()函数下添加代码,创建黑色画笔和红色画刷,分别用来勾画椭圆(气
球)、直线(绳子)和填充气球。通过ExcludeClipRect()函数擦除四边,从而规定了图形显示区域。绘图时,以椭圆中心坐标作为参考,用Ellipse()函数绘制气球,用MoveTo() 、LineTo()函数画绳子和地平线。
3
当点击“退出”按钮时弹出确认退出对话框,点“是”则调用OnOK()退出程序。
4/
首先定义一个bool型变量并初始化为1,用它的值作为if语句的判断条件实现开始和暂停的功能转换。如果时间为0,按钮变为“重新开始” ,点击后弹出确认重新开始对话框,选择“是” 重新开始游戏;游戏开始后单击按钮,用
KillTimer()实现暂停功能,按钮变为“开始”;再点击则重新调用SetTimer()开启计时器,按钮变为“暂停”。
5
定义一个二维数组,第一维
示随机产生的气球数目,第二维表示气球中心坐标。 在OnTimer事件中,响应绘图事件时,通过纵坐标递减实现气球上升,并使用
rand()函数使横纵坐标随机变化实现气球的随机摆动,当气球升至显示区域外就
重新产生一个;响应时间事件时,定义一个时间全局变量并初始化为60,变量递减同时以文本形式不断输出以实现倒计时。如果时间为0,则暂停。在OnCreate()函数下,通过for循环随机产生每个气球中心点的横纵坐标,并通过SetTimer()定义两个定时器,分数每0.05秒刷新一次,时间每过1秒刷新一次。
6
这是鼠标的单击事件,当游戏暂停或结束时不执行。游戏进行时,判断鼠标单击
的位置是否在气球上,即判断鼠标坐标到气球中心的距离,当点在气球范围内的
时候将气球坐标移至界面外,气球消失,同时分数加100,并重新以文本形式输出。
3.
1
#define NUM 10 //每一次随机产生的气球最大数
int a[NUM][2]; //第一维控制数目 第二维表示气球坐标 int count=60; //时间
long score; //分数
bool state=1;
:此段代码写在程序的开端,将产生的气球数目赋值给变量,则只需改变此
处变量的值就可以轻易地改变气球个数,易于修改操作;二维数组的第一维与for循环语句相结合,可以实现对每一个气球的操作,第二维表示坐标,实现对气球
的定位。
2
else
{
// CDialog::OnPaint();
CPaintDC dc(this);
CPen bPen(PS_SOLID, 0, RGB(0,0,0)); //黑色画笔
CBrush rbrush(RGB(255,0,0)); //红色画刷
2
dc.SelectObject(&bPen);
dc.SelectObject(&rbrush);
dc.ExcludeClipRect(450,0,800,600); //规定图形显示区域
dc.ExcludeClipRect(0,401,800,600);
dc.ExcludeClipRect(0,0,10,600);
dc.ExcludeClipRect(0,0,800,10);
dc.MoveTo(30,400); //绘制地平线
dc.LineTo(440,400);
for(int i=0;i<=NUM;i++) //绘制气球和绳子 以气球的中心点作为参考点
{
dc.Ellipse(a[i][0]-20,a[i][1]-25,a[i][0]+20,a[i][1]+25); //宽40 高50
dc.MoveTo(a[i][0],a[i][1]+25);
dc.LineTo(a[i][0],a[i][1]+65); //绳长40
}
}
:用RGB()函数选择颜色,黑色画笔绘制气球轮廓、绳子和地平线,红色画
刷填充气球;
为使界面分明,需要规定显示气球的区域,四次调用ExcludeClipRect()函数分别擦除了界面右、下、左、上的部分区域;绘制地平线时,纵坐标需稍小于绘图区
的下边界纵坐标;为简化操作,选取球心作为气球的定位点,并以此点坐标表示
气球和绳子的端点坐标,操作此点即操作整体。
3
void CTempDlg::OnExit()
{
if(IDYES==MessageBox("确认退出?","Exit",MB_YESNO)) //弹出确认退出对话框
CDialog::OnOK();
}
:单击按钮,屏幕弹出对话框,点击“是”,调用OnOK()退出程序。
4/
void CTempDlg::OnS_P()
{
if(state==0&&count==0) //时间为0 单击重新开始
{
if(IDYES==MessageBox("是否重新开始?"," ",MB_YESNO))
{
SetDlgItemText(IDP_S,"暂停"); //按钮变为暂停
char buffer[15];
score=0;
sprintf (buffer, "SCORE:%4d",score);
m_score1=(LPCTSTR)buffer;
3
UpdateData(0);
count=60;
state=1;
SetTimer(1,50,NULL); //分数0.05秒刷新一次
SetTimer(2,1000,NULL); //时间1秒刷新一次
}
return;
}
if(state==1) //点击后暂停 按钮变为开始
{
SetDlgItemText(IDP_S,"开始");
KillTimer(1);
KillTimer(2);
state=0;
}
Else //点击后游戏开始 按钮变为暂停
{
SetDlgItemText(IDP_S,"暂停");
SetTimer(1,50,NULL);
SetTimer(2,1000,NULL);
state=1;
}
}
:state初值为1,通过判断其值执行相应的if语句。游戏开始后,单击按钮,按钮变为“开始”,同时调用KillTimer()函数,程序暂停;再次单击,按钮变为“暂停”,调用SetTimer()函数,程序继续。随着游戏的进行,当时间递减为0
时,点击弹出确认重新开始的对话框,若点击“是”,则时间变为60,分数归0
并输出,按钮变为“暂停”,游戏重新开始。
5
void CTempDlg::OnTimer(UINT nIDEvent)
{
int i;
switch (nIDEvent)
{
case 1:
for(i=0;i<=NUM;i++)
{
if(a[i][1]<=0||a[i][0]<0) //气球升至界面外 则重新随机产生一个
{
a[i][0]=rand()%(450-30)+30;
a[i][1]=rand()%(480-450)+450;
}
Else //气球上升并随机摆动
4
{
a[i][1]-=rand()%4+2;
a[i][0]+=rand()%4-2;
}
InvalidateRect(NULL,TRUE);
}
return ;
case 2: //输出时间
char buffer[15];
count--;
sprintf (buffer, "TIME:%4d",count); //格式化输出到字符串
m_time1=(LPCTSTR)buffer;
UpdateData(0);
if(count==0)
{
SetDlgItemText(IDP_S,"重新开始");
KillTimer(1);
KillTimer(2);
state=0;
}
return ;
}
}
:OnTimer()函数是通过switch语句对事件进行响应,此段程序中,case1中
首先对每个气球进行了位置判断,若气球中心到了界面之外则重新产生气球;而
界面中的气球则在上升过程中随机摆动,这样就大大避免了气球相互重叠的情
况。case2中主要实现倒计时功能,并将时间转化为字符格式输出,时间为0时,
开始按钮变为“重新开始”。
6
int CTempDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
srand ((int) GetCurrentTime ()); //初始化随机数发生器
rand()%(MAX-MIN)+MIN
POINT point; //
鼠标位置
GetCursorPos(&point);
ScreenToClient(&point);
for(int i=0;i<=NUM;i++)
{
a[i][0]=rand()%(450-30)+30;
a[i][1]=rand()%(480-450)+450;
5
}
SetTimer(1,50,NULL);
SetTimer(2,1000,NULL);
char buffer[15];
sprintf (buffer, "SCORE:%4d",score); //输出分数
m_score1=(LPCTSTR)buffer;
sprintf (buffer, "TIME:%4d",count); //输出时间
m_time1=(LPCTSTR)buffer;
return 0;
}
:此段代码主要就各功能进行函数的创建和定义,包括:初始化随机数发生
器并获取随机数产生气球,定义并获取鼠标位置,设置时间和分数的定时器,定
义时间和分数的输出。
7
void CTempDlg::OnLButtonDown(UINT nFlags, CPoint point) //打气球
{
if(state==0) return;
for(int i=0;i<=NUM;i++)
{
if((point.x-a[i][0])*(point.x-a[i][0])<=400&&
(point.y-a[i][1])*(point.y-a[i][1])<=625) //判断鼠标是否点中气球
{
a[i][0]=0;
a[i][1]=0;
score+=100;
char buffer[15];
sprintf (buffer, "SCORE:%4d",score);
m_score1=(LPCTSTR)buffer;
UpdateData(0);
break;
}
}
CDialog::OnLButtonDown(nFlags, point);
}
:打气球过程是通过鼠标点击气球实现的。State为0时,游戏暂停或结束,程序不运行;游戏过程中,通过判断鼠标点击的位置与气球中心位置的距离判断
是否点中气球。若点中,将气球移至显示界面外,同时分数加100并重新输出。
4.
游戏开始后,界面如下:
6
点击暂停按钮,程序暂停:
7
用鼠标点击气球,分数加100:
时间倒计为0,程序暂停,按钮变为“重新开始”:
8
点击“重新开始”按钮,弹出确认对话框:
点击“是”,游戏重新开始,分数为0,时间从60倒计:
9
点击“退出”按钮,弹出确认对话框:
点击“是”则程序退出。
5.
1随机产生气球时,为使界面清晰美观,需要控制产生的数目,尽量避免气
球相互重叠覆盖的情况。数目可由二维数组控制,后一问题则必须操作气球位置
来解决,程序中是通过气球的随机摆动来减少重叠机会的。 2打气球时,判断鼠标是否点中气球,实际就是判断到椭圆中心的距离。而
点中范围其实是气球所在的矩形。若要点中椭圆,则判断时最好与椭圆方程相结
合。
3游戏中对分数的刷新频率要把握适度,太快屏幕显得不稳定,太慢分数变
化不及时。经在游戏中的不断调整选择了0.05秒作为刷新频率,比较合适。 4对于“暂停/开始”按钮的功能交替,需要设置一个变量作为判断条件。因
此定义一个bool型变量,0、1分别代表两个功能。其实也可以定义一个整型变
量,用奇数和偶数代表两个功能状态。
6.
在游戏功能实现方面,本程序基本实现了游戏中需要的各种基本功能,包括:
10
(1) 气球由地平线随机产生,并向上运动,同时随机小范围摆动; (2) 用户使用鼠标点击气球,点中气球,气球消失,分数增加100分; (3) 游戏时间为60秒,时间采用倒计时方式显示,游戏时间为0时,程序暂停,单击“重新开始”按钮,提示用户是否重新开始;
(4) “开始/暂停”按钮。游戏开始后,点击“暂停”按钮,游戏处于暂停状态,按钮
文字变为“开始”;点击“开始”按钮,游戏重新开始,按钮文字变为“暂停”; (5) “退出”按钮。点击“退出”按钮,提示是否退出信息,若确定退出,关闭程序;
若取消,则回到软件运行界面。
仍存在的不足是:运行程序时游戏直接开始,不是通过点击“开始”按钮实现的。
本程序简洁易懂,层次清晰,可读性和可操作性都很强,用简单的语句实现
了多种功能,修改或完善时也较为灵活方便。
【1】 网冠科技,《C++ 时尚编程百例》 ,机械工业出版社
【2】 陈建峰、张荣奇等,《Vasual C++.NET》 实用编程百例,清华大学出版社 【3】 【美】Michael Morrison,《游戏编程入门》 ,人民邮电出版社 【4】 揣锦华,《面向对象程序设计与VC++实践》 ,西安电子科技大学出版社
11
课程设计成绩评定表
自我质量评价指标(在相应栏目打?)
评 价 质 量 评 价 项 目
优秀 良好 一般 及格 不及格
工作量和态度 ?
实验、计算可靠性 ?
文字和图表质量 ?
总体评价 ?
学 生 签 名
年 月 日
指导教师评定成绩
指 导 教 师 签 名
年 月 日
备 注
12