(完整版)mfc贪吃蛇
发布时间:2020-05-06 07:57:49
发布时间:2020-05-06 07:57:49
宿 迁 学 院
Windows程序设计
课程考核报告
班 级: 11计本2班
学 号:
姓 名:
得 分:
2013年12月 20 日
贪吃蛇
1.设计目的及要求
1.1设计目的及问题描述
贪吃蛇游戏经典,版本多样,娱乐性高,适合在闲暇时放松心神。并且可在多个平台上实现,程序简单易懂,非常适合MFC初学者学习制作。玩家通过按键上的上下左右四个键来控制蛇的方向移动,蛇头碰撞到食物则表示食物被蛇吃掉,这时蛇的身长长一节,接着出现食物,等待被蛇吃掉。在吃食物的过程中,蛇的运行速度会越来越快,从而增加了游戏难度,具有挑战性。如果蛇在移动过程中,撞到墙壁或者身体交叉蛇头撞到自己的身体游戏结束。
在贪吃蛇这款游戏设置和开发中,一方面考察了我们大一时候所学的C语言编程的算法运用,另一方面更是考察了我们大三这学期所学的在windows应用程序中编程的基础知识和MFC应用程序的基本知识,同时考察了我们面向对象程序设计的基本思路和方法的掌握情况。
1.2设计要求
设计要求使用MFC基础类库,完成题目任选可以是小游戏、管理系统、通信软件等。(题目需有一定规模)
1、必须用到所学的控件、对话框、菜单、消息处理、视图框架、图形等大部分知识
2、创建的项目名“姓名(拼音的首字母)”,需严格遵守,否则一定不及格。
3、若该程序涉及多个窗口则每个窗口对应的类名必须“姓名(拼音的首字母)”,需严格遵守,否则一定不及格。
1.3软件、硬件环境
软件环境:Visual C++ 6.0、Microsoft Windows 7;
硬件环境:笔记本电脑。
2.课程设计步骤
1、系统用户需求
贪吃蛇游戏以深入人心,版本多样,娱乐性高,适合大部分人在闲暇时放松心神。
2、需求分析
我设计的贪吃蛇游戏是基于单文档设置的,所以在我创立ZYT这个工程之后会由软件自动生产五个大类(CAboutDlg、CMainFrame、CZYTApp、CZYTDoc、CZYTView).
另外我在这款游戏设置中,我自己添加了如下几个十分重要的类:
CBeanzyt:这个类是用来对贪吃蛇游戏中食物的出现和消失以及再出现进行设置;
CGezyt:这个类是用来对贪吃蛇这款游戏中界面以及食物和蛇的设置进行编辑;
CSnakezyt:这个类是用来对蛇的绘制以及运动进行编辑
3、游戏设置功能设计
首先绘制所选课题详细的流程图,根据游戏所要实现的过程把每一个过程的动作进行解析,并将其变成相应的变量;
4、游戏功能的实现
这个过程是在编程环境下实现的,通过我自己对整个程序的解析,一步一步的将各个过程实现的代码输入编程软件,通过反复的调试和运行进行程序的检验和最终功能的实现。
5、编写实验报告
3. 课程设计内容
3.1 概要设计
3.1.1 程序总体框架描述
Y N
N Y
Y Y
N
3.1.2程序常用类声明和变量的声明
CBeanzyt类 | 用来实现有关豆子的所有功能 |
CGezyt类 | 用来实现游戏界面的设置以及蛇的运动环境 |
CSnakezyt类 | 用来实现蛇的所有功能 |
类型 | 变量名 |
CString | m_string |
CSnakezyt | snake |
CBeanzyt | bean |
bool | m_start |
bool | m_pause |
int | m_speed |
int | m_marks |
int | sign |
3.2主要技术
1、对于单文档设置界面大小功能的实现;
2、对于单文档中Menu的菜单删除、添加功能的实现;
3、对设置快捷键功能的实现;
4、对于Bitmap制做功能的实现;
5、代码的熟练运用。
3.3系统设计结果 (界面截图、操作流程)
1、打开VC++6.0,选中MFCAppWizard(exe),选路径如图:
然后选择单文档,在高级选项中,将窗口样式厚边框和最大化边框的勾勾去掉。
2、在Menu中设置游戏快捷键,并在Accelerator中实现快捷功能,最后对快捷键建立类向导
在OnStart()和OnPause()函数中添加如下代码:
void CZYTView::OnStart()
{
// TODO: Add your command handler code here
this->m_start=true;
SetTimer(1,200/m_speed,NULL);
}
void CZYTView::OnPause()
{
// TODO: Add your command handler code here
static t=1;
if(t%2==1)
{
this->m_pause=true;
}
else
{
this->m_pause=false;
}
t++;
}
3、接下来新建4个位图BIPMAP,
第一个IDB_BITMAP_zyt1用来设置游戏背景;
第二个IDB_BITMAP_zyt2用来设置蛇的头;
第三个IDB_BITMAP_zyt3用来设置蛇的身子;
第四个IDB_BITMAP_zyt4用来设置豆。
IDB_BITMAP_zyt1
IDB_BITMAP_zyt2
IDB_BITMAP_zyt3
IDB_BITMAP_zyt4
4、OnDraw函数中将背景设置成一个个小方框,代码如下:
//画背景
CDC dcMemory;
dcMemory.CreateCompatibleDC(pDC);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP_zyt1);
dcMemory.SelectObject(&bitmap);
int i,j;
for(i=0;i<=38;i++)
{
for(j=0;j<=38;j++)
{
pDC->StretchBlt(0+10*i,0+10*j,10,10,&dcMemory,0,0,48,48,SRCCOPY);
}
//边界线
bitmap.Detach();
CPen pen(PS_SOLID,1,RGB(0,0,0));
CPen *pOldPen=pDC->SelectObject(&pen);
pDC->MoveTo(0,390);
pDC->LineTo(390,390);
pDC->SelectObject(pOldPen);
调整游戏界面窗口,并改名字
cs.style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
cs.cx=400;
cs.cy=480;
cs.lpszName="周玉亭版贪吃蛇。。。一定吃到豆噢!";
5、画蛇
首先插入一个类CGezyt
class CGezyt
{
public:
CGezyt();
virtual ~CGezyt();
public:
int i;//小格所在的行数;
int j;//小格所在的列数;
};
6、再插入一个CSnakezyt类,其代码如下:
class CSnakezyt
{
public:
CSnakezyt();
virtual ~CSnakezyt();
public:
int n;//蛇身长度
CGezyt body[200];//蛇身数组
int direction;//蛇前进的方向,1代表向上,2代表向右,3代表向下,4代表向左
public:
CGezyt forwardUp(); //向上前进
CGezyt forwardRight();//向右前进
CGezyt forwardDown(); //向下前进
CGezyt forwardLeft(); //向左前进
};
7、在CSnakezyt类CSnakezyt()构造函数中写出蛇上下左右四个运动是body数组是怎么变化的
CGezyt CSnakezyt::forwardUp()
{
CGezyt ret=body[n-1];
for(int i=n-1;i>=1;i--)
{
body[i].i=body[i-1].i,body[i].j=body[i-1].j;
}
body[0].i--;
return ret;
}
CGezyt CSnakezyt::forwardRight()
{
CGezyt ret=body[n-1];
for(int i=n-1;i>=1;i--)
{
body[i].i=body[i-1].i,body[i].j=body[i-1].j;
}
body[0].j++;
return ret;
}
CGezyt CSnakezyt::forwardDown()
{
CGezyt ret=body[n-1];
for(int i=n-1;i>=1;i--)
{
body[i].i=body[i-1].i,body[i].j=body[i-1].j;
}
body[0].i++;
return ret;
}
CGezyt CSnakezyt::forwardLeft()
{
CGezyt ret=body[n-1];
for(int i=n-1;i>=1;i--)
{
body[i].i=body[i-1].i,body[i].j=body[i-1].j;
}
body[0].j--;
return ret;
}
8、在构造函数CViewZYT中初始化蛇
this->m_start=false;
this->m_pause=false;
snake.n=4,snake.direction=2;
snake.body[0].i=2,snake.body[0].j=5;
snake.body[1].i=2,snake.body[1].j=4;
snake.body[2].i=2,snake.body[2].j=3;
snake.body[3].i=2,snake.body[3].j=2;
9、在OnDraw函数中画蛇头和蛇身
//画蛇头
bitmap.LoadBitmap(IDB_BITMAP_zyt2);
dcMemory.SelectObject(&bitmap);
pDC->StretchBlt(0+10*snake.body[0].j,0+10*snake.body[0].i,10,10,&dcMemory,0,0,48,48,SRCCOPY);
bitmap.Detach();
//画蛇身
bitmap.LoadBitmap(IDB_BITMAP_zyt3);
dcMemory.SelectObject(&bitmap);
for(i=1;i
{
pDC->StretchBlt(0+10*snake.body[i].j,0+10*snake.body[i].i,10,10,&dcMemory,0,0,48,48,SRCCOPY);
}
bitmap.Detach();
10、在Onstart函数中设立一个计时器并在CylcView中添加计时函数OnTimer,其中的代码如下:
if(this->m_start==true && this->m_pause==false)
{
CGezyt tmp;
CClientDC dc(this);
CDC dcMemory;
dcMemory.CreateCompatibleDC(&dc);
CBitmap bitmap;
switch(snake.direction)
{
case 1:tmp=snake.forwardUp();
break;
case 2:tmp=snake.forwardRight();
break;
case 3:tmp=snake.forwardDown();
break;
case 4:tmp=snake.forwardLeft();
break;
}
if(ZytGameLose()==true)
{
m_marks=0;
m_speed=1;
m_start=false;
MessageBox("亲,你怎么撞上了呢?!\n要再接再厉哟!");
zytrestart();
}
if(ZytEatBean()==true)
{
snake.n++;
snake.body[snake.n-1].i=tmp.i;
snake.body[snake.n-1].j=tmp.j;
//初始化豆子
unsigned seed=time(NULL);
srand(seed);
do
{
bean.i=rand()%24;
bean.j=rand()%24;
}while(ZytBeanInSnake()==true);
bitmap.LoadBitmap(IDB_BITMAP_zyt4);
dcMemory.SelectObject(&bitmap);
dc.StretchBlt(0+10*bean.j,0+10*bean.i,10,10,&dcMemory,0,0,48,48,SRCCOPY);
bitmap.Detach();
//显示的得分
COLORREF clr=dc.SetTextColor(dc.GetBkColor());
dc.TextOut(100,400,m_string);
m_marks+=100;
CString m;
m.Format("%d",m_marks);
m_string="亲,您的得分是:"+m;
dc.SetTextColor(clr);
dc.TextOut(100,400,m_string);
}
bitmap.LoadBitmap(IDB_BITMAP_zyt1);
dcMemory.SelectObject(&bitmap);
dc.StretchBlt(0+10*tmp.j,0+10*tmp.i,10,10,&dcMemory,0,0,48,48,SRCCOPY);
bitmap.Detach();
//画蛇头
bitmap.LoadBitmap(IDB_BITMAP_zyt2);
dcMemory.SelectObject(&bitmap);
dc.StretchBlt(0+10*snake.body[0].j,0+10*snake.body[0].i,10,10,&dcMemory,0,0,48,48,SRCCOPY);
bitmap.Detach();
//画蛇身
bitmap.LoadBitmap(IDB_BITMAP_zyt3);
dcMemory.SelectObject(&bitmap);
for(int i=1;i
{
dc.StretchBlt(0+10*snake.body[i].j,0+10*snake.body[i].i,10,10,&dcMemory,0,0,48,48,SRCCOPY);
}
bitmap.Detach();
}
CView::OnTimer(nIDEvent);
}
对上面代码中加粗的添加成员函数:
添加ZytGameLose成员函数,其代码如下(撞上边框或撞上自己本身):
bool CZYTView::ZytGameLose()
{
if(snake.body[0].i==-1 || snake.body[0].i==39 || snake.body[0].j==-1
|| snake.body[0].j==39 || ZytHeadStrike()==true)
{
return true;
}
else
{
return false;
}
}
添加ZytHeadStrike成员函数,其代码如下:
bool CZYTView::ZytHeadStrike()
{
bool flag=false;
for(int i=1;i
{
if(snake.body[0].i==snake.body[i].i && snake.body[0].j==snake.body[i].j)
{
flag=true;
break;
}
}
return flag;
}
添加ZytEatBean成员函数,其代码如下:
bool CZYTView::ZytEatBean()
{
if(snake.body[0].i==bean.i && snake.body[0].j==bean.j)
{
//this->m_marks+=100;
return true;
}
else
{
return false;
}
}
11、为了蛇会转弯移动,先添加KEYDOWN函数
其代码如下:
void CZYTView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if(nChar==VK_UP && snake.direction!=3)
{
snake.direction=1;
}
if(nChar==VK_RIGHT && snake.direction!=4)
{
snake.direction=2;
}
if(nChar==VK_DOWN && snake.direction!=1)
{
snake.direction=3;
}
if(nChar==VK_LEFT && snake.direction!=2)
{
snake.direction=4;
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
12、下面对豆建立一个类
其代码如下:
class CBeanzyt
{
public:
CBeanzyt();
virtual ~CBeanzyt();
public:
int i;//豆子所在行
int j;//豆子所在列
};
13、在CylcView构造函数中初始化豆子
//初始化豆子
unsigned seed=time(NULL);
srand(seed);
do
{
bean.i=rand()%24;
bean.j=rand()%24;
}while(ZytBeanInSnake()==true);
对上段代码中ZytBeanInSnake构造成员函数,其代码如下:
bool CZYTView::ZytBeanInSnake()
{
bool flag=false;
for(int i=0;i
{
if(bean.i==snake.body[i].i && bean.j==snake.body[i].j)
{
flag=true;
break;
}
}
return flag;
}
14、在OnDraw函数中
//画豆子
bitmap.LoadBitmap(IDB_BITMAP_zyt4);
dcMemory.SelectObject(&bitmap);
pDC->StretchBlt(0+10*bean.j,0+10*bean.i,10,10,&dcMemory,0,0,48,48,SRCCOPY);
bitmap.Detach();
15、对游戏失败之后的游戏重启zytrestart进行设置,代码如下:
void CZYTView::zytrestart()
{
//初始化蛇
snake.n=4,snake.direction=2;
snake.body[0].i=2,snake.body[0].j=5;
snake.body[1].i=2,snake.body[1].j=4;
snake.body[2].i=2,snake.body[2].j=3;
snake.body[3].i=2,snake.body[3].j=2;
//初始化豆子
unsigned seed=time(NULL);
srand(seed);
do
{
bean.i=rand()%24;
bean.j=rand()%24;
}while(ZytBeanInSnake()==true);
Invalidate();
}
16、在界面上显示游戏分数代码如下(吃到一个豆就加100):
在CZYTView()函数中添加如下代码:
this->m_start=false;
this->m_pause=false;
this->m_speed=1;
this->m_marks=0;
CString m;
m.Format("%d",m_marks);
this->m_string="亲,您的得分是:"+m;
在OnDraw函数中添加:
//显示分数
pDC->TextOut(100,400,this->m_string);
在OnTimer函数中添加:
//显示的得分
COLORREF clr=dc.SetTextColor(dc.GetBkColor());
dc.TextOut(100,400,m_string);
m_marks+=100;
CString m;
m.Format("%d",m_marks);
m_string="亲,您的得分是:"+m;
dc.SetTextColor(clr);
dc.TextOut(100,400,m_string);
17、当游戏结束时分数变为0,在 zytrestart函数添加如下代码:
this->m_marks=0;
CString m;
m.Format("%d",m_marks);
this->m_string="您的得分是:"+m;
当吃到5个豆以后速度加快:
在OnTimer函数中添加如下代码:
if(m_marks%500==0 && m_marks!=0)//如果分数是500的则速度加快
{
m_speed++;
KillTimer(1);
SetTimer(1,200/m_speed,NULL);
}
18、最后设置一下游戏的模式:
在Menu中添加:
建立成员函数:
定义一个成员变量sign
在CZYTView()函数中给sign赋初值sign=0;
void CZYTView::OnLow()
{
// TODO: Add your command handler code here
sign=1;
}
void CZYTView::OnHigh()
{
// TODO: Add your command handler code here
sign=2;
}
在OnStart函数中添加如下代码:
if(sign==1)
{
this->m_start=true;
SetTimer(1,200/m_speed,NULL);
}
if(sign==2)
{
this->m_start=true;
SetTimer(2,100/m_speed,NULL);
}
再将OnTimer函数中原来的速度加快模式改为如下:
if(m_marks%500==0 && m_marks!=0)//如果分数是500的则速度加快
{
m_speed++;
KillTimer(1);
SetTimer(1,200/m_speed,NULL);
KillTimer(2);
SetTimer(2,100/m_speed,NULL);
}
运行结果:
4.设计总结
4.1遇到的问题及如何解决
1、 问题:有时候出现一些变量没有定义,例如error C2065: 'bean' : undeclared identifier、error C2228: left of '.i' must have class/struct/union type等
原因:相关函数的头文件没有定义
解决:在相关函数中定义头文件。
2、 问题:蛇运动区域与边框不匹配,造成蛇运动出边框或未到达边框而游戏结束。
原因:数据没有匹配好。
解决:重新对程序中的矩形框绘制函数数据进行修改,并修改蛇运动边界的判断条件。
3、 问题:食物出现在边框外,造成游戏无法进行。
原因:控制事物出现的随机函数出现问题,随机坐标超出蛇运动区域。
解决:重新计算边界条件,修改随机函数rand()%x中的x值。
4、问题:在最后在设置模式时,对于蛇的速度快慢的设置不是很顺利,速度变化有点大
原因:对相关函数和代码掌握的不是很好
解决:查找相关资料
4.2体会
这次MFC的课程考核,我选择了通过运用MFC程序设计来编写一个贪食蛇的程序。在编写程序之前,我信心满满,因为课上讲的还都挺好实现的,不过等到我自己去设置一个小程序时,我发现,我根本不知道从哪里下手,也突然觉得自己对MFC的知识也不够深入。因此,首先我查阅许多关于贪食蛇的资料,也在网上看了许多关于贪食蛇的算法。做好了准备之后,才开始写我的贪食蛇。但是,在写的过程中还是遇到了许多问题。首先我遇到的第一个问题是我怎么画出贪吃蛇的格子,后来找到相关资料,这个需要代码来实现。还有的问题是如何将存储贪食蛇节点的数组与显示相结合起来。在开始的两天,我一直没有能够解决这个问题,最后呢,我在网上查资料才找到一个比较好的解决方法。另外我还在程序设计的过程中碰到了如何让蛇身隔在每个给定的时间间隔内移动一个的问题,这也是通过和同学讨论解决的。以及这次课程考核通过完成贪食蛇游戏的程序设计,锻炼了我的动手能力,增加了我的实际编程经验,提高了我独立思考和解决问题的能力。这次考核收获颇丰啊!