VC可视化课程设计MFC贪吃蛇.docx
- 文档编号:9016200
- 上传时间:2023-02-02
- 格式:DOCX
- 页数:35
- 大小:138.52KB
VC可视化课程设计MFC贪吃蛇.docx
《VC可视化课程设计MFC贪吃蛇.docx》由会员分享,可在线阅读,更多相关《VC可视化课程设计MFC贪吃蛇.docx(35页珍藏版)》请在冰豆网上搜索。
VC可视化课程设计MFC贪吃蛇
课程名称
可视化编程(VC)
学院
计算机科学与技术学院
专业
软件工程专业
班级
姓名
指导教师
2012——2013学年第2学期
目录
1、课程设计要求-----------------------------------------------------2
2、系统功能实现与操作--------------------------------------------2
3、系统设计概述---------------------------------------------------2
3.1添加结构体说明---------------------------------------------2
3.2模块说明------------------------------------------------------3
3.3主要功能函数代码及注释---------------------------------3
4、调试过程------------------------------------------------------------11
4.1添加功能说明----------------------------------------------11
4.2错误及解决方案-------------------------------------------11
5、程序运行结果--------------------------------------------------12
附:
主要类Cview的成员函数代码-----------------------------15
1、课程设计要求
题目:
基于MFC的贪吃蛇游戏设计与实现。
要求:
1.游戏界面设计
2.食物的初始化
3.游戏开始以及具体实现
2、系统功能实现与操作
本系统运用API函数的相关功能进行界面设计和功能实现。
通过Rectangle()函数绘制操作区域及障碍物。
通过连续的小矩形实现蛇身的绘制。
通过srand()函数和
rand()函数生成随机数,完成生成食物功能的实现。
综合运用多种if条件判断和for循环的嵌套使用的算法完成各种条件判断。
该系统功能简单,操作简易。
打开程序后,点击菜单栏的游戏按钮,点击开始游戏,系统弹出对话框,提示还有3秒开始。
游戏开始后,贪吃蛇开始移动,通过操作键盘上的上下左右方向键控制蛇的移动,躲避障碍物并吃掉食物。
当吃掉的食物累计到达一定数量后,蛇行速度加快。
直至撞到边界或障碍物或撞到自身,游戏结束。
此时弹出对话框,提示所得分数。
游戏中途可通过暂停和继续按钮执行相应操作。
3、系统设计概述
3.1添加结构体说明
structSnake
{
intx,y;
intlen;
intdirect;
}Snake[50];
structFood
{
intx;
inty;
intfood;
}Food;
Snake结构体数组用于存放贪吃蛇的相关数据,其中x,y存放当前节点矩形左上角的坐标,(Snake[0]为蛇头),len存放当前蛇身的长度,direct存放蛇的行进方向,可在1~4取值。
Food结构体用于存放食物的位置信息。
其中x,y存放食物小矩形左上角左标,food存放食物存在的布尔判断值。
3.2模块说明
系统代码部分主要分为三个模块,界面控制模块、初始化模块和功能模块。
界面控制模块包括函数:
OnStart(),OnPause(),OnContinue(),OnExit()
初始化模块包括函数:
OnInitialUpdate(),OnDraw(),oninit()
功能模块包括函数:
OnKeyDown(),OnTimer()
3.3主要功能函数代码及注释
①OnStart()
voidCSnakeView:
:
OnStart()
{
//TODO:
Addyourcommandhandlercodehere
SetTimer(1,3000,NULL);
AfxMessageBox("还有3秒开始~~!
");
}
点击开始游戏触发,设置定时器控制三秒后开始游戏。
②OnPause()
voidCSnakeView:
:
OnPause()
{
//TODO:
Addyourcommandhandlercodehere
KillTimer
(1);
AfxMessageBox("不要让人家等太久");
}
关闭定时器,暂停游戏
③OnContinue()
voidCSnakeView:
:
OnContinue()
{
//TODO:
Addyourcommandhandlercodehere
SetTimer(1,10,NULL);
}
重启定时器,恢复游戏
④OnExit()
voidCSnakeView:
:
OnExit()
{
//TODO:
Addyourcommandhandlercodehere
AfxMessageBox("欢迎下次再来!
");
exit(0);
}
弹出对话框提示退出。
⑤OnInitialUpdate()
voidCSnakeView:
:
OnInitialUpdate()
{
CView:
:
OnInitialUpdate();
//TODO:
Addyourspecializedcodehereand/orcallthebaseclass
Snake[0].x=10;
Snake[0].y=10;
Snake[1].x=10;
Snake[1].y=10;
Snake[2].x=10;
Snake[2].y=10;
Snake[0].direct=2;
Snake[0].len=3;
Food.food=1;//初始化蛇的长度和位置
}
用这个函数进行蛇的初始化工作。
初始化贪吃蛇起初有3个节点,长度为3,起始坐标存入数组,食物为1无0有。
⑥OnDraw()
voidCSnakeView:
:
OnDraw(CDC*pDC)
{
CSnakeDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
//TODO:
adddrawcodefornativedatahere
CBrushbackBrush(RGB(111,111,111));
CBrush*pOldBrush=pDC->SelectObject(&backBrush);
CRectrect;
pDC->GetClipBox(&rect);
pDC->PatBlt(rect.left,rect.top,rect.Width(),rect.Height(),PATCOPY);
pDC->SelectObject(pOldBrush);
pDC->Rectangle(9,9,401,401);
CBrushzaBrush(RGB(111,111,111));
pDC->SelectObject(zaBrush);
for(intm=0;m<20;m++)
{
pDC->Rectangle(150,(100+m*10),160,(110+m*10));
}
for(intn=0;n<10;n++)
{
pDC->Rectangle((250+n*10),200,(260+n*10),210);
}
oninit();
}
窗口创建时架构调用的函数,通过此函数完成界面初始化。
创建灰色画刷选入设备环境,通过绘图函数绘制用户区。
用两个for循环逐次绘制矩形的算法绘制出场景中的障碍物。
⑦oninit()
voidCSnakeView:
:
oninit()
{
CDC*pDC=GetDC();
CBrushDrawBrush=(RGB(255,0,0));
CBrush*Drawbrush=pDC->SelectObject(&DrawBrush);
for(inti=0;i<=Snake[0].len-1;i++)
{
pDC->Rectangle(Snake[i].x*10,Snake[i].y*10,(Snake[i].x+1)*10,(Snake[i].y+1)*10);
}
pDC->SelectObject(DrawBrush);
//利用for循环绘制起始的蛇身
}
该函数为自己添加的成员函数,用于绘制开始时的蛇身(长度为3)。
⑧OnKeyDown()
voidCSnakeView:
:
OnKeyDown(UINTnChar,UINTnRepCnt,UINTnFlags)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
switch(nChar)
{
caseVK_UP:
if(Snake[0].direct!
=2)Snake[0].direct=1;break;
caseVK_DOWN:
if(Snake[0].direct!
=1)Snake[0].direct=2;break;
caseVK_LEFT:
if(Snake[0].direct!
=4)Snake[0].direct=3;break;
caseVK_RIGHT:
if(Snake[0].direct!
=3)Snake[0].direct=4;break;
}
//通过键盘设置转头方向
CView:
:
OnKeyDown(nChar,nRepCnt,nFlags);
CView:
:
OnKeyDown(nChar,nRepCnt,nFlags);
}
该函数为键盘消息的接收函数。
从键盘得到的上下左右的不同按键,会根据switch判断语句的case改变Snake。
direct的值,从而改变蛇的行进方向。
⑨OnTimer()
voidCSnakeView:
:
OnTimer(UINTnIDEvent)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
CDC*pDC=GetDC();
CStringsoure;
intj=0;
if(Snake[0].len==3)SetTimer(1,170,NULL);
if(Snake[0].len==9)SetTimer(1,130,NULL);
if(Snake[0].len==14)SetTimer(1,95,NULL);
if(Snake[0].len==18)SetTimer(1,60,NULL);
//根据蛇的长度进行速度的设定
soure.Format("得分:
%d!
",(Snake[0].len-3)*10);//设置每个食物100分
//撞墙判断
if((Snake[0].direct==3)&&Snake[0].x*10<=17||(Snake[0].direct==1)&&Snake[0].y*10<=17||(Snake[0].direct==4)&&Snake[0].x*10>=382||(Snake[0].direct==2)&&Snake[0].y*10>=382)
{
j=1;//用来控制直接结束当前函数
KillTimer
(1);
AfxMessageBox(soure);
}
//分四种情况撞障碍判断,k为循环控制标识------------------------------------
if(Snake[0].direct==3)
{
for(intk=0;k<20;k++)
{
if(Snake[0].x*10==160&&Snake[0].y*10==(100+k*10))
{
j=1;//用来控制直接结束当前函数
KillTimer
(1);
AfxMessageBox(soure);
}
}
if(Snake[0].x*10==350&&Snake[0].y*10==200)
{
j=1;//用来控制直接结束当前函数
KillTimer
(1);
AfxMessageBox(soure);
}
}
//--------------------------------
if(Snake[0].direct==2)
{
for(intk=0;k<10;k++)
{
if(Snake[0].x*10==(250+k*10)&&Snake[0].y*10==200)
{
j=1;//用来控制直接结束当前函数
KillTimer
(1);
AfxMessageBox(soure);
}
}
if(Snake[0].x*10==150&&Snake[0].y*10==90)
{
j=1;//用来控制直接结束当前函数
KillTimer
(1);
AfxMessageBox(soure);
}
}
//------------------------------
if(Snake[0].direct==1)
{
for(intk=0;k<10;k++)
{
if(Snake[0].x*10==(250+k*10)&&Snake[0].y*10==210)
{
j=1;//用来控制直接结束当前函数
KillTimer
(1);
AfxMessageBox(soure);
}
}
if(Snake[0].x*10==150&&Snake[0].y*10==300)
{
j=1;//用来控制直接结束当前函数
KillTimer
(1);
AfxMessageBox(soure);
}
}
//-------------------------
if(Snake[0].direct==4)
{
for(intk=0;k<20;k++)
{
if(Snake[0].x*10==140&&Snake[0].y*10==(100+k*10))
{
j=1;//用来控制直接结束当前函数
KillTimer
(1);
AfxMessageBox(soure);
}
}
if(Snake[0].x*10==240&&Snake[0].y*10==200)
{
j=1;//用来控制直接结束当前函数
KillTimer
(1);
AfxMessageBox(soure);
}
}
//----------------------------------------------------------------------------------
//撞蛇身判断
if(Snake[0].len>3)
{
for(intsn=Snake[0].len-1;sn>0;sn--)
{
if(Snake[0].x*10==Snake[sn].x*10&&Snake[0].y*10==Snake[sn].y*10)
{
j=1;
KillTimer
(1);
AfxMessageBox(soure);
}
}
}
//对尾节点用白笔涂掉
if(j!
=1)
{
pDC->SelectStockObject(WHITE_PEN);
pDC->Rectangle(Snake[Snake[0].len-1].x*10,Snake[Snake[0].len-1].y*10,(Snake[Snake[0].len-1].x+1)*10,(Snake[Snake[0].len-1].y+1)*10);
for(inti=Snake[0].len-1;i>0;i--)
{
Snake[i].x=Snake[i-1].x;
Snake[i].y=Snake[i-1].y;
}
}
//行走方向判断
if(Snake[0].direct==1)Snake[0].y--;
if(Snake[0].direct==2)Snake[0].y++;
if(Snake[0].direct==3)Snake[0].x--;
if(Snake[0].direct==4)Snake[0].x++;
pDC->SelectStockObject(BLACK_PEN);
CBrushDrawBrush=(RGB(255,0,0));
CBrush*Drawbrush=pDC->SelectObject(&DrawBrush);
if(j!
=1)pDC->Rectangle(Snake[0].x*10,Snake[0].y*10,(Snake[0].x+1)*10,(Snake[0].y+1)*10);
pDC->SelectObject(DrawBrush);
//判断吃食物的条件
if(Snake[0].x*10==Food.x*10&&Snake[0].y*10==Food.y*10)
{
Snake[0].len++;
Food.food=1;
Snake[Snake[0].len-1].x=Snake[Snake[0].len-2].x;
Snake[Snake[0].len-1].y=Snake[Snake[0].len-2].y;
}
//生成食物
into=1;//控制生成食物成功时跳出循环
if(Food.food==1)
{
srand((unsigned)time(NULL));
do
{
Food.x=rand()%40;
Food.y=rand()%40;
if(Food.x!
=0&&Food.y!
=0)//不能再区域外或障碍物中生成食物
{
for(intp=0;p<20;p++)
if(Food.x*10!
=150&&Food.y!
=(100+p*10))o=0;
if(o!
=1)
{
for(intq=0;q<10;q++)
if(Food.x*10!
=(250+q*10)&&Food.y!
=200)o=0;
}
}
}
while(o);
pDC->Rectangle(Food.x*10,Food.y*10,(Food.x+1)*10,(Food.y+1)*10);
Food.food=0;
}
CView:
:
OnTimer(nIDEvent);
}
主要功能函数,响应于定时器消息,用于实时改变游戏界面,是游戏功能实现的主要函数。
下面依次解释其中功能模块:
1、用四个if判断语句对蛇身长度进行判断,然后设置定时器间隔控制蛇行速度。
2、对撞墙进行判断,当蛇头的坐标企图越过系统边界并且其行进方向是冲着墙的方向时,判定撞墙,关闭定时器并且置程序结束标志j=1。
3、撞障碍物判断,同撞墙判断,对蛇的行进方向分上下左右四种情况进行判断,然后判断蛇头坐标是否企图越过障碍物。
若撞届则同样置程序结束标志j=1。
4、撞蛇身判断,此算法是if嵌套for循环嵌套if判断,对蛇头的坐标和舍身每一点的坐标进行一次比较,看是否有蛇头的坐标与蛇身坐标重合。
若有则判定自撞,置程序结束标志j=1。
5、蛇行进的实现。
蛇行进主要的算法是,每一次Timer消息,将蛇的蛇头的下一位置添加一个矩形,并且将蛇尾的小矩形用与背景相同颜色的矩形填充掉,完成删除。
通过一个for循环来实现。
6、行走方向判断,通过对Snake.direct进行判断,控制蛇头坐标在x,y方向上自加或自减,完成方向的设定。
7、判断吃食物的条件,若蛇头坐标与食物坐标重合,则吃食物。
置食物标识为1表示没有食物。
8、生成食物方法。
通过rand()函数产生随机数,通过计算得到相应食物的坐标。
对该坐标的可行性进行判断,若坐标在操作区外或在障碍物中,则舍弃,循环执行这一步知道得到可行坐标,绘制食物矩形。
4、调试过程
4.1添加功能说明
在此软件完成后,仅能实现定速度的无障碍的贪吃蛇游戏。
经过改进,添加了初始化的相应代码,修改了Timer()函数的相关功能代码,实现了速度根据食物个数可变,添加了障碍物。
4.2错误及解决方案
在编写程序的过程中遇到了诸多错误,除书写错误外,其他错误通过探讨和查阅资料的方式得到解决。
具体问题有以下几个:
①之前对MFC图形的相关类了解的比较少,仔细看书后学习了CDC类的相关知识,了解了在NFC编程中图形函数的使用方法。
②在遍写程序之前,不清楚在单文档MFC程序中,架构对各个类的功能函数的调用次序,所以不知道应该将初始化的相关内容写在哪一个函数中,经查阅资料得知,OnInitialUpdate是视图窗口完全建立后第一个被框架调用的函数。
框架在第一次调用OnDraw前会调用OnInitialUpdate,因此OnInitialUpdate是设置滚动视图的逻辑尺寸和映射模式的最合适的地方。
在时间上,两者先后顺序不同,构造函数生成本类的对象,但没有产生窗口,OnCreate后窗口产生,然后才是视图的OnInitialUpDate,一般在这里对视图的显示做初始化。
③在运行程序后,出现BUG,当蛇行至靠近障碍物或墙壁那一列时就判定了撞墙,导致蛇没有办法沿着游戏区域的边界行走,吃不到边上的食物。
仔细研究代码后,在撞界判断中对蛇头坐标的基础上加入了对蛇行方向的判断,解决了这一问题。
④在撞界结束游戏后,蛇仍然会向前走一步,走到边界的外面。
仔细研究代码后没有发现错误,和同学探讨后发现,在OnTimer()函数中,撞界后则弹出对话框,终止计时器,但程序还会将此函数执行完,所以蛇还会向前走一步。
于是在撞界判断中加入了程序结束标志,在撞界后立即结束程序不再执行任何语句。
解决了这一问题。
⑤在添加了障碍物之后食物生成在“墙”里。
于是在生成随机数的判定中,加入了对生成坐标在墙内的判断,解决了这一问题。
⑥在程序完成后,我还想完成脱机运行的功能,即在没有VC的计算机上也能够运行这个程序。
上网查阅了相关资料,了解到:
在编写MFC程序时,对基类的连接有静态和动态两种。
当选择静态连接时,系统会在编译组建时将用到的类封装在程序中,就能够离开编程环境运行了。
也就是MFC的EXE可执行文件分为Debug版和Release版。
于是这个问题也得到了解决。
最终,在不懈的努力下,程序终于得以完美运行了~
5、程序运行结果
附:
主要
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- VC 可视化 课程设计 MFC 贪吃