首页 VC_实现连连看游戏

VC_实现连连看游戏

举报
开通vip

VC_实现连连看游戏 2010. 1 [3] J2ME 3D 手机游戏开发详解. 人民邮电出版社, 2007. [4] J2ME 手机游戏开发详解. 清华大学出版社, 2005. [5] J2ME 程序设计. 清华大学出版社, 2001.3. [6] Java 手机/PDA 程序设计入门. 电子工业出版社出版, 2004. [7] Java 2 实用教程. 清华大学出版社. [8] 3D MAX 8 入门与提高. 电子工业出版社. [9] 火星人 3D MAX 8. 人民邮电出版社. [10] Photoshop CS 中文版. ...

VC_实现连连看游戏
2010. 1 [3] J2ME 3D 手机游戏开发详解. 人民邮电出版社, 2007. [4] J2ME 手机游戏开发详解. 清华大学出版社, 2005. [5] J2ME 程序 设计 领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计 . 清华大学出版社, 2001.3. [6] Java 手机/PDA 程序设计入门. 电子工业出版社出版, 2004. [7] Java 2 实用教程. 清华大学出版社. [8] 3D MAX 8 入门与提高. 电子工业出版社. [9] 火星人 3D MAX 8. 人民邮电出版社. [10] Photoshop CS 中文版. 电子工业出版社出版, 2005. (收稿日期: 2009-09-25) 相信大家都对 QQ 游戏中的连连看不陌生, 那漂亮的图片 以及紧张的节奏, 使很多人都爱不释手。 本文利用 VC++, 借 助 MFC 实现一个简单的连连看游戏。 1 基本思路 首先需要对连连看游戏有一个整体的认识, 它是在一个背 景下有若干的图片, 如果连续点击相同的图片, 并且可以用最 多两折的线段连接, 之间还没有其他障碍物, 就可以消掉这一 对图片, 如果所有的图片都被消掉, 就可取得胜利。 倘若在规 定的时间内没有消完所有图片, 则游戏失败。 游戏界面如图 1 所示。 这其中需要注意几个问题。 首先, 必须是随机生成的, 也 就是说每次游戏生成的画面都不能一样。 其次, 每个小图片都 应该是成对出现的, 否则无法将它们都消掉, 游戏也就无法完 成了。 最后, 程序应该有重新排序的功能, 在玩家找不到可消 的图片, 或者程序判断出无可消的图片时, 可以重新排序, 以 便游戏可以继续进行。 为了游戏的美观, 当点击每个图片时, 应该有类似按钮点 击的效果。 要实现这一点, 最简单的就是把每个图片都做成一 个按钮, 这可以利用 MFC 的 CButton 类, 编写 CLLKanButton 类, 使其继承于 Cbutton 类, 使每个 Button 都有自己的 ID 号, 用来存储图片类型, 还应该有一个 CPoint 类型的成员变量, 用 来存储每个 Button 的位置信息, 最后在创建时, 将和 ID 号对 应的图片贴到相应的位置上即可。 通过这样的思路来保证每种图片都是成对出现的: 首先定 位到第一个按钮的位置, 随机选择一种图片, 生成这个按钮, 然后随机选择一个位置, 仍然放置这种图片, 再定位到第二个 按钮的位置, 随机选择一种图片, 生成这个按钮, 然后再随机 选择一个位置, 如果这个位置已经有按钮了, 则重新随机选择 一个位置, 直到选到一个空的位置, 放置相同的图片, 以此类 推, 将整个地图布完。 将地图布完后就是游戏的过程了。 根据大家熟悉的连连看 规则, 两个相同的图片如果可以用不超过 3 根的折线将其相 连, 就可以消掉这一对图片。 关于具体算法的实现, 在后面 “游戏核心实现” 一章中做详细讲解。 2 游戏初始化以及地图生成 编程环境是 Microsoft Visual C++ 6.0。 首先新建一个工程, 选择 MFC AppWizard, 取名 “LLKan”, 建立一个基于对话框的 工程, 可以根据需要选择共享 DLL 模式或静态 DLL 模式, 这 里直接点击完成, 如图 2 所示。 现在工程中已经有了 3 个类: CLLKanApp 类, CLLKanDlg VC++实现连连看游戏 袁 伟 摘 要: 用 VC++实现连连看游戏, 可自由设置游戏时间及难度, 加入秘籍功能, 并自动消除图 片。 关键词: VC++; MFC; 连连看 图 1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 78 2010. 1 图 2 类以及 CaboutDlg 类。 其中的 CLLKanDlg 类就是主对话框类, 可以在此类中做一些基本的设定。 首先利用 SetWindowPos () 函数来设置框架的大小及基本属性 。 在 CLLKanDlg 类中的 OnInitDialog () 函数中, 在如下位置添加代码: CDialog::OnInitDialog(); SetWindowPos(NULL,100,100,750,500, SWP_DRAWFRAME | SWP_SHOWWINDOW | SWP_NOZORDER); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command //range. 为了显示剩余时间, 创建一个矩形区域 CRgn m_timeRgn, 在这个区域内显示剩余时间, 利用 InvalidateRgn () 函数刷新 这一区域 , 达到实时显示时间的目的 。 在 CLLKanDlg 类的 OnPaint () 函数中, 将原代码注释掉, 添加如下代码: CPaintDC dc(this); CPen *pRedPen = new CPen; pRedPen->CreatePen(PS_SOLID, 2, RGB(255,0,0)); //设置字体颜色 CGdiObject *pOldPen = dc.SelectObject(pRedPen); //选择画笔 CFont font; CString str; str.Format("剩余时间: %d 秒",m_time); //m_time 中存储剩余时间信息 font.CreatePointFont(100,"宋体"); //设置字体 dc.SelectObject(&font); dc.SetTextColor(RGB(255,0,0)); dc.SetBkColor(TRANSPARENT); //设置背景为透明 dc.TextOut(10,40,str); //显示时间 其中, 第一部分代码设置字体颜色为红色 , 第二部分显 示剩余时间, m_time 存储时间信息。 在 CLLKanApp 类的 InitInstance () 函数中添加如下代码, 将 背景色设置为黑色: SetDialogBkColor(RGB(0,0,0),RGB(255,0,0)); //设置背景色为黑色 现在来生成游戏地图。 首先为 CLLKanDlg 类添加如下成 员变量: int m_typeNum; //图片种类数 int map[MAXX][MAXY]; //地图数组,存储图片类型 CPtrArray m_btnGroup; //Button 组 CLineStatic m_line; //用于画线 其中 MAXX 和 MAXY 做如下定义: #define MAXX 9 //行数 #define MAXY14 //列数 这里需要注意一点, MAXX 和 MAXY 的积一定要是个偶 数, 因为如果为奇数, 则不能成对的容纳所有的图片。 然后在 CLLKanDlg 类中添加成员函数 InitMap (int map [] [MAXY]), 源代码如下: void CLLKanDlg::InitMap(int map[][MAXY]) { int i,j; int x,y; int type; //随机数种子 srand((unsigned int)time(NULL)); for(i=0;iCreate(str, WS_CHILD|BS_BITMAP, CRect(130+(i%(MAXY-2))*40, 70+(i/(MAXY-2))*40, 170+(i%(MAXY-2))*40, 110+(i/(MAXY-2))*40), this, IDC_BLOCK+i); if(btn->m_id)//如果为 0 则不显示 { str.Format("res\\%d.bmp", btn->m_id); HBITMAP m_fkBmp = (HBITMAP)::LoadImage (AfxGetInstanceHandle(), str, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION|LR_LOADFROMFILE); //加载图片 if(m_fkBmp == NULL) if (MessageBox (" 缺少图片资源 ! ", " 错误 ", MB_ICONERROR|MB_OK) == IDOK) { CDialog::OnCancel(); return; } btn->SetBitmap(m_fkBmp); btn->ShowWindow(SW_SHOW); } else btn->ShowWindow(SW_HIDE); } } 函数首先清除原按钮, 这是必须的。 如果不做这一步, 当 玩完一次游戏再重新开始时, 由于没有清除原按钮, 就会出现 错误。 然后添加新按钮, 用 new 的 方法 快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载 将新按钮一个一个地贴 到地图中。 选用的图片是 QQ 头像, 共 105 张, 都存放在工程 目录下的 res 文件夹中, 如果读取图片失败, 则会弹出警告对 话框。 通过设置 m_typeNum 的值来调整游戏难度, 图片类型 越多则越难。 至此, 已经完成了整个游戏的初始化的工作, 在菜单栏中 加入游戏开始菜单, 来调用 InitMap () 函数和 ShowMap () 函 数, 就可以看到游戏基本的样子了。 当然, 还可以加入定时 器, 来控制游戏的进程, 如果设定的时间结束还没有消掉所有 的图片, 游戏就失败了。 此外还可以在菜单栏中加入设定游戏 时间和图片种类的功能, 使得游戏有更高的可玩性。 关于这 些, 这里不再做详细介绍。 3 游戏核心实现 可以做简要 分析 定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析 , 对于消图来说, 可以分为如下 3 种情 况: (1) 直连方式 (2) 有一个折点的线相连 (3) 有两个折点的线相连 对于第一种方式, 判断起来比较简单, 只要两图片相同, 在一条直线上, 并且之间没有障碍物即可。 如图 3 所示。 为 CLLKanButton 类添加 FindLine (CPoint p1, CPoint p2) 函数, 实现判断是否可直连的功能。 源代码如下: 80 2010. 1 图 4 BOOL CLLKanButton::FindLine(CPoint p1,CPoint p2) { CLLKanDlg *parent = (CLLKanDlg *)GetParent(); int max, min; int i; if( (p1.x) == (p2.x) ) { max = (p1.y>p2.y)?p1.y:p2.y; min = (p1.ymap[p1.x][i]! =0) return FALSE; } return TRUE; } if( (p1.y) == (p2.y) ) { max = (p1.x>p2.x)?p1.x:p2.x; min = (p1.xmap[i][p1.y]! =0) return FALSE; } return TRUE; } return FALSE; } 其中, p1, p2 分别保存有先后两次点击图片的位置信息。 为了能够得到这些信息, 可以先在 CLLKanButton 类中为鼠标 左键按下添加消息响应。 由于每个图片都是一个 Button, 因此 在鼠标点击这个 Button 时 , 都会有一个 this 指针指向这个 Button, 可以利用这个 this 指针, 将 Button 的类型信息、 位置 信息都分别保存下来。 只当先后点击的是同一种图片时, 才调 用 FindLine 等函数来判断是否能消掉, 这样来提高程序的运行 效率。 函数是这样运行的, 首先要分为横向连接和纵向连接两种 类型分别判断。 以横向为例, 谁的横坐标值小, 谁就在左边, 由此得到它们大致的位置信息。 然后从左至右, 依次判断它们 之间是否有其他的图片, 如果有, 则返回 FALSE; 如果没有, 则 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 示可以通过直连的方式, 消掉这一对图片。 对于第二种方式, 判断起来也相对简单, 连接它们的折线 的折点, 其横纵坐标一定分别和它们的横纵坐标相同。 如图 4 所示。 为 CLLKanButton 类 添 加 FindOneConner ( CPoint p1, CPoint p2) 函数, 实现判断是否可通过一个折点相连的功能。 源代码如下: BOOL CLLKanButton::FindOneConner (CPoint p1,CPoint p2) { CLLKanDlg *parent = (CLLKanDlg *)GetParent(); int maxx, maxy, minx, miny; maxx = (p1.x>p2.x)?p1.x:p2.x; maxy = (p1.y>p2.y)?p1.y:p2.y; minx = (p1.xmap[minx][maxy] == 0) { m_ptCross1.x = minx; m_ptCross1.y = maxy; if ((FindLine ( p1, m_ptCross1)) && (FindLine (m_ptCross1, p2))) return TRUE; } if(parent->map[maxx][miny] == 0) { m_ptCross1.x = maxx; m_ptCross1.y = miny; if ((FindLine (p1, m_ptCross1)) && (FindLine (m_ptCross1, p2))) return TRUE; } if(parent->map[minx][miny] == 0) { m_ptCross1.x = minx; m_ptCross1.y = miny; if ((FindLine (p1, m_ptCross1)) && (FindLine (m_ptCross1, p2))) return TRUE; } if(parent->map[maxx][maxy] == 0) { m_ptCross1.x = maxx; m_ptCross1.y = maxy; if ((FindLine (p1, m_ptCross1)) && (FindLine (m_ptCross1, p2))) 81 2010. 1 return TRUE; } return FALSE; } 折点的横纵坐标一定分别为其中一个图片的横坐标以及另 外一个点的纵坐标, 由此, 就可以定为出折点的位置, 值得注 意的是, 这里可以利用已经写好的 FindLine 函数, 来简化判断 过程。 对于第 3 种方式, 可以做进一步细分。 经常玩连连看的朋 友一定知道这样的规律, 在游戏进行的初期, 通常是先在某一 边来找相同的图片。 因为在游戏初期, 地图还比较完整, 即使 找到了相同的图片, 由于中间有障碍物, 也不能消掉, 而找到 在一侧的相同图片, 是不错的突破口, 如图 5 所示。 特意编写 了判断这样连接方式的函数, 力求提高程序运行效率。 为 CLLKanButton 类添加 FindSide (CPoint p1, CPoint p2) 函数, 来实现判断是否可通过一侧相连的功能, 源代码如下: BOOL CLLKanButton::FindSide(CPoint p1,CPoint p2) { CLLKanDlg *parent = (CLLKanDlg *)GetParent(); int max, min; int i; BOOL line=TRUE; BOOL col=TRUE; if( (p1.x) == (p2.x) ) { max = (p1.y>p2.y)?p1.y:p2.y; min = (p1.ymap[p1.x-1][i]! =0) { line=FALSE; break; } } if(line) { m_ptCross1.x = p1.x-1; m_ptCross1.y = p1.y; m_ptCross2.x = p1.x-1; m_ptCross2.y = p2.y; return TRUE; } else line=TRUE; for(i=min; i<=max; i++) { if(parent->map[p1.x+1][i]! =0) //下侧 { line=FALSE; break; } } if(line) { m_ptCross1.x = p1.x+1; m_ptCross1.y = p1.y; m_ptCross2.x = p1.x+1; m_ptCross2.y = p2.y; return TRUE; } } else line=FALSE; if( (p1.y) == (p2.y) ) { max = (p1.x>p2.x)?p1.x:p2.x; min = (p1.xmap[i][p1.y-1]! =0) //左侧 { col=FALSE; break; } } if(col) { m_ptCross1.x = p1.x; m_ptCross1.y = p1.y-1; m_ptCross2.x = p2.x; m_ptCross2.y = p2.y-1; return TRUE; } else col=TRUE; for(i=min; i<=max; i++) { if(parent->map[i][p1.y+1]! =0) //右侧 { col=FALSE; break; } } if(col) { 图 5 82 2010. 1 m_ptCross1.x = p1.x; m_ptCross1.y = p1.y+1; m_ptCross2.x = p2.x; m_ptCross2.y = p2.y+1; return TRUE; } } else col=FALSE; if(line || col) return TRUE; else return FALSE; } 函数分别判断上侧、 下侧、 左侧和右侧, 以左侧为例, 如 果这两个图片的左侧都为空, 则可消掉这一对图片。 最后, 也是最困难的, 也就是需要用两个折点才能连接的 情况, 如图 6 所示。 这种情况中, 其中一个折点的横坐标 (或纵坐标) 一定和 其中某图片的横坐标 (或纵坐标) 相同, 另一个折点的横坐标 (或纵坐标) 一定和另外一幅图片的纵坐标 (或横坐标) 相同, 并且这两个折点还可以直连。 基于这一特点, 可以这样来判断 两图片是否能通过两折而相连, 首先从某一图片出发, 沿某一 方向, 如纵向搜索, 找到一个空白的位置, 一旦这一位置确定 下来, 就可以分别判断它和起始的图片是否能够直连以及是否 能够通过一个折点和另外一幅图片相连。 如果这两点都能满 足, 那么就找到了这样的一条路径, 可将两图片通过两折相 连, 如果不满足, 则继续搜索, 以此类推, 直到搜索完毕。 如 果依旧找不到这样的一条路径, 则说明它们不能够通过两折相 连 。 可以为 CLLKanButton 类添加成员函数 FindTwoConner (CPoint p1, CPoint p2) 来实现上述功能, 源代码如下: BOOL CLLKanButton::FindTwoConner(CPoint p1, CPoint p2) { int i; CLLKanDlg *parent = (CLLKanDlg *)GetParent(); CPoint tempPoint1 = 0; CPoint tempPoint2 = 0; //横向 for(i=0; imap[tempPoint1.x][tempPoint1.y]==0)) { if(FindLine(tempPoint1, p1)) { tempPoint2.x = p2.x; tempPoint2.y = tempPoint1.y; if((parent->map[tempPoint2.x][tempPoint2.y]==0)) { if(FindLine(tempPoint1,tempPoint2)) { m_ptCross4 = tempPoint2; if(FindLine(tempPoint2, p2)) return TRUE; } } } } } //纵向 for(i=0; imap[tempPoint1.x][tempPoint1.y]==0) ) { if(FindLine(tempPoint1, p1)) { tempPoint2.x = tempPoint1.x; tempPoint2.y = p2.y; if ((parent->map [tempPoint2.x] [tempPoint2.y]==0)) { if(FindLine(tempPoint1,tempPoint2)) { m_ptCross4 = tempPoint2; if(FindLine(tempPoint2, p2)) return TRUE; } } } } } return FALSE; } 至此, 就完成了游戏的核心实现部分, 已经可以判断出先 后点击的两幅图片是否能消掉, 接下来的工作就是将它们真正 图 6 83 2010. 1 地从游戏界面上消除掉, 以及做出消图片时画线的效果。 对于 消掉图片的操作, 要做如下工作: 首先, 要把消掉的两张图片 的类型全都置零, 前面已经说过, 图片类型为零代表此处没有 图片, 然后, 通过 ShowWindow (SW_HIDE) 的方式将其隐藏 起来, 这样给玩家的感觉就像将这两张图片真正地消掉了。 完 成这一步后, 就可以制作消图片时画线的效果。 这里可以在主 对话框上添加静态空间, 设置 ID 为 IDC_LINE, 并为其关联变 量 m_line, 然后再为工程添加一个类, 名为 ClineStatic, 它继 承于 Cstatic, 用这个类来实现画线的功能。 每次需要画线时, 可以通过 m_line.Invalidate () 引起重绘, 并且可以在该类内部 设置定时器, 来限制所画出的线存在的时间。 4 其他功能 经常玩连连看的朋友都知道, 在游戏过程中, 有可能会出 现无图片可消的情况, 因此, 为了游戏的流畅性, 也应该为程 序添加自动判断是否有图片可消的功能, 如果判断出无图片可 消, 可以自动重新排序。 如果程序能判断出是否有图片可消, 也就可以自动找出可 消图片, 并且自动将其消掉。 这样, 相当于给程序添加了秘 籍, 可以设定某一按键或某一菜单项, 当按下这一按键或点击 这一菜单项时, 程序就可以自动地消掉一对图片, 在实在找不 到时, 可以用这一招。 这里只介绍寻找可消图片函数的代码, 设定消图按键等操 作这里就不做介绍了。 BOOL CLLKanButton::Hint() { CLLKanDlg *parent = (CLLKanDlg *)GetParent(); int x1, y1, x2, y2; CPoint p1 = 0; CPoint p2 = 0; for(x1=1; x1map[x1][y1]==0) || (parent->map[x2][y2] ==0))//空白格子 continue; if(parent->map[x1][y1] ! = parent->map[x2][y2]) //不相等 continue; if((x1==x2) && (y1==y2)) //同一个点 continue; if(FindLine(p1, p2)) { m_hintNum1 = (x1-1) * (MAXY-2) + y1 - 1; m_hintNum2 = (x2-1) * (MAXY-2) + y2 - 1; m_hintType=1; return TRUE; } if(FindOneConner(p1, p2)) { m_hintNum1 = (x1-1) * (MAXY-2) + y1 - 1; m_hintNum2 = (x2-1) * (MAXY-2) + y2 - 1; m_hintType=2; return TRUE; } if(FindSide(p1,p2)) { m_hintNum1 = (x1-1) * (MAXY-2) + y1 - 1; m_hintNum2 = (x2-1) * (MAXY-2) + y2 - 1; m_hintType=3; return TRUE; } if(FindTwoConner(p1, p2)) { m_hintNum1 = (x1-1) * (MAXY-2) + y1 - 1; m_hintNum2 = (x2-1) * (MAXY-2) + y2 - 1; m_hintType=4; return TRUE; } } return FALSE; } 通 过 上 述 函 数 , 就 可 以 找 到 可 消 的 对 , 并 能 通 过 m_hintNum1 和 m_hintNum2 保 存 可 消 对 在 按 钮 数 组 m_btnGroup 中的位置。 这样就可以通过后续函数实现将其消 掉的目的。 如果函数返回值为 FALSE, 就说明没有可消的图 片, 这时就必须将剩余的图片进行重新排列。 具体实现和前面 所述的对地图初始化的过程是类似的, 不同的是现在图片的类 型和数量已经确定了, 需要重新分配的只是位置。 最后, 当所有图片都消完后, 就说明游戏已经成功了, 这 时应该给出提示。 因此需要写一个判断是否所有图片都消完的 函数。 这很简单, 只要判断所有图片的类型是否都为零就可以 了。 至此为止, 整个游戏的基本功能已经完成了。 为了游戏更 完整, 可以加入设置游戏时间和图片类型数的菜单项, 一共提 供了 105 种图片, 这一难度应该已经相当大了。 当成功玩完一 次游戏后, 可以自动增加图片类型, 也就是加大了游戏难度, 提示用户进入下一局继续游戏。 如果图片类型已经是 105 了, 无法再增加难度, 就提示用户已经通关。 另外还应该提供自动 消图, 以及自动重新排列的菜单项, 来保证游戏的流畅性。 (收稿日期: 2009-11-12) 84
本文档为【VC_实现连连看游戏】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_403407
暂无简介~
格式:pdf
大小:605KB
软件:PDF阅读器
页数:7
分类:互联网
上传时间:2010-12-20
浏览量:47