江苏科技大学经济管理学院
2011至20##学年第二学期管理信息系统课程设计报告
第二篇:基于MFC的贪吃蛇程序设计(课程设计) 报告
《MFC应用程序设计》
课程设计报告
题 目:
学 院:
专 业:
班 级:
姓 名:
学 号:
指导教师:
评阅成绩:
二0 年 月 日
目录
概述 …………………………………………………………………3
1. 设计目的………………………………………………………3
2. 功能描述………………………………………………………3
3. 大体思想………………………………………………………3
4. 程序框图………………………………………………………4
5. 主要知识点……………………………………………………5
6. 重要的MFC ……………………………………………………5
7. 数据结构………………………………………………………6
8. 主要函数………………………………………………………7
9. 代码实现………………………………………………………7
10. 运行界面………………………………………………………18
11. 总结……………………………………………………………19
12. 参考文献………………………………………………………20
概述
此次课程设计我利用MFC制作了一个贪吃蛇游戏,运行程序时窗口会自动最大化,游戏会根据贪吃蛇身体的长度自动增加难度,而且单机右键可以出现坐标。我还添加了bmp格式的图片作为游戏背景。
1 设计目的
⑴ 通过实践进一步领会程序设计的特点和应用,提高运用C++ 以及面向对象知识解决实际问题的能力,初步掌握MFC变成的基本功能。
⑵ 通过实践加强对程序设计语言课程知识点的理解和掌握,培养对课程知识综合运用能力、实际分析问题能力及编程能力,养成良好的编程习惯。
⑶ 通过实践掌握用C++ 编写面向对象的实用程序的设计方法,对面向对象方法和思想增加感性的认识;学会利用C++程序设计语言编写出一些短小、可靠的Windows实用程序,切实提高面向对象的程序设计能力,为后续的相关课程的学习打下基础。
2 功能描述
⑴ 运行程序,窗口会自动最大化,出现bmp格式图片的背景。
⑵ 点击“游戏->游戏开始”,出现围墙界面,一条蛇在密闭的围墙内,在围墙内随机出现一个食物,游戏开始。
⑶ 游戏开始后,界面右下角会出现游戏规则。
⑷ 游戏开始后,界面右下角会同步游戏得分。
⑸ 通过四个方向键控制蛇的运动方向,吃掉随机出现的豆子,若不小心碰到墙壁或者与自身相交,则该轮游戏结束。
⑹ 点击“游戏->游戏暂停”,暂停游戏。
⑺ 点击“游戏->游戏继续”,继续游戏。
⑻ 游戏会根据蛇身的长度自动调节难易程度。
⑼ 点击“游戏->游戏退出”,退出游戏。
⑽ 在界面中右键单击鼠标可以显示光标的当前坐标。
⑾ 单击帮助->游戏规则查看游戏规则。
3 大体思想
⑴ 定义蛇类和食物类,初始化贪吃蛇各项成员变量,包括图像的出现在屏幕的初始位置,长度,以及蛇的行走方向。食物类的定义包括出现的初始位置,以及食物是否被吃掉的判断。
⑵ 用数组初始化长度为3的贪吃蛇,并且默认食物未出现
⑶ 在CView类上运用MFC提供的Windows消息中WM_TIMER消息,运用OnTimer()函数让系统提供一个时钟节拍,更新游戏
⑷ 具体游戏实现,包括蛇撞到自己和围墙都将使游戏结束,判断吃豆等,其中还包括根据蛇的长度来进行游戏难度的改变。
⑸ 具体键盘游戏操作运用到Windows消息响应中的WM_KEYDOWN,用OnKeyDown()来响应玩家的实际操作。
⑹ 界面会自动最大化
⑺ 而且单击右键可以出现坐标
⑻ 添加了bmp格式的图片作为游戏背景。
4 程序框图
4.1 整体框架图
4.2 游戏控制模块框架图
5 主要知识点
⑴ CView类中的消息响应
⑵ 控件的消息响应
⑶ 基于CView类内的具体游戏实现
⑷ 数组
⑸ 游戏图形的实现用CDC类实现
⑹ SDI界面窗口样式的设计
⑺ 文本的显示
6 重要的MFC
⑴ CWnd:窗口,它是大多数“看得见的东西”的父类(Windows里几乎所有看得见的东西都是一个窗口,大窗口里有许多小窗口),比如视图CView、框架窗口CFrameWnd、工具条CToolBar、对话框CDialog、按钮CButton,etc;一个例外是菜单(CMenu)不是从窗口派生的。该类很大,一开始也不必学,知道就行了。
⑵ CDocument文档,负责内存数据与磁盘的交互。最重要的是OnOpenDocument(读入),OnSaveDocument(写盘),Serialize(读写)
⑶ CView视图,负责内存数据与用户的交互。包括数据的显示、用户操作的响应(如菜单的选取、鼠标的响应)。最重要的是OnDraw(重画窗口),通常用CWnd::Invalidate()来启动它。另外,它通过消息映射表处理菜单、工具条、快捷键和其他用户消息。你自己的许多功能都要加在里面,你打交道最多的就是它。
⑷ CDC设备文本。无论是显示器还是打印机,都是画图给用户看。这图就抽象为CDC。CDC与其他GDI(图形设备接口)一起,完成文字和图形、图像的显示工作。把CDC想象成一张纸,每个窗口都有一个CDC相联系,负责画窗口。CDC有个常用子类CClientDC(窗口客户区),画图通常通过CClientDC完成。
⑸ CDialog对话框
⑹ CWinApp应用程序类。似于C中的main函数,是程序执行的入口和管理者,负责程序建立、消灭,主窗口和文档模板的建立。最常用函数InitInstance():初始化。
⑺ CGdiObject及子类,用于向设备文本画图。它们都需要在使用前选进DC
⑻ CPen笔,画线
⑼ CBrush刷子,填充
⑽ CFont字体,控制文字输出的字体
⑾ CBitmap位图
⑿ CPalette调色板
⒀ CRgn区域,指定一块区域可以用于做特殊处理。
⒁ CFile文件。最重要的不外是Open(打开),Read(读入),Write
⒂ CString字符串。封装了C中的字符数组,非常实用。
⒃ CPoint点,就是(x,y)
CRect矩形,就是(left,top,right,bottom)
7 数据结构
7.1 蛇的全局变量
struct Snake
{
int x,y;
int len;
int direct;
}Snake[50];
7.2 食物的全局变量
struct Food
{
int x;
int y;
int isfood;
}Food;
8 主要函数
⑴ void CMFCexp8_4View::OnDraw(CDC* pDC)//显示背景图片
⑵ void CMFCexp8_4View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)//按照用户的按键来控制蛇的移动
⑶ void CMFCexp8_4View::OnRButtonDown(UINT nFlags, CPoint point)//用鼠标右键屏幕,就会马上显示当前位置的坐标信息
⑷ void CMFCexp8_4View::OnTimer(UINT nIDEvent)//根据蛇的长度来进行SetTimer()函数的定义,可以根据长度来进行游戏难度的设定。//撞界判断//蛇身相撞判断//抹掉蛇行走的痕迹//判断吃豆的条件,撞到就吃//如果食物被吃了 就生成
⑸ void CMFCexp8_4View::oninit()//进行贪吃蛇外观的初始化
⑹ void CMFCexp8_4View::OnStart()//开始游戏
⑺ void CMFCexp8_4View::OnPause()//游戏暂停
⑻ void CMFCexp8_4View::OnContinue()//继续游戏
⑼ void CMFCexp8_4View::OnExit()//退出游戏
⑽ void CMFCexp8_4View::OnInitialUpdate()//初始化蛇的位置
⑾ void CMFCexp8_4View::OnDrawNew()//第二层白色正方形背景以及右下角的文字
9 代码实现
9.1 添加Windows信息
⑴在已有工程下的“ClassView”中右键CView类添加以下Windows信息
① WM_KEYDOWN
② WM_RBUTTONDOWN
③ WM_TIMER
⑵右键CView类选择 “ADD Virtual Funciton”选OnInitialUpdate(),再者还要添加一个成员函数oninit()进行贪吃蛇外观的初始化
9.2 控件的设计
⑴ 我们可以点击“工作空间”的“ResourceView”进行控件的具体设计,设计游戏的一些控件来控制“游戏开始” “游戏结束” 和“游戏暂停”。
⑵ 右键其中的标题栏,点击属性,会得到一个菜单栏标题,我们建立“游戏”菜单栏标题。
⑶ 点击并且在已有控件中的列表中点击属性,进行“菜单项目属性”的设置。分别建立的属性“标明”与对应的ID有
游戏开始 IDM_START
游戏暂停 IDM_PAUSE
游戏继续 IDM_CONTINUE
游戏退出 IDM_EXIT
⑷ 成功设置ID之后,分别单击右键各项属性进行消息响应处理函数的生成,具体方法操作例子如下:
右键“游戏开始”》点击“类向导建立”》在Message Maps页面,在要进行消息响应的控件ID列表Object IDs中上选择对应的ID, 这里我们选择IDM_START,具体实现的环境是CView类,所以我们必须把“Class name”的默认“CMainFrame”改为“CView类”,并且在“Messages”类型设置中,用“COMMAND”设置为其为命令消息。其余各项也按照同理进行设置。
9.3 具体实现游戏
9.3.1 在文件开头处分别定义蛇和食物 的全局变量
//定义蛇的全局变量
struct Snake
{
int x,y;
int len;
int direct;
}Snake[50];
//定义食物的全局变量
struct Food
{
int x;
int y;
int isfood;
}Food;
9.3.2 初始化蛇的位置
void CMFCexp8_4View::OnInitialUpdate()
{
CView::OnInitialUpdate();
Snake[0].x=10;//初始化起点坐标
Snake[0].y=10;
Snake[1].x=11;
Snake[1].y=10;
Snake[2].x=12;
Snake[2].y=10;
Snake[0].direct=3;//初始化贪吃蛇起初有3个节点,长度为3
Snake[0].len=3;
Food.isfood=1;//食物默认为1无0有
}
OnInitialUpdate()的功能如下:
视图窗口完全建立后第一个被框架调用的函数。框架在第一次调用OnDraw前会调用OnInitialUpdate,因此OnInitialUpdate是设置滚动视图的逻辑尺寸和映射模式的最合适的地方。
时间上,两者先后顺序不同,构造函数生成本类的对象,但没有产生窗口,OnCreate后窗口产生,然后才是视图的OnInitialUpDate,一般在这里对视图的显示做初始化。简单点,就是ONCREATE只是产生VIEW的基本结构和变量而在OnInitialUpDate()中,主要初始化视图中控件等。对各个变量进行初始化操作,所以我们要用这个函数来进行贪吃蛇的初始化工作。
9.3.3 对OnKeyDown()具体添加代码
void CMFCexp8_4View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch(nChar)
{
//Snake[0]代表的是蛇头,对蛇头的方向Snake[0].direct进行判断
case VK_UP:if(Snake[0].direct!=2)Snake[0].direct=1;break;
//蛇头不等于“下”的时候,才能做出“上”的操作动作,否则则忽略用户“向上”的操作按键效果
case VK_DOWN:if(Snake[0].direct!=1)Snake[0].direct=2;break;
//蛇头不等于“上”的时候,才能做出“下”的操作动作,否则则忽略用户“向下”的操作按键效果
case VK_LEFT:if(Snake[0].direct!=4)Snake[0].direct=3;break;
//蛇头不等于“右”的时候,才能做出“左”的操作动作,否则则忽略用户“向左”的操作按键效果
case VK_RIGHT:if(Snake[0].direct!=3)Snake[0].direct=4;break;
//蛇头不等于“左”的时候,才能做出“右”的操作动作,否则则忽略用户“向右”的操作按键效果
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
9.3.4 对OnRButtonDown()具体添加代码
void CMFCexp8_4View::OnRButtonDown(UINT nFlags, CPoint point)
//这个函数功能是:用鼠标右键屏幕,就会马上显示当前位置的坐标信息
{
CString str;
str.Format("%d,%d",point.x,point.y);
AfxMessageBox(str);
CView::OnRButtonDown(nFlags, point);
}
代码说明:这个函数并不是本游戏中必要添加的函数,只是为了在后面游戏页面的设计的时候可以用鼠标右键屏幕了解大概的屏幕坐标信息,才特地做的。这个设备坐标系以用户区窗口左上角为(0 , 0)原点,X坐标向右为正,Y坐标向下为正。
9.3.5 对oninit()具体添加代码
//画一个矩形,用当前的画笔画矩形轮廓,用当前画刷进行填充
{
CDC *pDC=GetDC();
CBrush DrawBrush=(RGB(100,100,100));
CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);
for(int i=0;i<=Snake[0].len-1;i++)
pDC->Rectangle(Snake[i].x*20,Snake[i].y*20,(Snake[i].x+1)*20,(Snake[i].y+1)*20);
pDC->SelectObject(DrawBrush);
}
代码说明:利用Windows给我们提供的CDC类来进行画图,我们首先用一个指向CDC类的指针去接受与该窗口相关联的DC句柄,然后用定义画刷一个DrawBrush对象,并且用RGB(100,100,100)来给画刷初始化颜色。并且用SelectObject(&DrawBrush);函数把对象画刷选入到设备描述表中,用for循环依次把贪吃蛇的3个节点画出来。
void CMFCexp8_4View::oninit()
9.3.6 对控件添加具体代码
void CMFCexp8_4View::OnStart()
{
CMFCexp8_4View::OnDrawNew();
SetTimer(1,3000,NULL);
//定时器。3000是3000毫秒,即3秒
AfxMessageBox("3秒后开始游戏!");
}
void CMFCexp8_4View::OnPause()
{
KillTimer(1);
AfxMessageBox("暂停游戏...");
}
void CMFCexp8_4View::OnContinue()
{
SetTimer(1,10,NULL);
}
void CMFCexp8_4View::OnExit()
{
AfxMessageBox("退出游戏...");
exit(0);
}
代码说明:由于之前我们设定了WM_TIMER消息,我们能运用计时器功能。用WM_TIMER来设置定时器,SetTimer(m_hWnd,1,1000,NULL)是一个1秒触发一次的定时器,在MFC程序中SetTimer被封装在CWnd类中,调用就不用指定窗口句柄了,所以我们这里可以只去后3个参数写成SetTimer(1,10,NULL);其中1000为1秒。
9.3.7 对OnDraw()添加具体代码,实现出现背景图片
⑴ 在“Resource.h”中添加“#define IDB_MY_BITMAP 130”
⑵ 在“Res”中添加“qiche.bmp”
⑶ 在“MFCexp8—4.cpp”中添加“IDB_MY_BITMAP BITMAP DISCARDABLE "res\\qiche.bmp"”
⑷添加以下代码:
void CMFCexp8_4View::OnDraw(CDC* pDC)
//以res文件夹中的bmp格式图片为背景
{
CMFCexp8_4Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CDC MemDC;
MemDC.CreateCompatibleDC(NULL);
MemDC.SelectObject(pDoc->m_Bitmap);
pDC->BitBlt(0,0,pDoc->m_nWidth,pDoc->m_nHeight,&MemDC,0,0,SRCCOPY);
}
9.3.8 对OnDrawNew()添加具体代码
⑴ 在添加代码之前,在“MFCexp8_4View.h”中添加:
class CMFCexp8_4View : public CView
{
protected: // create from serialization only
CMFCexp8_4View();
DECLARE_DYNCREATE(CMFCexp8_4View)
// Attributes
public:
CMFCexp8_4Doc* GetDocument();
CClientDC* myDC;
CClientDC* my2DC;
// Operations
……
}
⑵ 对 “CClientDC* myDC”和“CClientDC* my2DC”建立类向导
int CMFCexp8_4View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
myDC =new CClientDC(this);
my2DC =new CClientDC(this);
return 0;
}
⑶ void CMFCexp8_4View::OnDrawNew()
{
RECT Rect;
Rect.left=Rect.top=20;
Rect.right=Rect.bottom =500;
CBrush Brush(RGB(255,255,255));
myDC->FillRect (&Rect,&Brush);
//画出第二层背景,一个白色正方形
my2DC->SetBkColor(RGB(0,0,0));
my2DC->SetTextColor(RGB(255,255,255));
my2DC->TextOut(750,400,"单击“游戏”,选择“开始游戏”");
my2DC->SetTextColor(RGB(255,0,255));
my2DC->TextOut(800,430,"请不要咬到自己或撞到围墙!");
//在界面右下角分别出现三行背景色为黑色的彩色文本
}
9.3.9 对OnTimer(UINT nIDEvent)添加具体代码
void CMFCexp8_4View::OnTimer(UINT nIDEvent)
{
CDC *pDC=GetDC();
CString soure;
if(Snake[0].len==2)SetTimer(1,370,NULL);
if(Snake[0].len==3)SetTimer(1,270,NULL);
if(Snake[0].len==6)SetTimer(1,200,NULL);
if(Snake[0].len==9)SetTimer(1,100,NULL);
//这段代码的作用是根据蛇的长度来进行SetTimer()函数的定义,可以根据长度来进行游戏难度的设定
//如上代码分别370ms 270ms 200ms 100ms进行一次新的移动。
soure.Format("当前得分:%d!",(Snake[0].len-3)*10);
my2DC->SetTextColor(RGB(0,255,0));
my2DC->TextOut(800,460,soure);
//撞界判断
if(Snake[0].x*20<=37||Snake[0].y*20<=37||Snake[0].x*20>=462||Snake[0].y*20>=462)
{
KillTimer(1);
AfxMessageBox(soure);
CMFCexp8_4View::OnExit();
}
//蛇身相撞判断
if(Snake[0].len>3)
for(int sn=Snake[0].len-1;sn>0;sn--)
//根据蛇长sn,进行sn次for sn-1次循环,判断蛇自己是否咬到了自己
{
if(Snake[0].x*20==Snake[sn].x*20&&Snake[0].y*20==Snake[sn].y*20)
{
KillTimer(1);
//止计时器,和之前的SetTimer()对应
AfxMessageBox(soure);
CMFCexp8_4View::OnExit();
}
}
///////////////////////////////////////////////////////////////////////////
pDC->SelectStockObject(WHITE_PEN);
//把白色的“PEN”选入设备进行画图
pDC->Rectangle(Snake[Snake[0].len-1].x*20,Snake[Snake[0].len-1].y*20,(Snake[Snake[0].len-1].x+1)*20,(Snake[Snake[0].len-1].y+1)*20);
//让它去画最后一个节点
for(int i=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++;
//“1234”本别代表“上下左右” x++,y++进行移动
//然后用选定画刷画出一个新节点的矩形作为头结点
pDC->SelectStockObject(BLACK_PEN);
CBrush DrawBrush=(RGB(255,0,0));//控制蛇和食物的颜色
CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);
pDC->Rectangle(Snake[0].x*20,Snake[0].y*20,(Snake[0].x+1)*20,(Snake[0].y+1)*20);
pDC->SelectObject(DrawBrush);
//判断吃豆的条件,撞到就吃
if(Snake[0].x*20==Food.x*20&&Snake[0].y*20==Food.y*20)
{
Snake[0].len++;
Food.isfood=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;
}
//如果食物被吃了 就生成
if(Food.isfood==1)
{
srand((unsigned)time(NULL));
//当食物被吃了,指定食物在小于70 和大于430的矩形区域内出现
do
{
for(int isfo=Snake[0].len-1;isfo>=0;isfo--)
if(Snake[0].x*20==Snake[isfo].x*20&&Snake[0].y*20==Snake[isfo].y*20)
{
Food.x=rand()%25;
Food.y=rand()%25;
}
}
while(Food.x*20<70||Food.y*20<70||Food.x*20>430||Food.y*20>430);
pDC->Rectangle(Food.x*20,Food.y*20,(Food.x+1)*20,(Food.y+1)*20);
Food.isfood=0;
//把食物的属性更改为“存在”
}
CView::OnTimer(nIDEvent);
}
10 运行界面
⑴ 运行程序后
⑵开始游戏时
11 总结
这次课程设计,我选择用MFC编写一个贪食蛇的程序。在编写程序之前,我只有很少的关于MFC的程序设计知识,因此,在刚开始,我遇到了很多困难。为此,我查阅了许多关于贪食蛇的资料,也在网上看了许多关于贪食蛇的算法。做好了准备之后,才开始写我的贪食蛇,并且加了很多附加功能。这次课程设计锻炼了我的动手能力。通过完成贪食蛇游戏的程序设计,累积了我的实际编程经验,也提高了我独立解决问题的能力。
12 参考文献
[1]. 欧阳志宏,MFC程序设计轻松入门,人民邮电出版社,北京,20##-4-1
[2]. 姚领田,MFC窗口程序设计,水利水电出版社,北京,20##-5-1
[3]. 侯俊杰,深入浅出MFC(第2版), 华中科技大学出版社,武汉,20##-1-2
[4].邹筝、康晓林、袁建州,Visual C++6.0实用教程,电子工业出版社,北京,20##-1-1
[5]. 任哲,MFC Windows 应用程序设计,清华大学出版社,北京,20##-9-1