当前位置:文档之家› 连连看游戏设计

连连看游戏设计

连连看游戏设计Company number:【0089WT-8898YT-W8CCB-BUUT-202108】中南林业科技大学《数据结构课程设计》报告必做题:连连看游戏选做题:姓名:学号:专业班级:软件工程1班学院:计算机与信息工程学院指导老师:签名:2017年 1 月10 日目录一、连连看问题问题描述该游戏对一堆图案中相同的图案进行配对,点击开始按钮后,要求在一定的时间内完成对所有的图片配对,玩家每次选择两个图形,如果两图形相同,且这两个图形之间存在少于2个拐角的连通路径,则可以消除这两个图形。

成功消除一对图片则计分板会增加10分,对应的时间增加3秒。

要求各类类型的图片个数为偶数个,途中若有玩家找不到可以匹配的图片时可以点击提示按钮,由系统提示出一对可以消除的图片,通过玩家的点击可以消除。

当所有的图片消除时,系统提示消息为“恭喜你,通关了”,如果没有在规定时间完成所有图片的配对,则系统提示为“游戏结束”。

基本要求(1)生成游戏初始局面;(2) 每次用户选择两个图形,如果图形能满足一定条件(如果两个图形一样,且这个两个图形直接存在少于 2个弯的拐角),则两个图形都能消掉。

给定具有相同图形的任意两个格子,我们需要寻找这两个格子之间在转弯少的情况下,经过格子数目少的路径。

如果这个优路径的转弯数目少于 2,则这个两个格子可以消去;(3)判断游戏是否结束。

如果所有图形全部消去,游戏结束;(4) 判断死锁,当游戏玩家不可能消去任意两个图像的时候,游戏进入“死锁”状态。

设计思想判断两个图形可以消除的条件是:两个图形必须相同,它们之间存在着0个拐角,或1个拐角,亦或是2个拐角可以互相连通消除,否则,消除不了。

广度优先搜索的具体实现:如果两个图形可以通过0个拐角连通,则从第一个选中的图片一次向右,向下,向左,向上搜索可以和当前选中图片消除的另一图片,当搜索到与之相同的图片时,则与之消去;图片布局刷新;如果两个图片之间存在1个拐角可以消去,则广度搜索从当前图片出发,向左,右上下一次寻找一个路径节点(没有图片的点),使得寻找到的路径节点可以与选中的图片一线连通,并且可以与第二次选中的图片一线连通,则可以判断为可消除图片;图片布局重新刷新;如果连个图片之间存在两个拐角可以连通,则分为两种情况:一种是两个图片在矩形区域的最外沿,则通过判断是否存在一条线使得在两待消除的图片周围一个单位,若存在则消去。

二是两图片在矩形区域内部,则在两待消除的图片水平方向和垂直方向上寻找另外两个中间点能使两中间点之间连通,并且其中一个中间点能和待消图片1一线连通,另一中间点能和待消图片2一线连通,如若找到这样的点,则判断两图片能消去,找不到则不能消去。

需求分析(1) 初始化游戏界面该部分主要由执行窗口创建函数及游戏地图加载函数来实现。

通过数据的初始化及游戏地图资源的加载为用户呈现一个游戏初始的界面。

(2) 图片的选择该功能主要由鼠标来完成。

在OnLButtonDown()事件函数中通过鼠标的点击事件选取所要消除的两个相同图片。

(3) 图形的判断与消除对于第(2)步所选的两个图片,对其连通性进行判断:如果两图片直线连通,则相互消去;如果两图片连接为一个拐点且相通,则相互消去;如果两图片连接为两个拐点且相通,则相互消去;否则,不能消去。

(4) 判断游戏是否死锁或结束如果所有的图片全部消去,则提示“游戏结束!”的信息。

当游戏玩家不可能在消去任意两个图片时,游戏进入死锁状态。

此时提示相关信息。

(5)游戏的提示当玩家找不到两个可以消去的函数时,可以通过点击提示按钮获取系统帮助,继续游戏。

无具体要求,当玩家第一次点击图片时,对应图片响应点击消息,图片变成红色底片,若第二次点击的图片能和第一次的图片连通,则同时消失,否则选中的图片为红色的底。

系统设计a.主要组成类:图1b.调试测试图4图5(1)在设计时没有要求去设计提示次数,所以在设计时没有设计点击提示次数的限制条件,玩家可以一直点击提示。

(2)还有分数是固定的,玩家完成所有图片的连接消除,所得的分数是一致的。

(3)为了提高算法的执行效率,鼠标点击图片的次序不同,可能导致出现本可以消除的一对图片不能消去,但解决方案很简单,就是颠倒鼠标点击的次序。

(4)待消除的两图片在矩形区域内部并且有两个拐角时,为了提高执行效率,附加了一些判断条件,导致代码量增多。

(1)根据实际玩家需求,对系统提示次数加以限制,比如最多可以提示5次之类的具体方案,或者没使用一次提示,则所得分为5分而不是10分。

(2)找一个良好的分数与时间的函数,使得玩家用时少的时候,所得分数相对要高一些。

程序界面图6核心程序清单(1)中//在重画函数中添加时间信息,关卡信息和路径线条,通过窗口重绘,来画路径和改变时间void CLLKDlg::OnPaint(){CPaintDC dc3(this);CWindowDC dc(this);CPen lPen(PS_SOLID, 2, RGB(222,211,140));//画笔的样式,宽度,颜色//设置字体颜色(&lPen);//选择画笔CFont font; //字体类CString str;(_T("剩余时间: %3d 秒"),m_time);//m_time 中存储剩余时间信息(100,_T("宋体")); //请求的的大小,取其1/10为其字体大小,这个函数提供了一种简单的方法来创建指定字体类型和字体大小//设置字体(&font);(RGB(222,211,140));(TRANSPARENT);(10,40,str); //显示时间//显示分数();(100,_T("宋体"));(&font);(RGB(201,186,131));/*m_score= m_time-30;*/(_T("分数:%d"),m_score);(RGB(255,255,255));(610,35,str);CWindowDC dc2(this);CPen pen(PS_SOLID, 3, RGB(161,23,21));(pen);//画出路径if (LLKButton::ms_firstBtn != NULL && LLKButton::ms_secondBtn != NULL) {//设置计时器,秒后擦除路径线条SetTimer(2,100,NULL);= LLKButton::ms_firstBtn-> * 50 + 45;= LLKButton::ms_firstBtn-> * 50 + 70;= LLKButton::ms_secondBtn-> * 50 + 45;= LLKButton::ms_secondBtn-> * 50 + 70;= LLKButton:: * 50 + 45;= LLKButton:: * 50 + 70;= LLKButton:: * 50 + 45;= LLKButton:: * 50 + 70;//无拐点if (LLKButton:: == -1){(pt1);(pt2);}//一个拐点else if (LLKButton:: == -1){(pt1);(pt3);(pt3);(pt2);}//两个拐点else{(pt1);(pt3);(pt3);(pt4);(pt4);(pt2);}}}//初始化地图void CLLKDlg::InitMap(int map[][MAXY]){int i,j;int x,y;int type;//随机数种子srand((unsigned int)time(NULL));//srand函数是随机数发生器的初始化函数。

原型:void srand(unsigned int seed);为了防止随机数每次重复,常常使用系统时间来初始化,即使用 time函数来获得系统时间,它的返回值为从 00:00:00 GMT,January 1, 1970 到现在所持续的秒数,然后将time_t型数据转化为(unsigned)型再传给srand函数,//map值表示图片类型,0表示没有图片for(i=0;i<MAXX;i++){for(j=0;j<MAXY;j++){map[i][j]=0;}}//map的最外层空出来,不放置图片for(i=1;i<MAXX-1;i++){for(j=1;j<MAXY-1;j++){if(map[i][j] != 0)continue;else{//保证了图片成对出现type=rand() % m_typeNum;//图片种类map[i][j]=type+1;do{x = rand()%(MAXX-2)+1;y = rand()%(MAXY-2)+1;}while(map[x][y]);map[x][y]=type+1;}}}}//根据map构造按钮void CLLKDlg::ShowMap(int map[][MAXY]){int i, j;CPoint p;CString str = _T("");//清除原有按钮for(i=0; i<(); i++)delete (LLKButton *)(i);();//删除所有btn组//添加新按钮for(i=1; i<=MAXX-2; i++)for(j=1; j<=MAXY-2; j++){= i;= j;//将按钮放入m_btnGroup指针数组中(new LLKButton(map[i][j], p));}//显示按钮for(i=0; i<(MAXX-2)*(MAXY-2); i++){LLKButton *btn = (LLKButton *)(i);//构造按钮的大小和位置btn->Create(str, WS_CHILD|BS_BITMAP|WS_VISIBLE,CRect(70+(i%(MAXY-2))*50, 70+(i/(MAXY-2))*50,120 +(i%(MAXY -2))*50, 120 +(i/(MAXY -2))*50), this,IDC_BLOCK+i);if(btn->m_ID)//如果为0则不显示{//尽量用绝对路径(_T("res\\%"), btn->m_ID);HBITMAP m_fkBmp = (HBITMAP)::LoadImage//VC中显示bmp要用到CBitmap类(AfxGetInstanceHandle(),str, IMAGE_BITMAP, 0, 0,LR_CREATEDIBSECTION|LR_LOADFROMFILE);//加载图片if(m_fkBmp == NULL)if (MessageBox (_T(" 缺少图片资源! "),_T(" 错误"),MB_ICONERROR|MB_OK) == IDOK){CDialog::OnCancel();return;}btn->SetBitmap(m_fkBmp);btn->ShowWindow(SW_SHOW);}elsebtn->ShowWindow(SW_HIDE);}}//是否通关了BOOL CLLKDlg::IsWin(void){//时间结束,没有过关if (m_time == 0){KillTimer(1);MessageBox(_T("Game Over !"),_T("时间结束"));m_time = 30;//清除桌面的按钮for(int i=0; i<(); i++)delete (LLKButton *)(i);();return FALSE;}for (int i = 0; i < MAXX; i++)for (int j = 0; j < MAXY; j++){if(map[i][j] != 0)return FALSE;}//过关后停止计时KillTimer(1);MessageBox(_T("恭喜你,已经通关"),_T("胜利"));m_score =m_time-30;return TRUE;}类//两按钮在同一条直线上BOOL LLKButton::FindLine(CPoint p1, CPoint p2){int max, min;int i;//在同一行if( == ){max = >:;min = <:;if(max == min+1) //相邻的两个格子return TRUE;for(i=min+1; i<max; i++){if(parent->map[][i] != 0)return FALSE;}return TRUE;}//在同一列if( == ){max = >:;min = <:;if(max == min+1)return TRUE; //相邻的两个格子for(i=min+1; i<max; i++){if(parent->map[i][] !=0)return FALSE;}return TRUE;}return FALSE;}//有一个拐点的路径BOOL LLKButton::FindOneConner(CPoint p1, CPoint p2){int maxx, maxy, minx, miny;maxx = >:;maxy = >:;minx = <:;miny = <:;//4个点分别进行判断,看能否找到到两个目标点的直线路径if(parent->map[minx][maxy] == 0){= minx;= maxy;if ((FindLine ( p1, ms_ptCross1)) && (FindLine(ms_ptCross1, p2))) return TRUE;}if(parent->map[minx][miny] == 0){= minx;= miny;if ((FindLine (p1, ms_ptCross1)) && (FindLine(ms_ptCross1, p2))) return TRUE;}if(parent->map[maxx][miny] == 0){= maxx;= miny;if ((FindLine (p1, ms_ptCross1)) && (FindLine(ms_ptCross1, p2))) return TRUE;}if(parent->map[maxx][maxy] == 0){= maxx;= maxy;if ((FindLine (p1, ms_ptCross1)) && (FindLine(ms_ptCross1, p2))) return TRUE;}return FALSE;}BOOL LLKButton::FindSide(CPoint p1, CPoint p2){int max, min;int i;BOOL line=TRUE;BOOL col=TRUE;if( == ){max = >:;min = <:;for(i=min; i<=max; i++) //上侧{if(parent->map[][i] !=0){line=FALSE;break;}}if(line){= ;= ;= ;= ;return TRUE;}elseline=TRUE;for(i=min; i<=max; i++){if(parent->map[+1][i] !=0) //下侧{line=FALSE;break;}}if(line){= +1;= ;= +1;= ;return TRUE;}}elseline=FALSE;if( == ){max = >:;min = <:;for(i=min; i<=max; i++){if(parent->map[i][] !=0) //左侧{col=FALSE;break;}}if(col){= ;= ;= ;= ;return TRUE;}elsecol=TRUE;for(i=min; i<=max; i++){if(parent->map[i][+1] !=0) //右侧{col=FALSE;break;}}if(col){= ;= +1;= ;= +1;return TRUE;}}elsecol=FALSE;if(line || col)return TRUE;elsereturn FALSE;}//两个拐点BOOL LLKButton::FindTwoConner(CPoint p1, CPoint p2){//两个拐点必定有一个坐标相同,另一个坐标分别跟两个结点相同int i;CPoint tempPoint1 = 0;CPoint tempPoint2 = 0;//在两个目标结点的水平线上寻找拐点if>={for(i=; i>=0; i--)//向左侧找{= ;= i;ms_ptCross1 = tempPoint1;if((parent->map[][]==0)){if(FindLine(tempPoint1, p1)){= ;= ;if((parent->map[][]==0)){if(FindLine(tempPoint1,tempPoint2)){ms_ptCross2 = tempPoint2;if(FindLine(tempPoint2, p2))return TRUE;}}}}}}if<={for(i=+1; i<=MAXY+1; i++)//向右侧找{= ;= i;ms_ptCross1 = tempPoint1;if((parent->map[][]==0)){if(FindLine(tempPoint1, p1)){= ;= ;if((parent->map[][]==0)){if(FindLine(tempPoint1,tempPoint2)){ms_ptCross2 = tempPoint2;if(FindLine(tempPoint2, p2))return TRUE;}}}}}}//在两个目标结点的垂直线上寻找拐点if>={for(i=; i>=0; i--)//上侧寻找{= i;= ;ms_ptCross1 = tempPoint1;if((parent->map[][]==0) ){if(FindLine(tempPoint1, p1)){= ;= ;if ((parent ->map [][]==0)){if(FindLine(tempPoint1,tempPoint2)){ms_ptCross2 = tempPoint2;if(FindLine(tempPoint2, p2))return TRUE;}}}}}}if<={for(i=; i<=MAXX+1; i++)//下侧寻找{= i;= ;ms_ptCross1 = tempPoint1;if((parent->map[][]==0) ){if(FindLine(tempPoint1, p1)){= ;= ;if ((parent ->map [][]==0)){if(FindLine(tempPoint1,tempPoint2)){ms_ptCross2 = tempPoint2;if(FindLine(tempPoint2, p2))return TRUE;}}}}}}return FALSE;}。

相关主题