C++课程设计报告贪吃蛇游戏文档格式.docx
《C++课程设计报告贪吃蛇游戏文档格式.docx》由会员分享,可在线阅读,更多相关《C++课程设计报告贪吃蛇游戏文档格式.docx(42页珍藏版)》请在冰豆网上搜索。
虽然我的编程能力不是很强,但是我有信心,在这次学习中我将从实践和实际的项目中提高自己的编程能力。
因此我选定了这个题目。
1.2关于编译软件
本程序采用MicrosoftVisualC++6.0的中文版本进行编译。
VisualC++6.0是Microsoft公司推出的基于Windows操作系统的可视化C++编程工具,尽管Microsoft公司推出了.NET平台的集成开发环境,但由于其良好的界面和可操作性,加上支持标准C/C++规范,但仍有相当多的编程人员使用VisualC++6.0进行应用系统的开发。
1.3关于兼容性
本程序经过调试,可以在XP系统下编译运行,也可以在Vista/Win7下运行,界面稍有不同,但不影响运行结果。
第二章设计概要
2.1程序功能简介
据分析,贪吃蛇游戏一共要实现如下几个功能:
Ø
开始运行
蛇在上下左右键的操作下运动
闯关设置
游戏音效
游戏说明
2.2程序流程
根据分析后的贪吃蛇结构设计出相应的贪吃蛇流程(图1)。
贪吃蛇的内容主要包括:
开始游戏,随机出现食物,控制蛇的运动,吃食物,关卡处的消息询问,游戏音效的播放,分数的输出,游戏帮助的输出等等。
图1.流程图
第三章游戏实现
3.1创建工程
本程序使用Win32Application工程,编辑在Windows窗口中打开的应用程序。
由于之前没有接触过这类工程,所以我在短短几天内笼统的学习了一些相关的基本知识。
在程序的编写过程中,遇到了很多问题,通过查阅资料和请教别人,解决了很多问题,但是仍然有遗留的未解决的问题,所以本程序虽然实现了大部分功能,但不是很完善,需要在将来的学习过程中逐渐改进。
图2.创建Win32Application工程
Win32Application工程与Win32ConsoleApplication工程有很大的不同(图2)。
其主函数是WinMain()函数。
创建一个经典的”Hello,World!
”工程,就会有已经写好的WinMain()函数。
WinMain()函数是整个程序的入口,虽然区区几行。
不需要改动什么,但确实最主要的函数。
函数定义如下:
intAPIENTRYWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPSTRlpCmdLine,intnCmdShow)
在创建好的工程中,还有注册窗口函数和窗口过程函数。
窗口过程函数
实现贪吃蛇游戏的算法就写在窗口过程函数中。
3.2游戏界面控制
贪吃蛇游戏的游戏界面包括墙的绘制、蛇身体的绘制、蛇移动范围的绘制、食物的绘制、分数框和注释的绘制等等。
其中贪吃蛇的身体的绘制,是本程序中的核心算法。
3.2.1墙、框体的绘制
在win32application工程中,有很多绘制图形的函数。
这里最多使用到的是Rectangle()函数绘制矩形。
通过坐标控制矩形的两对角点来确定矩形的长、宽、高,通过在窗口中添加画笔和画刷,使用不同的线条颜色和样式,画出所需的图框。
如下是墙的绘制:
hpen=CreatePen(PS_SOLID,1,RGB(255,255,255));
//创建无色的画笔
hbru=CreateSolidBrush(RGB(255,255,255));
//创建无色的画刷
hpenold=(HPEN)SelectObject(hdc,hpen);
hbruold=(HBRUSH)SelectObject(hdc,hbru);
Rectangle(hdc,rt.left,rt.top,rt.right,rt.bottom);
//绘制背景
SelectObject(hdc,hpenold);
//恢复笔
SelectObject(hdc,hbruold);
//恢复画刷
hpen=CreatePen(PS_SOLID,1,RGB(255,100,0));
//创建一支砖红色的笔
//把砖红色的笔选进画板
Rectangle(hdc,45,35,615,465);
hbru=CreateSolidBrush(RGB(255,100,0));
//创建一支砖红色的画刷
//把砖红色的画刷选进画板
for(i=50;
i<
=600;
i+=10)//画围墙
{
Rectangle(hdc,i,40,i+10,49);
//上边
Rectangle(hdc,i,451,i+10,460);
//下边
}
for(i=40;
=450;
i+=10)
{
Rectangle(hdc,50,i,59,i+10);
//左边
Rectangle(hdc,601,i,610,i+10);
//右边
}
SelectObject(hdc,hbruold);
SelectObject(hdc,hpenold);
程序代码中,各种函数的功能已经备注的很详细,这里不再赘述。
在写程序的初期,围墙是用小方格围成的(图3),后来在此基础上做了改动(图4),最后生
成的效果,改变一下画笔,完全可用几句代码来代替,如下:
hpen=CreatePen(PS_SOLID,10,RGB(255,100,0));
//创建一支砖红色的笔,只是在画刷的粗//细上做了改动
Rectangle(hdc,50,40,610,460);
//画围墙
图4
图3
分数框和注释框是用RoundRect()倒角矩形函数绘制的,和前面的矩形函数用法相同。
3.2.2蛇身的绘制
贪吃蛇的身体的绘制,是程序中的核心算法。
包括蛇身的移动、吃食物后增长、颜色的改变。
首先我们创建了蛇的类,蛇的位置是由坐标控制的,蛇的每一节是10×
10的小方格,x[i],y[i]表示蛇每一节的坐标,node表示蛇的节数,direction表示蛇的方向,life为蛇的生命(0存活1死亡)。
开始游戏时,蛇身为两节,在固定的位置出现,然后控制时间移动。
蛇的移动放发就是在原先的蛇头前加一节,在用无色的画刷绘制此时的蛇尾,用来“擦出”最后一节,这样便形成了蛇的移动。
old_pos_x=snake.x[snake.node-1];
//保存移动前蛇尾x的坐标
old_pos_y=snake.y[snake.node-1];
//保存移动前蛇尾y的坐标
//创建一支无色的笔
//创建一支无色的画刷
//把无色的笔选进画板
//把无色的画刷选进画板
Rectangle(hdc,old_pos_x,old_pos_y,old_pos_x+10,old_pos_y-10);
//用无色的笔绘制蛇尾
//用了无色的画刷绘制背景
//恢复有色的笔
for(i=snake.node-1;
i>
0;
i--)//蛇的每个环节往前移动
{
snake.x[i]=snake.x[i-1];
snake.y[i]=snake.y[i-1];
}
当蛇头的坐标和食物的坐标重合,就表明蛇吃到食物了,此时蛇要增长,将原来的食物用无色的笔“擦除”,node+1。
如下:
if(snake.x[0]==food.x&
&
snake.y[0]==food.y)//吃到食物后
hpen=CreatePen(PS_SOLID,1,RGB(255,255,255));
//创建一支无色的笔
hpenold=(HPEN)SelectObject(hdc,hpen);
//把无色的笔选进画板
Rectangle(hdc,food.x,food.y,food.x+10,food.y-10);
//用无色的笔绘制食物以取消
//被吃掉的食物
//恢复有色笔
在程序完成之后,我又添加了闯关模式的功能,其中每过一关,蛇的颜色就会改变,这是使用switch-case语句,每一关都改变绘制蛇的画笔和画刷来实现的。
3.2.3食物的绘制
确定用坐标绘制贪吃蛇蛇身以后,贪吃蛇的食物如何达到随机出现,蛇吃完食物后再次出现的食物不与蛇身相重合,并且能够按照网格式与蛇头无偏差相接就是一个亟待解决的问题。
随机出现应采用rand()函数来实现,使食物随机出现在蛇可以运动的范围内;
使用语句“food.x=rand()%400+60;
food.y=rand()%350+60;
”,让食物的坐标产生在墙内来;
而食物与蛇头无偏差相接则用if语句判断生成的坐标是否可用最小网格的间距整除处理。
使随机出现的点能够整除最小网格,也就是使食物与蛇头无偏差相接。
for(i=2;
snake.node;
i++)
food.x=rand()%400+60;
food.y=rand()%350+60;
if(food.x!
=snake.x[i])
break;
在游戏运行的过程中,经常会出现新出现的食物与蛇身重叠的情况,让玩家看不清食物到底在哪。
经过认真的考虑,解决这个问题的办法就是,每次随机出现食物坐标时,用循环语句判断要出现的食物坐标是否与蛇身某一节的坐标重合,若是重合,重新生成一组随机数,再次判断,直到不重合时在画面上显示新食物。
代码如下:
再由食物坐标(food.x,food.y)与蛇头坐标是否相同判断蛇是否“吃”到了食物,设置判断标签food.yes,如果吃到了,food.yes为0,则再出现下一个食物,反之,如果没吃到,则不出现食物直到标签为0为止。
代码如下:
srand((unsigned)time(NULL));
//随机数发生器
food.yes=1;
//食物标记:
1表示需要出现新食物,0表示已存在食物
if(food.yes==1)//需要出现新食物
{
food.x=rand()%400+60;
food.y=rand()%350+60;
while(food.x%10!
=0)//食物随机出现后必须让食物能在整格内,这样才能让蛇吃到
food.x++;
while(food.y%10!
=0)
food.y++;
food.yes=0;
//画面上有食物了
}
//创建一只无色的笔
hbru=CreateSolidBrush(RGB(0,0,0));
//创建一支黑色的画刷
//把无色的笔选进画板
//把黑色的画刷选进画板
if(food.yes==0)//画面上有食物就显示
Rectangle(hdc,food.x,food.y,food.x+10,food.y-10);
3.3游戏控制
3.3.1蛇的移动
开始游戏后的重点是如何用键盘来控制蛇的移动并传递到时间控制器中去判断蛇是否死亡。
首先说明键盘与蛇的响应,设置一个方向控制变量snake.direction,设置其值为1、2、3、4分别表示左、右、前、后,在窗口控制过程的WM_KEYDOWN中实现键盘消息的传递,按下不同的键盘按键,snake.direction会相应的改变,再利用if语句对坐标进行相应改变即可。
caseWM_KEYDOWN:
key=int(wParam);
//接受按键
if(key==VK_UP&
snake.direction!
=4)//判断是否往相反的方向移动
snake.direction=3;
elseif(key==VK_RIGHT&
=2)
snake.direction=1;
elseif(key==VK_LEFT&
=1)
snake.direction=2;
elseif(key==VK_DOWN&
=3)
snake.direction=4;
在这里,需要注意的是,蛇在转向的时候,不可能转到前进方向的反方向,即向右运动时,蛇不可能转到屏幕的左边前进,需要用if语句进行筛选判断处理。
蛇的移动速度是用SetTimer()函数和KillTimer()函数打开和关闭计时器来控制的。
其中,SetTimer()函数中的参数控制蛇的速度,例如刚开始时为“SetTimer(hWnd,1,500,NULL);
”,表示每500毫秒蛇向前移动一格,此时的速度很慢,玩家可以很方便的控制其位置。
为了增加游戏难度,我使用switch-case语句,改变每一关蛇的速度,越到后面速度越快,增加游戏的刺激性和趣味性,以激起玩家的兴趣。
后来,为了完善游戏,我又在WM_KEYDOWN和WM_KEYUP中增添了模块,使玩家觉的蛇的运行速度过慢而不耐烦时,长按前进方向的方向键,就可以加快蛇的速度。
当计算机接受到长按方向键的响应后,改变计时器的参数,加快蛇的移动速度,当长按键释放后,计算机接受释放按键的响应,恢复蛇原来的速度。
if(Checkpoint<
4)//前三关可以通过长按键改变蛇的速度
if(key==VK_LEFT&
snake.direction==2)
SetTimer(hWnd,1,200,NULL);
snake.direction==1)
elseif(key==VK_UP&
snake.direction==3)
snake.direction==4)
}
break;
caseWM_KEYUP:
key=int(wParam);
switch(Checkpoint)//按键释放恢复蛇的速度
……//这里的代码省略
模块代码如下:
while(GetMessage(&
msg,NULL,0,0))
TranslateMessage(&
msg);
if(msg.message==WM_KEYDOWN&
!
hold)
hold=true;
DispatchMessage(&
elseif(msg.message==WM_KEYDOWN)
;
//忽略多余的按键
elseif(msg.message==WM_KEYUP)
hold=false;
else
但是添加此模块运行时,长按键后蛇停止不动了,释放按键后蛇又开始以原来的速度运动,好像变成了所需功能的相反功能了。
后来查阅资料才明白,计时器的优先级是很低的,当长按方向键时,计算机只是接受了按键的响应,而无暇继续计时器的执行,导致蛇的运动停止,需要在WinMain函数中做一些改动,改变消息机制的处理,当长按键时,忽略之后多余的按键,是计时器响应,这样便很好的解决了这个问题。
代码改变如下:
3.3.2蛇死亡的判断
其次来解释一下如何判断蛇的死亡,由于蛇的身体是由点坐标数组构成的,因此判断蛇是否死亡其本质就是判断蛇头的坐标是否与游戏边框相同或者蛇头坐标是否与蛇自己的身体相同。
该过程也在WM_TIMER中写入即可,设置一个判断死亡变量snake.life,如果snake.life为1,则说明蛇已经死亡,应当弹出对话框提示死亡,输出得分,结束游戏。
在蛇死亡后,要使用无色的画刷将死亡的蛇身和画面中的食物“擦除”,获取蛇与食物的最终坐标利用循环结构便可做到。
if(snake.life==1)
KillTimer(hWnd,1);
//游戏结束,蛇和食物消失
hpen=CreatePen(PS_SOLID,1,RGB(255,255,255));
hbru=CreateSolidBrush(RGB(255,255,255));
hpenold=(HPEN)SelectObject(hdc,hpen);
hbruold=(HBRUSH)SelectObject(hdc,hbru);
for(i=0;
i++)//擦除蛇身
Rectangle(hdc,snake.x[i],snake.y[i],snake.x[i]+10,snake.y[i]-10);
//擦除食物
SelectObject(hdc,hpenold);
SelectObject(hdc,hbruold);
3.3.3消息窗口的弹出
贪吃蛇游戏是一个计算机与用户交互的游戏,当然也就少不了稍息框的弹出设置了。
这一部分需要考虑到消息框在什么时候弹出,弹出后根据不同的用户的选择,程序该怎么运行等问题。
图5
3.3.3.1开始消息框
当用户进入游戏后,便弹出该消息框(图5),选择“确定”按钮后,游戏开始,食物出现,蛇通过Sleep()函数延迟1秒后开始运动。
if(score==0)
MessageBox(hWnd,"
开始游戏!
"
贪吃蛇"
MB_ICONINFORMATION);
SetTimer(hWnd,1,500,NULL);
Sleep(1000);
}
3.3.3.2关卡消息框
图6
为了增加游戏的趣味性和游戏难度,我给程序添加了关卡模块。
每一关中需要吃的食物,蛇的颜色和蛇移动的速度都会不同。
当玩家的分数到达下一关卡的标准时,弹出消息框供玩家选择是否开始下一关(图6)。
当玩家选择“是”时,开始下一关,蛇变颜色,速度加快。
当玩家选择“否”时,退出游戏。
if(score%50==0&
score/50==1)
{
KillTimer(hWnd,1);
if(IDYES==MessageBox(hWnd,"
开始第二关!
MB_YESNO|MB_ICONINFORMATION))
SetTimer(hWnd,1,400,NULL);
else
ReleaseDC(hWnd,hdc);
SendMessageW(hWnd,WM_DESTROY,NULL,NULL);
3.3.3.3结束消息框
当蛇碰到自身或者墙壁时,蛇死亡,snake.life=1,此时弹出消息框(图7),询问是否退出游戏,用户选择“是”,退出程序,用户选择“否”,则在画面上显示用户的最终得分,需要用户关闭窗口。
图7
if(IDYES==MessageBox(hWnd,"
你要退出游戏吗?
MB_YESNO|MB_ICONINFORMATION))
ReleaseDC(hWnd,hdc);
SendMessageW(hWnd,WM_DESTROY,NULL,NULL);
//创建一只无色的笔
hpenold=(HPEN)SelectObject(hdc,hpen