软件综合设计项目报告书扫雷文档格式.docx
《软件综合设计项目报告书扫雷文档格式.docx》由会员分享,可在线阅读,更多相关《软件综合设计项目报告书扫雷文档格式.docx(42页珍藏版)》请在冰豆网上搜索。
但几乎每个电脑使用者都接触过它,并且深爱着这款小游戏。
扫雷游戏是比较经典的一款小游戏,实现它的方法很多,可以用很多不同算法和语言实现,如C,C++,VB,JAVA等。
该设计以VisualC++6.0为开发环境,设计并开发一款扫雷游戏,其功能类似于Windows操作系统自带的扫雷游戏。
该报告首先介绍了制作游戏的整体思路及整个游戏设计的流程规划,然后介绍了雷区的布置及地雷随机产生的实现方法;
重点介绍了在游戏过程中各事件的处理,其中又以鼠标事件和清除未靠近地雷区方块这两方面最为最要,鼠标事件是利用鼠标所发出的信息了解使用者的意图,进而做出相对应的动作。
关键字:
扫雷,VisualC++6.0,事件
1、需求分析
扫雷游戏的基本功能:
点击鼠标左键于未知区域,如果未知区域有雷,游戏停止,显示所有的地雷。
如果没雷,则显示周围雷数,如果周围没雷,则再查看周围八个区域是否有雷直到有雷为止并显示,这其实是一个递归过程。
点击鼠标右键于未知区域,则将其置为有雷而不管是否真的有雷。
可选择初、中、高三级并可自定义雷数和区域大小。
雷区上部左侧显示总雷数减被标明有雷区域的数目。
雷区上部中间位置显示一按钮用于开局和显示鼠标动作的结果。
雷区上部右侧显示扫雷的时间。
将雷全部扫清后,则显示一对话框将你的姓名记入排行榜。
2、概要设计
本设计实现类似于Windows操作系统自带的扫雷游戏。
在设计中,系统开发平台为Windows
XP,程序设计语言采用Visual
C++,程序运行平台为Windows
2000/XP。
在程序设计中,把整个雷区看成一个二维数组,把雷方块定义为具有所在雷区二维数组的行和列、当前状态、方块属性、历史状态的结构体。
整个游戏程序包括了布雷、扫雷过程和结果三个阶段,在处理鼠标响应事件中伴随着GDI绘图。
程序通过调试运行,实现了设计目标,能够同时满足扫雷游戏初学者和高手的需要。
流程规划大致上可以分为三个部分,分别为:
画面初始、游戏者按下第一个方块和为非地雷方块时展开。
画面初始时,以游戏者最后一次设定的地雷区大小为范围画出地雷区,但此时并未产生地雷。
当游戏者按下第一个方块时产生地雷资料并启动定时器,为何在游戏者按下第一个方块才产生地雷资料呢?
其主要的用意在于不要让游戏者第一次就踩到地雷,这样在某种程度上可以提高游戏者游玩的气氛。
接着就是如何判断按下的方块是非地雷时的处理,这也是整个游戏的技术核心,我们可以通过递归的观念来检查周边的方块是否含有地雷及是否继续往外翻开。
基于以上思路,绘制功能图如下:
图1.1扫雷游戏总体功能图
图1.2总体流程图
3、详细设计
扫雷游戏的开发主要包括两大部分:
一个部分是布雷,该部分主要将雷随机布置在游戏区域内,以避免出现相同的雷区布置地图。
另一部分是扫雷,该部分包括判断鼠标左键点击某区域该区域是否是雷,如果是雷该如何操作,如果不是雷该如何操作,鼠标右键点击某区域时如果判断该区域是雷则加以标记,如果不是雷也加以标记,以及当鼠标双击某区域时,判断与该区域相邻的其它8个区域是否是雷并做一个标记。
3.1游戏系统资源
扫雷游戏中用到了很多资源,比如菜单资源、位图资源、声音资源等。
3.1.1菜单资源
菜单资源主要用于游戏的控制及说明。
菜单资源只有两个菜单选项:
第1个菜单选项包括游戏控制的选项,第2个菜单选项包含游戏说明的选项,在资源视图中插入这两个菜单选项,如图所示。
这些菜单选项的功能将在游戏的主窗体对话框中实现。
图2:
游戏菜单选项图3:
帮助菜单选项
3.1.2位图资源
在该项目中,位图资源比较多,也正是这些丰富的位图资源才实现了扫雷丰富的功能,下面按功能介绍各个位图的资源。
(1)游戏状态按钮位图。
由于游戏可以处于彩色及黑白两色两种状态,因此也需要载入两种不同的状态按钮的位图资源,如图所示。
图4:
彩色状态按钮位图图5:
黑白色状态按钮位图
资源中的位图自上而下一次对应游戏的正常、胜利、失败、及方块的展开状态。
在游戏菜单中选择游戏的颜色状态。
(2)方块状态位图。
在主游戏区中每个方块都可能对应很多不同的状态,如初始状态、下压状态、数字状态、雷状态、炸雷状态、标识累状态、错误标志类状态、及位图状态。
因此方块状态位图设计如图所示。
图6:
方块彩色位图图7:
方块黑白色位图
(3)数字位图。
在游戏中需要提示累得剩余数量及用户游戏已用的时间。
为了美观,将数字设计成8段数码管的方式来显示,设计了两个数字位图,如图所示。
图8:
彩色数字位图图9:
黑白色数字位图
3.1.3声音资源
为了是游戏更有趣,该设计还为游戏添加声音资源,并根据游戏的不同状态播放这些资源,如图所示。
图10:
声音资源
当游戏开始后,开始计时,IDR_WAVE_CLOCK对应的声音资源将被播放,每个一秒钟播放一次,与时钟显示同步。
当游戏失败时,将播放IDR_WAVE_DEAD对应的声音资源。
当游戏胜利是将播放IDR_WAVE_VICTORY对应的声音资源。
3.2游戏初始化及游戏界面绘制
与游戏的功能相对应,所有的功能都对应这一个实现体。
如:
等级选择的功能对应着等级选择的实现、游戏交互的功能对应着游戏交互的实现。
游戏主要的功能基本都在主游戏窗体类实现。
该设计主游戏模块中实现的游戏界面的绘制、游戏初始化、用户交互、绘制游戏界面并初始化游戏。
但无论是何种操作,在进行游戏之前都需要初始化游戏数据、绘制游戏界面并初始化游戏,这是实现游戏的第一步。
以下为游戏的数据说明。
MINEWND*GetMine(longx,longy);
protected:
UINTm_uXNum;
//X方向小方块个数
UINTm_uYNum;
//Y方向小方块个数
UINTm_uMineNum;
//总的雷个数
intm_nLeaveNum;
//剩余的雷个数
UINTm_uSpendTime;
//游戏开始击到目前所花费的时间
UINTm_uGameState;
//游戏状态
UINTm_uTimer;
//定时器标识
UINTm_uNewState;
//当前选中的小方块的状态
UINTm_uLevel;
//当前游戏等级
UINTm_uPrimary;
//初级记录
UINTm_uSecond;
//中级记录
UINTm_uAdvance;
//高级记录
CStringm_szPrimary;
//初级记录保持者
CStringm_szSecond;
//中级记录保持者
CStringm_szAdvance;
//高级记录保持者
BOOLm_bLRBtnDown;
//是否为左右键同时按下
BOOLm_bClickBtn;
//左键按下的时候鼠标是否位于按钮区域内
BOOLm_bMarkful;
//是否能显示标记
BOOLm_bColorful;
//是否彩色显示
BOOLm_bSoundful;
//是否有声音
CMenu*m_pSubMenu;
//子菜单
CBitmapm_bmpMine;
//雷区背景图像
CBitmapm_bmpNumber;
//数字背景图像
CBitmapm_bmpButton;
//笑脸按钮背景图像
CBrushm_brsBG;
//背景画刷对象
COLORREFm_clrDark;
//各按钮的深色调
RECTm_rcClient;
//客户区域
UINTm_uBtnRect[3];
//按钮框区域坐标数组
UINTm_uBtnState;
//按钮状态
UINTm_uNumRect[3];
//数字框区域坐标数组(包括时间和雷个数)
UINTm_uShellRcX[2];
//内框以及边界的坐标X方向
UINTm_uShellRcY[2];
//内框以及边界的坐标Y方向
MINEWNDm_pMines[100][100];
//表示雷区内的所有小方块的二维数组
MINEWND*m_pNewMine;
//当前选中的小方块
MINEWND*m_pOldMine;
//上次选中的小方块
void*m_pSndDead;
//失败提示音
void*m_pSndVictory;
//胜利提示音
void*m_pSndClock;
//时钟提示音
};
这些定义在主游戏窗体类头文件中的所有变量可以分为8组,子变量声明的顺序是:
第1组用来声明表示主游戏区(方块)及累得信息;
第2组是画刷、按钮位图、数字位图及方块状态位图,都是用于绘制界面的元素;
第3组用于保存主游戏区、时间框、雷数框及笑脸按钮的坐标;
第4组用于记录各种游戏状态;
第5组用于标识当前游戏特效属性;
第6组用于记录英雄榜数据;
第7组是各种声音资源的指针;
第8组只有一个变量用于保存菜单选项的指针。
3.2.1初始化游戏变量
LoadWaveSrc()函数用于载入声音资源,实现如下:
voidCMineWnd:
:
LoadWaveSrc()//载入声音资源P397由OnMemuSound()函数调用
{
HMODULEhMdl;
hMdl=AfxGetResourceHandle();
HRSRChSrc;
hSrc=FindResource(hMdl,MAKEINTRESOURCE(IDR_WAVE_DEAD),_T("
WAVE"
));
m_pSndDead=LoadResource(hMdl,hSrc);
hSrc=FindResource(hMdl,MAKEINTRESOURCE(IDR_WAVE_VICTORY),_T("
m_pSndVictory=LoadResource(hMdl,hSrc);
hSrc=FindResource(hMdl,MAKEINTRESOURCE(IDR_WAVE_CLOCK),_T("
m_pSndClock=LoadResource(hMdl,hSrc);
}
·
与LoadWaveSrc()函数对应的还有FreeWaveSrc()函数,它实现了释放声音资源的功能,实现方法如下:
FreeWaveSrc()//释放声音资源由OnMemuSound()函数调用
if(m_pSndDead)
{
FreeResource(m_pSndDead);
m_pSndDead=NULL;
}
if(m_pSndVictory)
FreeResource(m_pSndVictory);
m_pSndVictory=NULL;
if(m_pSndClock)
FreeResource(m_pSndClock);
m_pSndClock=NULL;
SizeWindow()函数用来初始化窗体中重要部分的位置,实现如下:
SizeWindow(void)//调整窗体,用来初始化窗体中重要部分的位置
UINTuWidth=DEFAULT_FRAME_X+m_uXNum*MINE_WIDTH+
LINE_WIDTH_0*3+SIDE_WIDTH_0+SIDE_WIDTH_1;
UINTuHeight=DEFAULT_FRAME_Y+m_uYNum*MINE_HEIGHT+
LINE_WIDTH_0*3+SIDE_WIDTH_0*2+SIDE_WIDTH_1+SHELL_S_H;
SetWindowPos(&
wndTopMost,0,0,uWidth,uHeight,
SWP_NOZORDER|SWP_NOMOVE|SWP_NOCOPYBITS);
GetClientRect(&
m_rcClient);
m_uBtnRect[0]=m_rcClient.right/2-12;
m_uBtnRect[1]=m_rcClient.right/2-13;
m_uBtnRect[2]=m_rcClient.right/2+12;
m_uNumRect[0]=m_rcClient.right-55;
m_uNumRect[1]=m_rcClient.right-15;
m_uNumRect[2]=m_rcClient.right-54;
m_uShellRcX[0]=m_rcClient.right;
m_uShellRcX[1]=m_rcClient.right-14;
m_uShellRcY[0]=m_rcClient.bottom;
m_uShellRcY[1]=m_rcClient.bottom-SHELL_L_START_Y-5;
最后还要重载窗体的菜单初始化函数,根据上面初始化的数据,设置菜单选项的选中状态,实现如下:
OnInitMenu(CMenu*pMenu)//菜单初始化函数
CWnd:
OnInitMenu(pMenu);
if((m_pSubMenu=pMenu->
GetSubMenu(0))==0)
AfxMessageBox("
初始化菜单失败!
"
);
PostQuitMessage(0);
else
SetCheckedLevel();
//设置等级菜单选项的选中状态
SetCheckedMark();
SetCheckedColor();
//设置颜色菜单选项的选中状态
SetCheckedSound();
//设置声音菜单选项的选中状态
//SetCheckedCheat();
3.2.2初始化游戏
在窗体的初始化函数中调用了InitGame()函数,该函数用于初始化游戏,实现如下:
InitGame()//游戏的初始化
LoadBitmap();
//载入位图资源
m_nLeaveNum=m_uMineNum;
//剩余雷数
m_uSpendTime=0;
//花费的时间
m_uBtnState=BUTTON_NORMAL;
//状态按钮初始化为正常态
m_uGameState=GS_WAIT;
//游戏状态为等待状态
if(m_uTimer)//定时器已打开
KillTimer(ID_TIMER_EVENT);
//关闭定时器
m_uTimer=0;
//初始化时间
m_pNewMine=NULL;
//新单击的方块
m_pOldMine=NULL;
//上次单击的方块
FreeMines();
//将所有的方块初始化为原始状态
for(UINTi=0;
i<
m_uYNum;
i++)
for(UINTj=0;
j<
m_uXNum;
j++)
{
m_pMines[i][j].uRow=i;
m_pMines[i][j].uCol=j;
m_pMines[i][j].uState=STATE_NORMAL;
//正常态
m_pMines[i][j].uAttrib=ATTRIB_EMPTY;
m_pMines[i][j].uOldState=STATE_NORMAL;
}
LoadBitma()函数根据当前颜色状态为游戏载入不同的位图资源,实现如下:
LoadBitmap()
if(m_bColorful)//彩色位图模式
m_clrDark=COLOR_DARK_GRAY;
//灰色
m_bmpMine.DeleteObject();
m_bmpMine.LoadBitmap(IDB_MINE_COLOR);
//载入方块的彩色状态位图
m_bmpNumber.DeleteObject();
m_bmpNumber.LoadBitmap(IDB_NUM_COLOR);
//载入数字的彩色位图
m_bmpButton.DeleteObject();
m_bmpButton.LoadBitmap(IDB_BTN_COLOR);
//载入状态按钮的彩色位图
else//黑白色模式
m_clrDark=COLOR_BLACK;
m_bmpMine.LoadBitmap(IDB_MINE_GRAY);
//载入方块的黑白色状态位图
m_bmpNumber.LoadBitmap(IDB_NUM_GRAY);
//载入数字的黑白色位图
m_bmpButton.LoadBitmap(IDB_BTN_GRAY);
//载入状态按钮的黑白色位图
3.2.3绘制游戏界面
主窗体的OnPaint()函数实现绘制游戏界面的功能,利用双环冲的绘图方法实现实现界面闪屏的效果。
依次绘制状态按钮、雷数标识、时间记录、游戏边框及主游戏区,实现如下:
OnPaint()//实现绘制游戏界面的功能
CPaintDCdc(this);
//用以屏幕显示的设备
CDCdcMemory;
//内存设备
CBitmapbitmap;
if(!
dc.IsPrinting())
if(dcMemory.CreateCompatibleDC(&
dc))//与dc设备兼容
{
//使得bitmap与实际显示的设备兼容
if(bitmap.CreateCompatibleBitmap(&
dc,m_rcClient.right,m_rcClient.bottom))
{
//内存设备选择物件-位图
dcMemory.SelectObject(&
bitmap);
//绘制背景框
dcMemory.FillRect(&
m_rcClient,&
m_brsBG);
DrawButton((CPaintDC&
)dcMemory);
//笑脸按钮绘图
DrawNumber((CPaintDC&
//文字绘图(计时器文字和剩余雷数文字)
DrawShell((CPaintDC&
//3D效果外壳绘图
DrawMineArea((CPaintDC&
//雷区绘图
//将内存设备的内容拷贝到实际屏幕显示的设备
dc.BitBlt(m_rcClient.left,m_rcClient.top,m_rcClient.right,m_rcClient.bottom,&
dcMemory,0,0,SRCCOPY);
bitmap.DeleteObject();
//释放位图内存
}
DrawButton()函数实现了绘制状态按钮的功能。
其实状态按钮并不是真正按钮,它只是一个绘有表情及3D边框的一片区域,该片区域根据当前的游戏状态代表按钮上的表情,实现如下:
DrawButton(CPaintDC&
dc)
CDCcdc;
//内存设备
cdc.CreateCompatibleDC(&
dc);
//设备兼容性
cdc.SelectObject(m_bmpButton);
//载入状态按钮位图
dc.StretchBlt(m_uBtnRect[0],16,24,24,&
cdc,0,24*m_uBtnState,24,24,SRCCOPY);
//绘制状态按钮上的位图
//由于每个标识状态的位图高度是24,因此可以根据m_uBtnState来计算开始当前状态对应位图的高度
dc.Draw3dRect(m_uBtnRect[1],15,26,26,m_clrDark,m_clrDark);
//绘制3D边框实现按钮的效果
DrawNumber()函数实现绘制游戏当前剩余雷数,以及当前游戏时间的功能,它所绘制的雷数及时间随着游戏的进行不断地变化着,实现如下:
DrawNumber(CPaintDC