先上图片预览

需要的所有属性字段:

//背景色集Color[] cls = new Color[] {Color.White,Color.AliceBlue,Color.Aqua,Color.Azure,Color.Bisque,Color.Gray,Color.Red,Color.Coral,Color.Brown,Color.Yellow};String[] images = new string[] {"1.png","2.png", "3.png", "4.png" ,"5.png", "6.png", "7.png", "8.png", "9.png" , "10.png" };//随机数Random r;//地图大小public int mapSize = 10;//地图按钮集合public Button[,] btns;//记录上一个按钮public static Button last_b = null;//记录按钮对应坐标public Hashtable BtoI;//记录按钮是否已熄灭public int[]map_index;//划线点public static Point p1;public static Point p2;//画图工具Pen p;Graphics g;//得分窗口public int score;public TextBox text;//计时器ProgressBar pb;

  在窗体加载时开始窗体控件的设计(已在设计面板上创建panel容器panel1用于放置各元素,个人认为使用panel的好处之一是可更方便定位元素放置位置):

private void Form2_Load(object sender, EventArgs e){Font f = new Font("楷体", 20);//label样式设计Label score_l = new Label();score_l.Height =50;score_l.Width = 100;score_l.Font = f;score_l.Text = "得分:";score_l.Location = new Point(panel1.Width + 200, 150);//textbox样式设计TextBox score_t = new TextBox();text = score_t;text.Text = score.ToString();score_t.Location = new Point(panel1.Width+300,120);score_t.Font = f;//字体score_t.Height = 70;//高度score_t.Width = 200;score_t.Multiline = true;this.Controls.Add(score_l);this.Controls.Add(score_t);this.Location = new Point(5, 5);this.Width = 1200;this.Height = 800;textBox1.Hide();textBox2.Hide();textBox3.Hide();panel1.Width = 750;panel1.Height = 750;//进度条样式设计//pb = new ProgressBar();//pb.Width = 500;pb.Height = 50;//pb.Location = new Point(100, 0);//放置位置//panel1.Controls.Add(pb);//pb.Value = 100;//初始长度拉满initialize();}

  以上设置了所需的各类自定义控件(可根据需求自行修改);

  接下来设置最重要的按钮集及其各项属性:

private void initialize(){btns = new Button[mapSize, mapSize];//timer1.Enabled = true;//pb.Value = 100;ArrayList arr = new ArrayList();BtoI = new Hashtable();for (int i=0;i<50;i++){int x = r.Next(0, 10);arr.Add(x);arr.Add(x);}//初始化mapindexint c = arr.Count;map_index = new int[c];while(--c>=0){int i = r.Next(0, c + 1);map_index[c] =(int) arr[i];arr.RemoveAt(i);}//初始化按钮集坐标及大小int w = panel1.Width;int h = panel1.Height;for (int i= 0;i<mapSize;i++){for (int j = 0; j < mapSize; j++){btns[i, j] = new Button();btns[i, j].Width = btns[i, j].Height= w / 12;//设置大小btns[i, j].Location = new Point(btns[i,j].Width+ j* (btns[i, j].Width+1) ,btns[i,j].Height+ i*(1+btns[i, j].Height));//设置坐标panel1.Controls.Add(btns[i, j]);//btns[i, j].Text = map_index[i * mapSize + j].ToString();btns[i, j].BackgroundImage = Image.FromFile("Resources"+"\\"+images[map_index[i * mapSize + j]]);btns[i, j].BackgroundImageLayout = ImageLayout.Zoom;btns[i, j].BackColor = cls[map_index[i*mapSize+j]];btns[i, j].Click += new EventHandler(this.Image_Click);//添加点击事件Point p = new Point(i, j);BtoI.Add(btns[i, j], p);}}}

  -由于每个按钮需要随机设置图案,map_Index数组用于存放该随机值。一个难点是如何设计成对的随机数字:

  我这里的做法是先产生所需随机数的一半,另一半则通过第二次循环来填充。

  -而我在这里具体使用到的的生成随机数的办法是:

  Arraylist数组,其可根据需要插入和删除,并且内部的Count方法可以返回当前数组中元素个数。其类似一个可随机访问,但只可尾端插入的栈。

  -原理是:每次生成一个随机数,将其拷贝一份一同插入Arraylist数组中,这样在结束插入时,数组中存放着两份完全相同的随机数序列。接下来每次生成一个随机数作为Arraylist的索引(前文提到其具随机访问和删除特性),将该索引处的数据作为按钮集

  buttons的随机值,然后删除该索引处的数据。注意:此时Arraylist数组Count计数减一,相应的随机索引也需要对Count-1取模。以上实现成对的随机数。

  -接下来对每个随机数自定义操作,可以针对每个索引使用不同的图片或背景色等等;可适当考虑需求。

  对每个按钮添加点击事件:

private void Image_Click(object sender, EventArgs e){var cur_btn = (Button)sender;//初始if (last_b == null){last_b = cur_btn;return;}//Point p_1 = new Point();Point p_2 = new Point();Point p_3 = new Point();Point p_4 = new Point();var p_1 = (Point)BtoI[last_b];var p_2 = (Point)BtoI[cur_btn];var p_3 = btns[p_1.X, p_1.Y].Location;var p_4 = btns[p_2.X, p_2.Y].Location;var p_start = new Point(p_3.X + last_b.Width / 2, p_3.Y + last_b.Height / 2);var p_end = new Point(p_4.X + last_b.Width / 2, p_4.Y + last_b.Height / 2);if (last_b.BackColor != cur_btn.BackColor || !isConnected(last_b, cur_btn)){//MessageBox.Show("您有事吗?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Information);last_b = cur_btn;}else{last_b.Hide(); cur_btn.Hide();//以下划线g = panel1.CreateGraphics();p = new Pen(Color.Red, 3);MyDrawLine(g, p, p_1, p_2, p_start, p_end);//以上划线map_index[((Point)BtoI[last_b]).X * mapSize + ((Point)BtoI[last_b]).Y] = -1;map_index[((Point)BtoI[cur_btn]).X * mapSize + ((Point)BtoI[cur_btn]).Y] = -1;score += 100;text.Text = score.ToString();if (GameOver()){MessageBox.Show("通关成功√", "提示", MessageBoxButtons.OKCancel);initialize();}last_b = null;}}

  先梳理流程:

  -用户开始游戏,此时没有任何点击动作。

  -点击第一个方块,此时需要判断是否有已选中的方块:如果没有(如游戏开始阶段),记录当前的图案(其实也就是先前生成的随机数,记住随机的根源是前面产生的随机数,图案等为装饰,但也可作为判断标记);

  而如果已有选中方块:判断两者图案是否相同:相同则消去,并且置空前一个按钮图案。(想象如果消除了一对图案,那么下一次点击是不会消除的)

                       不同则将当前图案置为对比图。

  由于需要从点击的按钮返回寻找按钮对应的位置(将map_index置为访问过的标记等等),此处我的做法是在初始化按钮集时生成一个哈希表存储每个按钮对应位置。(由于还未十分了解C#哈希表部分内容,无法多作讲解)

  在消除时增加了一个划线函数,即图示的消除红线。

private void MyDrawLine(Graphics g,Pen p, Point p_1, Point p_2, Point p_start,Point p_end){if (IsStraightConnected(p_1.X, p_1.Y, p_2.X, p_2.Y)){g.DrawLine(p, p_start, p_end);Delay_Show();}else if (IsLConnected(p_1.X, p_1.Y, p_2.X, p_2.Y)){var p_lx = new Point(btns[p1.X, p1.Y].Location.X + last_b.Width / 2,btns[p1.X, p1.Y].Location.Y + last_b.Height / 2);g.DrawLine(p, p_start, p_lx);g.DrawLine(p, p_lx, p_end);Delay_Show();}else if (IsZConnected(p_1.X, p_1.Y, p_2.X, p_2.Y)){var p_lx1 = new Point(btns[p1.X, p1.Y].Location.X + last_b.Width / 2,btns[p1.X, p1.Y].Location.Y + last_b.Height / 2);var p_lx2 = new Point(btns[p2.X, p2.Y].Location.X + last_b.Width / 2,btns[p2.X, p2.Y].Location.Y + last_b.Height / 2);g.DrawLine(p, p_start, p_lx1);g.DrawLine(p, p_lx1, p_lx2);g.DrawLine(p, p_lx2, p_end);Delay_Show();}}

  具体的画法也就是通过哈希寻找到按钮坐标,在其中心处向外划线。(注意,我曾在划线处遇到过问题,即在Form函数中使用drawline是无法看见画出的线的,与系统的渲染顺序有关,具体原因有兴趣的读者可以查阅资料)

  -以及,Location方法获得的是图形左上角的坐标。需将其做一定调整才能变为中心处划线。

  -由于设定划线为一闪而过,大致定个100ms:划线定时清除代码:

//定时删除划线async public void Delay_Show(){await Task.Delay(100);textBox1.Text = "..";g = panel1.CreateGraphics();g.Clear(this.BackColor);}

  用到async异步方法,不过合理只是简单调用了一个匿名task对象,完成简单的时延任务。

  最后是判断按钮连线的逻辑代码:

//按钮判断条件private bool isConnected(Button b1,Button b2){int r1 = ((Point)BtoI[b1]).X;int c1 = ((Point)BtoI[b1]).Y;int r2 = ((Point)BtoI[b2]).X;int c2 = ((Point)BtoI[b2]).Y;if (IsStraightConnected(r1, c1, r2, c2)){textBox3.Text = "straight";//g= btns[r1,c1]. CreateGraphics();//p = new Pen(Color.Red);//g.DrawLine(p,btns[r1,c1].Location,btns[r2,c2].Location);//Delay_Show();return true;}else if (IsLConnected(r1, c1, r2, c2)){textBox3.Text = "L";return true;}else if (IsZConnected(r1, c1, r2, c2)){textBox3.Text = "Z";return true;}return false;}//直接相连private bool IsStraightConnected(int r1, int c1,int  r2, int c2){if (c1 == c2){if (r2 < r1) { int t = r1; r1 = r2; r2 = t; }for (int i = r1 + 1; i != mapSize; i++){if (i == r2) return true;else if (map_index[i * mapSize + c1] != -1) return false;}}else if (r1 == r2){if (c2 < c1) { int t = c1; c1 = c2; c2 = t; }for (int j = c1 + 1; j != mapSize; j++){if (j == c2) return true;else if (map_index[r1*mapSize+j] != -1) return false;}}return false;}//L型private bool IsLConnected(int r1, int c1, int r2, int c2){if (IsStraightConnected(r1, c2, r2, c2) && IsStraightConnected(r1, c2, r1, c1)&& map_index[r1 * mapSize + c2] == -1){p1 = new Point(r1, c2);return true;}if (IsStraightConnected(r2, c1, r2, c2) && IsStraightConnected(r2, c1, r1, c1)&& map_index[r2 * mapSize + c1] == -1){p1 = new Point(r2, c1);return true;}return false;}//Z型private bool IsZConnected(int r1, int c1, int r2, int c2){int direction = 1;for (int count=0;count<2;count++){direction = -direction;//横向先行for (int i = c1 + direction; (i>=0&&i<mapSize); i += direction){if (IsLConnected(r1, i, r2, c2) && IsLConnected(r2, i, r1, c1)){p1 = new Point(r1, i);p2 = new Point(r2, i);return true;}}}for (int a=0;a<2;a++){direction = -direction;//纵向先行for (int i = r1 + direction; (i >= 0 && i < mapSize); i += direction){if (IsLConnected(i, c1, r2, c2) && IsLConnected(i, c2, r1, c1)){p1 = new Point( i,c1);p2 = new Point(i,c2);return true;}}}return false;}

  有一点迭代的意思:即直线连线的逻辑也适用于L型或Z型连接;L型即有一次转折,Z型为有两次转折。本处用到direction用于双向循环,从而避免对两个方向分别增加逻辑代码,造成冗余。

  最后是游戏结束判断逻辑:

private bool GameOver(){for (int i=0;i<mapSize*mapSize;i++){if (map_index[i] != -1)return false;}return true;}

  即map_index全部被置为-1时,游戏结束。

  本次设计也遇到了许多问题:1.我尝试使用ProgressBar进度条来限定一局游戏时间;并添加了Timer计时器,在time结束时调用initialize方法。但当计时结束但未消除完毕时,调用initialize方法后报错:按钮到索引的BtoI哈希表未设置引用到对象的实例。目前还          不知道如何解决。

  2.还未实现地图外的连接,比如边界上两个图标可以通过边界外加的一行消除。思路大概是将map_index表向外扩展一圈,但具体实现还没能完成。

  3.由于本次设计是三分钟热度,因而没有提前进行代码的设计,代码冗余十分严重,而且结构并不清晰,还望各位读者多多包涵。

C# winform简易连连看小游戏相关推荐

  1. python连连看小游戏_python递归法实现简易连连看小游戏

    问题:简单版连连看小游戏 一个分割成w*h个正方格子的矩形板上,每个正方格子可以有游戏卡,也可以没有游戏卡 两个游戏卡之间有一条路径相连需满足以下三个条件: 1.路径只包含水平和垂直的直线段 2.路径 ...

  2. 原生js实现一个连连看小游戏(一)

    前几天使用原生的js写了一个连连看小游戏,地址:连连看(js),基本功能都实现了,运行截图为: 根据游戏规则获取开发思路 创建棋盘格 生成随机不重复数字 映射到棋盘格 鼠标点击事件 寻路,无通路,则到 ...

  3. python连连看小游戏_利用Python制作一个连连看小游戏,边学边玩!

    导语 今天我们将制作一个连连看小游戏,让我们愉快地开始吧~ 开发工具 Python版本:3.6.4 相关模块: pygame模块: 以及一些Python自带的模块 环境搭建 安装Python并添加到环 ...

  4. C# 游戏制作 | ✨ 简易文字小游戏

    简易文字小游戏 在学习了一些C#的基础知识后就要做一些小东西来练练手,就比如本文所介绍的一个通过用VS中的C#写的一个简易文字小游戏 这个小游戏只由一个脚本完成,主要是用来拿C#中的一些基础知识完成, ...

  5. MATLAB连连看小游戏

    这是一款MATLAB连连看小游戏,基本实现了连连看的功能,不过目前没有写出死局判定的模块,日后可能会对其进行改进. 目录 游戏截图 游戏组装说明 完整代码 其他说明 后记 游戏截图 游戏组装说明 我们 ...

  6. H5——连连看小游戏实现思路及源码

    部门要求推广新产品用连连看小游戏的方式, 设计那边UI还没有排期,先撸个功能demo,正好记录一下: 连连看都玩过,程序的关键在于判断连续点击的两张图片是否能够消除,两个图片消除的条件有两个: 图片相 ...

  7. java游戏开发——连连看小游戏

    本次课程主要为大家详细介绍了Java制作--<连连看>小游戏,示例代码介绍的非常详细,具有想当的参考价值,感兴趣的小伙伴们可以学习一下! 小伙伴们应该都玩过连连看吧,今天呢叫大家用Java ...

  8. python应用学习(六)——tkinter制作连连看小游戏

    python 制作连连看小游戏 前言 一.准备 二.游戏简单介绍 1.游戏规则 2.游戏设计所需的图片库: 三.游戏设计 I.创建Point点类 II.定义函数 III.游戏的主函数逻辑 IV.完整代 ...

  9. java连连看小游戏

    目录 运行界面图 代码如下(示例): 2.运行演示 这是一个用java编写的小游戏,连连看是一种消除类益智游戏,核心要求是在规定的时间内,消除游戏界面中选中的两张相同的图案,直至完全消除所有图案.这款 ...

最新文章

  1. 比如“古今数学思想”,BBC的“数学的故事”视频,一起看效果会更好
  2. IBM:物联网应重启,否则注定会失望
  3. python furl模块 网址修改 参数解析
  4. 【模式匹配】KMP算法的来龙去脉
  5. android listview源码--第一章RecycleBin
  6. 【好文链接】什么是最小二乘法?
  7. 10 个步骤让你成为高效的 Web 开发者
  8. 【TSP】基于matlab混合粒子群算法求解旅行商问题【含Matlab源码 397期】
  9. vfp邮件.html格式,VFP 实现自动发邮件代码
  10. python查看文件大小_python查看文件大小和文件夹内容的方法
  11. 什么是外贸网站?企业为什么要建设外贸网站?
  12. 光学三原色与色的三原色
  13. 北京理工大学计算机学院acm赵曜,王一拙_北京理工大学计算机学院
  14. 抓包软件wireshark分析
  15. 《轩辕剑四》:一场关于青铜朋客古代黑科技的浪漫幻想
  16. 【云原生之k8s】k8s安全机制
  17. 绝地求生——PUBG吃鸡游戏模糊,画质很差
  18. 5java讲解(xy)
  19. Rails 内置方法大全(慢慢积累中)
  20. 香港中文大学助理教授周博磊:十年之间的CVPR与我们

热门文章

  1. 服务器 ip 网络共享 多服务器用同一个公网IP
  2. 如何有效提升微信小程序的排名?
  3. c语言中字符串去掉逗号,JS四种方法去除字符串最后的逗号
  4. VL817B0 / VL817C0 与 VL817S / VL817-Q7S(A1) 的区别
  5. 新型激光辨识算法助力机器人逃离死胡同
  6. linux串口输出系统日志,linux系统连接串口工具打印log
  7. 三节锂电池充电管理芯片,IC电路图,BOM物料表
  8. python中文乱码问题
  9. Fiddler大师之路系列(五)
  10. RTSP H264播放器(基于live555、ffmpeg、d3d应用)