文章目录

  • 成品链接
  • 项目展示
  • 各部分功能解释
    • 一、项目分析
    • 二、项目设计
  • 全部代码
    • 窗体控件命名
    • 连连看功能类
    • 窗体功能实现

成品链接

https://download.csdn.net/download/weixin_45525272/13713452


PS:载完了如果报错,参考这篇博文改错:https://blog.csdn.net/weixin_45525272/article/details/117417379

项目展示


各部分功能解释

一、项目分析

系统设计思路

(1)设计连连看功能类:LinkClass,Line
(2)设计两个事件:成功事件和失败事件
(3)成功事件:1.垂直事件2.水平事件3.一个拐点事件4.两个拐点事件
(4)失败事件:

  1. 一个按钮点击两次
  2. 点击的两个按钮所带图片不一样
  3. 点击两个具有相同图案的按钮但没有通路

(5)可以选择三种行和列:88,1010,12*12
(6)可以选择两种模式:数字,图片
(7)设计随机数并且随机数要配对
(8)赋予按钮应有的相关属性
(9)计时器设计,暂停按钮时计时器停止计时,开始按钮显示继续游戏
(10)每消除一对图片,计分加100
(11)重新排列功能,每重新排列一次总得分减400

二、项目设计

一、难度和模式
用两个comboBox分别设计行和列,图片或者数字模式


if (comboBox1.Text == ""|| comboBox2.Text=="")
{MessageBox.Show("请选择难度或者模式", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{// 选择的关卡难度文本string Diffcult = comboBox1.Text;//根据对应难度初始化数组并给n赋值switch (Diffcult){case "8*8":hang = 8;break;case "10*10":hang = 10;break;case "12*12":hang = 12;break; }
//判断用户选的是图片还是数字string mul = comboBox2.Text;if (mul == "数字"){Button btnLinker = new Button() // 设置每个小按钮的相关属性{Text = rnd.ToString(),//按钮上显示的是随机数Tag = img,//标签Name = string.Format("{0},{1}", row, col),//每个按钮命名Dock = DockStyle.Fill,//调整控件层次,填充Margin = new Padding(1),//调整外边距};btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件this.TableLp.Controls.Add(btnLinker, col, row);//将按钮加进去并赋给位置}else//即用户选择了图片{Button btnLinker = new Button() // 设置每个小按钮的相关属性{BackgroundImage = GetImageByName(img),//给按钮填充背景图片BackgroundImageLayout = ImageLayout.Stretch,//背景图片拉申Tag = img,Name = string.Format("{0},{1}", row, col),Dock = DockStyle.Fill,Margin = new Padding(1),};btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件this.TableLp.Controls.Add(btnLinker, col, row);}}}this.LinkObj.LinkBoard = linkBoard;//初始化棋盘this.TableLp.Enabled = false;

2、进度条分数模块
用计时器线程来计时,进行进度条与时间标签的实时更新

// 计时器线程Thread timeDelay;       // 线程变量int timetemp = 0;       // 计时器中间变量// 定时器事件:时间剩余void timeDelay_Tick(){//ma.WaitOne(); // 根据是否收到信号判断是否阻塞当前线程while (this.curDelay > 0 )   // 当前时间大于零{if (!isClosing){this.curDelay--;this.pbTimeDelay.Value = this.curDelay;this.lblTimeDelay.Text = this.curDelay.ToString();}Thread.Sleep(1000);}if (!isClosing){this.btnStart.Enabled = true;this.TableLp.Enabled = false;}}

3、开始游戏(游戏初始化)
清除残余,并且设置新的表格

this.TableLp.ColumnStyles.Clear();this.TableLp.RowStyles.Clear();this.TableLp.Controls.Clear();
this.TableLp.ColumnCount = hang + 2;this.TableLp.RowCount = hang + 2;设置按钮及边框所占的比列
for (int i = 0; i < hang + 2; i++){//设置等宽等高  边缘2,里面内容10if (i == 0 || i == hang + 1){this.TableLp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 2));//占总画面的2%this.TableLp.RowStyles.Add(new RowStyle(SizeType.Percent, 2));}else{this.TableLp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 10));//占总画面的10%this.TableLp.RowStyles.Add(new RowStyle(SizeType.Percent, 10));}}为每个按钮赋给图片或者相应的数字string img = string.Format("_1_{0}", rnd.ToString("00"));//给图片命名linkBoard[row, col] = rnd;//赋给每个位置随机数字//判断用户选的是图片还是数字string mul = comboBox2.Text;if (mul == "数字"){Button btnLinker = new Button() // 设置每个小按钮的相关属性{Text = rnd.ToString(),//按钮上显示的是随机数Tag = img,//标签Name = string.Format("{0},{1}", row, col),//每个按钮命名Dock = DockStyle.Fill,//调整控件层次,填充Margin = new Padding(1),//调整外边距};btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件this.TableLp.Controls.Add(btnLinker, col, row);//将按钮加进去并赋给位置}else//即用户选择了图片{Button btnLinker = new Button() // 设置每个小按钮的相关属性{BackgroundImage = GetImageByName(img),//给按钮填充背景图片BackgroundImageLayout = ImageLayout.Stretch,//背景图片拉申Tag = img,Name = string.Format("{0},{1}", row, col),Dock = DockStyle.Fill,Margin = new Padding(1),};btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件this.TableLp.Controls.Add(btnLinker, col, row);}}}

4、暂停游戏
通过将当前时间的变量赋值为0来结束当前计时器线程,并且当点击继续游戏的时候,通过开启新线程来重新运行计数功能

暂停

        private void btnPause_Click(object sender, EventArgs e){this.TableLp.Enabled = false;btnStart.Enabled = true;btnStart.Text = "继续游戏";//ma.Reset();//信号关闭阻塞当前线程timetemp = this.curDelay;this.curDelay = 0;lblTimeDelay.Text = timetemp.ToString();}

继续

 else if (btnStart.Text == "继续游戏"){//ma.Set();//信号打开,不阻塞当前线程btnStart.Text = "开始游戏";btnStart.Enabled = false;this.TableLp.Enabled = true;this.curDelay= timetemp;// 将中间变量赋值给新的当前时间lblTimeDelay.Text = this.curDelay.ToString();timeDelay = new Thread(new ThreadStart(this.timeDelay_Tick));//新的计时器timeDelay.Start();}

5、重新排列
设置随机二维数组,将原来的数字所在的行和列用随机的二维数组替代

int rnd, rnd2;// 打乱二维数组顺序for (int row = 1; row < hang + 1; row++){for (int col = 1; col < hang + 1; col++){// 产生随机数rnd = r.Next(1, 1000) % hang + 1;rnd2 = r.Next(1, 1000) % hang + 1;int temp = linkBoard[rnd, rnd2];linkBoard[rnd, rnd2] = linkBoard[row, col];linkBoard[row, col] = temp;}}

6、判断连接的核心算法
(1)垂直事件
获取按钮1和按钮2的y值,并利用循环来判断按钮1和按钮2之间是否为通路

 private bool vertical(Point a, Point b){int col_start = a.Y < b.Y ? a.Y : b.Y;    //获取a,b中较小的y值int col_end = a.Y < b.Y ? b.Y : a.Y;      //获取a,b中较大的y值//遍历a,b之间是否通路,如果一个不是就返回false;for (int i = col_start + 1; i < col_end; i++){if (this.LinkBoard[a.X, i] != 0)//0是空的状态(即没有按钮){return false;}}return true;}

(2)水平事件
获取按钮1和按钮2的x值,并利用循环来判断按钮1和按钮2之间是否为通路

private bool horizon(Point a, Point b){int row_start = a.X < b.X ? a.X : b.X;//获取a,b中较小的x值int row_end = a.X < b.X ? b.X : a.X;//获取a,b中较大的x值for (int i = row_start + 1; i < row_end; i++){if (this.LinkBoard[i, a.Y] != 0)//0是空的状态(即没有按钮){return false;//return是函数结束}}return true;}

(3)一个拐点事件
首先判断拐点位置是否有按钮,若没有,将剩下部分看做由一个垂直事件和一个水平事件组成

private bool oneCorner(Point a, Point b){// 拐角点的x,y分别等于两点各个x,yPoint c = new Point(b.X, a.Y);Point d = new Point(a.X, b.Y);// 判断C点是否有元素,有就继续执行下面判断条件,没有就看a与c,b与c之间是否能水平或者垂直连线                if (this.LinkBoard[c.X, c.Y] == 0){bool path1 = vertical(b, c) && horizon(a, c);return path1;   //path1本身就是bool值}//判断D点是否有元素if (this.LinkBoard[d.X, d.Y] == 0){bool path2 = vertical(a, d) && horizon(b, d);return path2;//path2本身就是bool值}// 能执行到这就说明前面判断不成功,不存在一个拐点的情况return false;}

(4)两个拐点事件
首先判断两个拐点是都有按钮,并且判断两个拐点间是否为通路,此过程用到新建的scan函数,剩下即有两个水平事件或者两个垂直事件构成

private List<Line> scan(Point a, Point b){List<Line> linkList = new List<Line>();// 新建一个直线对象List容器,用来存成立的拐点之间的直线//检测a点上方是否能够水平直连for (int i = a.Y; i >= 0; i--){if (this.LinkBoard[a.X, i] == 0 && this.LinkBoard[b.X, i] == 0 && horizon(new Point(a.X, i), new Point(b.X, i))){linkList.Add(new Line(new Point(a.X, i), new Point(b.X, i), 0));}}//检测a点下方是否能够水平直连for (int i = a.Y; i < Col; i++){if (this.LinkBoard[a.X, i] == 0 && this.LinkBoard[b.X, i] == 0 && horizon(new Point(a.X, i), new Point(b.X, i))){linkList.Add(new Line(new Point(a.X, i), new Point(b.X, i), 0));}}//检测a点左侧是否能够垂直直连for (int j = a.X; j >= 0; j--){if (this.LinkBoard[j, a.Y] == 0 && this.LinkBoard[j, b.Y] == 0 && vertical(new Point(j, a.Y), new Point(j, b.Y))){linkList.Add(new Line(new Point(j, a.Y), new Point(j, b.Y), 1));}}//检测a点右侧是否能够垂直直连for (int j = a.X; j < Row; j++){if (this.LinkBoard[j, a.Y] == 0 && this.LinkBoard[j, b.Y] == 0 && vertical(new Point(j, a.Y), new Point(j, b.Y))){linkList.Add(new Line(new Point(j, a.Y), new Point(j, b.Y), 1));}}return linkList;}
}private bool twoCorner(Point a, Point b){// list容器:相当于一个Line类型的数组,存扫到的连通路径List<Line> ll = scan(a, b);//此时的直线已经判断成功并且拐点位置没有数字if (ll.Count == 0)  // 没扫到返回false,List容器里面的计数方法{return false;}for (int i = 0; i < ll.Count; i++){Line tempLine = ll[i];  // 临时连线变量用于判断拐点与a,b的通路是否成立if (tempLine.direct == 1)   // 两个拐点之间是水平直连{// 判断分别两个拐点与a,b能否相连if (horizon(a, tempLine.a) && horizon(b, tempLine.b))//判断a与a"之间是否有通路;判断b与b"之间是否有通路{return true;}}else if (tempLine.direct == 0)  // 两个拐点之间是垂直直连{// 判断分别两个拐点与a,b能否相连if (vertical(a, tempLine.a) && vertical(b, tempLine.b))//判断a与a"之间是否有通路;判断b与b"之间是否有通路{return true;}}}return false;}

7.重新排列
将原本储存的二维数组重新打乱顺序,在按照打乱后的数组动态生成按钮

  // 打乱二维数组顺序for (int row = 1; row < hang + 1; row++){for (int col = 1; col < hang + 1; col++){// 产生随机数rnd = r.Next(1, 1000) % hang + 1;rnd2 = r.Next(1, 1000) % hang + 1;int temp = linkBoard[rnd, rnd2];linkBoard[rnd, rnd2] = linkBoard[row, col];linkBoard[row, col] = temp;}}

8.连接成功判断
用两个循环挨个遍历数组元素并计数,最后判断计数是否够元素的个数

        // 判断成功private void Judge(){int count=0;// 将内置元素进行随机数分配等操作for (int row = 1; row < hang + 1; row++){for (int col = 1; col < hang + 1; col++){if (this.LinkObj.LinkBoard[row,col]==0){count++;}}}if (count==hang*hang){MessageBox.Show("拼完了,你可真厉害!","提示",MessageBoxButtons.OK,MessageBoxIcon.Asterisk);this.TableLp.Enabled = false;btnStart.Enabled = true;timetemp = this.curDelay;this.curDelay = 0;lblTimeDelay.Text = timetemp.ToString();}}

全部代码

窗体控件命名

namespace Linkup
{partial class MainWindow{/// <summary>/// 必需的设计器变量。/// </summary>private System.ComponentModel.IContainer components = null;/// <summary>/// 清理所有正在使用的资源。/// </summary>/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>protected override void Dispose(bool disposing){if (disposing && (components != null)){components.Dispose();}base.Dispose(disposing);}#region Windows 窗体设计器生成的代码/// <summary>/// 设计器支持所需的方法 - 不要修改/// 使用代码编辑器修改此方法的内容。/// </summary>private void InitializeComponent(){System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow));this.TableLp = new System.Windows.Forms.TableLayoutPanel();this.btnStart = new System.Windows.Forms.Button();this.pbTimeDelay = new System.Windows.Forms.ProgressBar();this.lblTimeDelay = new System.Windows.Forms.Label();this.lableScore = new System.Windows.Forms.Label();this.btnStop = new System.Windows.Forms.Button();this.comboBox1 = new System.Windows.Forms.ComboBox();this.btnPause = new System.Windows.Forms.Button();this.btnReset = new System.Windows.Forms.Button();this.comboBox2 = new System.Windows.Forms.ComboBox();this.label1 = new System.Windows.Forms.Label();this.label2 = new System.Windows.Forms.Label();this.SuspendLayout();// // TableLp// this.TableLp.ColumnCount = 1;this.TableLp.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));this.TableLp.Location = new System.Drawing.Point(61, 12);this.TableLp.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);this.TableLp.Name = "TableLp";this.TableLp.RowCount = 1;this.TableLp.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));this.TableLp.Size = new System.Drawing.Size(534, 534);this.TableLp.TabIndex = 0;// // btnStart// this.btnStart.Location = new System.Drawing.Point(697, 44);this.btnStart.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);this.btnStart.Name = "btnStart";this.btnStart.Size = new System.Drawing.Size(243, 36);this.btnStart.TabIndex = 1;this.btnStart.Text = "开始游戏";this.btnStart.UseVisualStyleBackColor = true;this.btnStart.Click += new System.EventHandler(this.btnStart_Click);// // pbTimeDelay// this.pbTimeDelay.Location = new System.Drawing.Point(61, 573);this.pbTimeDelay.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);this.pbTimeDelay.Name = "pbTimeDelay";this.pbTimeDelay.Size = new System.Drawing.Size(451, 19);this.pbTimeDelay.Style = System.Windows.Forms.ProgressBarStyle.Continuous;this.pbTimeDelay.TabIndex = 2;// // lblTimeDelay// this.lblTimeDelay.AutoSize = true;this.lblTimeDelay.Location = new System.Drawing.Point(542, 573);this.lblTimeDelay.Name = "lblTimeDelay";this.lblTimeDelay.Size = new System.Drawing.Size(15, 15);this.lblTimeDelay.TabIndex = 3;this.lblTimeDelay.Text = "0";// // lableScore// this.lableScore.AutoSize = true;this.lableScore.Location = new System.Drawing.Point(724, 573);this.lableScore.Name = "lableScore";this.lableScore.Size = new System.Drawing.Size(60, 15);this.lableScore.TabIndex = 4;this.lableScore.Text = "分数:0";// // btnStop// this.btnStop.Location = new System.Drawing.Point(697, 160);this.btnStop.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);this.btnStop.Name = "btnStop";this.btnStop.Size = new System.Drawing.Size(243, 36);this.btnStop.TabIndex = 1;this.btnStop.Text = "结束游戏";this.btnStop.UseVisualStyleBackColor = true;this.btnStop.Click += new System.EventHandler(this.btnStop_Click);// // comboBox1// this.comboBox1.FormattingEnabled = true;this.comboBox1.Location = new System.Drawing.Point(780, 318);this.comboBox1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);this.comboBox1.Name = "comboBox1";this.comboBox1.Size = new System.Drawing.Size(160, 23);this.comboBox1.TabIndex = 5;// // btnPause// this.btnPause.Location = new System.Drawing.Point(697, 102);this.btnPause.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);this.btnPause.Name = "btnPause";this.btnPause.Size = new System.Drawing.Size(243, 36);this.btnPause.TabIndex = 1;this.btnPause.Text = "暂停游戏";this.btnPause.UseVisualStyleBackColor = true;this.btnPause.Click += new System.EventHandler(this.btnPause_Click);// // btnReset// this.btnReset.Location = new System.Drawing.Point(697, 218);this.btnReset.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);this.btnReset.Name = "btnReset";this.btnReset.Size = new System.Drawing.Size(243, 36);this.btnReset.TabIndex = 1;this.btnReset.Text = "重新排列";this.btnReset.UseVisualStyleBackColor = true;this.btnReset.Click += new System.EventHandler(this.btnReset_Click);// // comboBox2// this.comboBox2.FormattingEnabled = true;this.comboBox2.Location = new System.Drawing.Point(780, 367);this.comboBox2.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);this.comboBox2.Name = "comboBox2";this.comboBox2.Size = new System.Drawing.Size(160, 23);this.comboBox2.TabIndex = 5;// // label1// this.label1.AutoSize = true;this.label1.Location = new System.Drawing.Point(694, 321);this.label1.Name = "label1";this.label1.Size = new System.Drawing.Size(52, 15);this.label1.TabIndex = 6;this.label1.Text = "难度:";// // label2// this.label2.AutoSize = true;this.label2.Location = new System.Drawing.Point(694, 372);this.label2.Name = "label2";this.label2.Size = new System.Drawing.Size(52, 15);this.label2.TabIndex = 6;this.label2.Text = "模式:";// // MainWindow// this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;this.ClientSize = new System.Drawing.Size(1008, 652);this.Controls.Add(this.label2);this.Controls.Add(this.label1);this.Controls.Add(this.comboBox2);this.Controls.Add(this.comboBox1);this.Controls.Add(this.lableScore);this.Controls.Add(this.lblTimeDelay);this.Controls.Add(this.pbTimeDelay);this.Controls.Add(this.btnReset);this.Controls.Add(this.btnPause);this.Controls.Add(this.btnStop);this.Controls.Add(this.btnStart);this.Controls.Add(this.TableLp);this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);this.Name = "MainWindow";this.Text = "Form1";this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);this.Load += new System.EventHandler(this.Form1_Load);this.ResumeLayout(false);this.PerformLayout();}#endregionprivate System.Windows.Forms.TableLayoutPanel TableLp;private System.Windows.Forms.Button btnStart;private System.Windows.Forms.ProgressBar pbTimeDelay;private System.Windows.Forms.Label lblTimeDelay;private System.Windows.Forms.Label lableScore;private System.Windows.Forms.Button btnStop;private System.Windows.Forms.ComboBox comboBox1;private System.Windows.Forms.Button btnPause;private System.Windows.Forms.Button btnReset;private System.Windows.Forms.ComboBox comboBox2;private System.Windows.Forms.Label label1;private System.Windows.Forms.Label label2;}
}

连连看功能类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;namespace Linkup
{// 连连看功能类public class LinkClass{// 连连看 看板public int[,] LinkBoard { get; set; }//命名为LinkBoard的连连看看板// 连线成功事件public event EventHandler SucClick;// 连接失败事件public event EventHandler FailClick;//Col是行private int col = 10;public int Col {get{return col;}set{col = value;}}// Row是列private int row = 10;public int Row{get{return row;}set{row = value;}}// 连接测试函数public void LinkLine(Point first,Point second)//点击的第一个点为first;点击的第二个点为second{// EventArgs:事件参数EventArgs e = new EventArgs();if (checkLink(first, second))   // 判断是否连接成功{// 连线成功this.LinkBoard[first.X, first.Y] = 0;//清除记忆中的first,以便下一次消除this.LinkBoard[second.X, second.Y] = 0;//清除记忆中的second,以便下一次消除if (this.SucClick != null)  // 给事件添加事件参数{SucClick(this, e); }}else{// 连线失败if (this.FailClick != null)//给事件添加事件参数{FailClick(this, e);}}}// 是否赋public bool IsChecked(Point p){bool flag = false;if (p.X != -1 && p.Y != -1){flag = true;}return flag;}// 判断是否连接成功——核心算法/*a,第一个点   b,第二个点*/private bool checkLink(Point a,Point b){if (!Point.Equals(a,b)) // 如果点击的不是同一位置{// 如果点击的不是同一位置if (this.LinkBoard[a.X, a.Y] == this.LinkBoard[b.X, b.Y])//如果两者为同一图案{// 判断连线能否成功if (a.X == b.X && vertical(a, b))//垂直{return true;}if (a.Y == b.Y && horizon(a, b))//水平{return true;}if (oneCorner(a, b))//一个拐点{return true;}else//两个拐点{return twoCorner(a, b);}}else{// 如果点击的不是同一个图案,直接返回falsereturn false;}}else{// 如果点击的是同一个位置的图案,直接返回false;return false;}}// 垂直的连线情况private bool vertical(Point a, Point b){int col_start = a.Y < b.Y ? a.Y : b.Y;    //获取a,b中较小的y值int col_end = a.Y < b.Y ? b.Y : a.Y;      //获取a,b中较大的y值//遍历a,b之间是否通路,如果一个不是就返回false;for (int i = col_start + 1; i < col_end; i++){if (this.LinkBoard[a.X, i] != 0)//0是空的状态(即没有按钮){return false;}}return true;}// 水平的连线情况private bool horizon(Point a, Point b){int row_start = a.X < b.X ? a.X : b.X;//获取a,b中较小的x值int row_end = a.X < b.X ? b.X : a.X;//获取a,b中较大的x值for (int i = row_start + 1; i < row_end; i++){if (this.LinkBoard[i, a.Y] != 0)//0是空的状态(即没有按钮){return false;//return是函数结束}}return true;}// 一个拐角连线情况private bool oneCorner(Point a, Point b){// 拐角点的x,y分别等于两点各个x,yPoint c = new Point(b.X, a.Y);Point d = new Point(a.X, b.Y);// 判断C点是否有元素,有就继续执行下面判断条件,没有就看a与c,b与c之间是否能水平或者垂直连线                if (this.LinkBoard[c.X, c.Y] == 0){bool path1 = vertical(b, c) && horizon(a, c);return path1;   //path1本身就是bool值}//判断D点是否有元素if (this.LinkBoard[d.X, d.Y] == 0){bool path2 = vertical(a, d) && horizon(b, d);return path2;//path2本身就是bool值}// 能执行到这就说明前面判断不成功,不存在一个拐点的情况return false;}// 两个拐点private bool twoCorner(Point a, Point b){// list容器:相当于一个Line类型的数组,存扫到的连通路径List<Line> ll = scan(a, b);//此时的直线已经判断成功并且拐点位置没有数字if (ll.Count == 0)  // 没扫到返回false,List容器里面的计数方法{return false;}for (int i = 0; i < ll.Count; i++){Line tempLine = ll[i];  // 临时连线变量用于判断拐点与a,b的通路是否成立if (tempLine.direct == 1)   // 两个拐点之间是水平直连{// 判断分别两个拐点与a,b能否相连if (horizon(a, tempLine.a) && horizon(b, tempLine.b))//判断a与a"之间是否有通路;判断b与b"之间是否有通路{return true;}}else if (tempLine.direct == 0)  // 两个拐点之间是垂直直连{// 判断分别两个拐点与a,b能否相连if (vertical(a, tempLine.a) && vertical(b, tempLine.b))//判断a与a"之间是否有通路;判断b与b"之间是否有通路{return true;}}}return false;}// 扫描A与B之间的连接点组成的线(scan)//List为容器private List<Line> scan(Point a, Point b){List<Line> linkList = new List<Line>();// 新建一个直线对象List容器,用来存成立的拐点之间的直线//检测a点上方是否能够水平直连for (int i = a.Y; i >= 0; i--){if (this.LinkBoard[a.X, i] == 0 && this.LinkBoard[b.X, i] == 0 && horizon(new Point(a.X, i), new Point(b.X, i))){linkList.Add(new Line(new Point(a.X, i), new Point(b.X, i), 0));}}//检测a点下方是否能够水平直连for (int i = a.Y; i < Col; i++){if (this.LinkBoard[a.X, i] == 0 && this.LinkBoard[b.X, i] == 0 && horizon(new Point(a.X, i), new Point(b.X, i))){linkList.Add(new Line(new Point(a.X, i), new Point(b.X, i), 0));}}//检测a点左侧是否能够垂直直连for (int j = a.X; j >= 0; j--){if (this.LinkBoard[j, a.Y] == 0 && this.LinkBoard[j, b.Y] == 0 && vertical(new Point(j, a.Y), new Point(j, b.Y))){linkList.Add(new Line(new Point(j, a.Y), new Point(j, b.Y), 1));}}//检测a点右侧是否能够垂直直连for (int j = a.X; j < Row; j++){if (this.LinkBoard[j, a.Y] == 0 && this.LinkBoard[j, b.Y] == 0 && vertical(new Point(j, a.Y), new Point(j, b.Y))){linkList.Add(new Line(new Point(j, a.Y), new Point(j, b.Y), 1));}}return linkList;}}// 两个拐点之间构成连线类public class Line{public Point a;public Point b;public int direct; //连线方向———  1:水平直连   0:垂直直连public Line(Point a, Point b, int direct)//Line是个构造函数{this.a = a;this.b = b;this.direct = direct;}}
}

窗体功能实现

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;namespace Linkup
{public partial class MainWindow : Form{private int curDelay = 0;                           // 当前剩余时间private LinkClass LinkObj = new LinkClass();        // 连连看功能类对象(变量),以后在这对象层面上操作private Point first = new Point(-1, -1);            // 第一次点击的位置private Point second = new Point(-1, -1);           // 第二次点击的位置private int score = 0;                              // 分数private bool isClosing = false;                     // 界面是否关闭public MainWindow(){InitializeComponent();//以下采用双缓冲方式,减少闪烁this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);this.SetStyle(ControlStyles.UserPaint, true);// 给该对象添加点击事件this.LinkObj.SucClick += Link_SucClick;this.LinkObj.FailClick += Link_FailClick;}private void Form1_Load(object sender, EventArgs e){//在combox加载三种关卡comboBox1.Items.Add("8*8");comboBox1.Items.Add("10*10");comboBox1.Items.Add("12*12");comboBox2.Items.Add("图片");comboBox2.Items.Add("数字");}// 初始化连连看数组int hang = 0;   // 行列数private void InitLinkGameOne(){// 难度设置if (comboBox1.Text == ""|| comboBox2.Text==""){MessageBox.Show("请选择难度或者模式", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);}else{// 选择的关卡难度文本string Diffcult = comboBox1.Text;//根据对应难度初始化数组并给n赋值switch (Diffcult){case "8*8":hang = 8;break;case "10*10":hang = 10;break;case "12*12":hang = 12;break; }// 用于产生不重复随机数long tick = DateTime.Now.Ticks;Random r = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));// 棋盘周围空出一圈0int[,] linkBoard = new int[hang + 2, hang + 2];this.LinkObj.Row = hang + 2;this.LinkObj.Col = hang + 2;// 将内置元素进行随机数分配等操作for (int row = 1; row < hang + 1; row++){for (int col = 1; col < (hang + 1)/2+1; col++){int rnd;switch (hang){case 8:rnd = r.Next(1, 1000) % 13 + 1;//13种图break;case 10:rnd = r.Next(1, 1000) % 15 + 1;//15种图break;case 12:rnd = r.Next(1, 1000) % 17 + 1;//17种图break;default:rnd = 0;break;}// n种图string img = string.Format("_1_{0}", rnd.ToString("00"));//给图片命名linkBoard[row, col] = rnd;                      // 赋给每个位置随机数字linkBoard[row, (hang + 1) / 2 + col] = rnd;     // 赋给相对位置随机数字}}this.LinkObj.LinkBoard = linkBoard;//初始化棋盘this.TableLp.Enabled = false;// 控件变量等初始化this.btnStart.Enabled = false;//设置时间以及按钮this.curDelay = pbTimeDelay.Maximum;//让剩余时间为计时条的最大值this.pbTimeDelay.Value = this.curDelay;//给进度条赋值this.lblTimeDelay.Text = this.curDelay.ToString();//显示剩余时间this.TableLp.Enabled = true;score = 400;//计分// 启动用多线程计时器函数timeDelay = new Thread(timeDelay_Tick);//ma.Set();timeDelay.Start();  //启动定时器线程}}// 获取图片名internal static Bitmap GetImageByName(string name){// 全局资源搜索object obj = global::Linkup.Properties.Resources.ResourceManager.GetObject(name, global::Linkup.Properties.Resources.Culture);return ((Bitmap)(obj));}// 打乱顺序函数private void Reset(){//设置tableLayoutPanel的列数和行数this.TableLp.ColumnCount = hang + 2;this.TableLp.RowCount = hang + 2;// 清除残余属性this.TableLp.ColumnStyles.Clear();this.TableLp.RowStyles.Clear();this.TableLp.Controls.Clear();for (int i = 0; i < hang + 2; i++){//设置等宽等高  边缘2,里面内容10if (i == 0 || i == hang + 1){this.TableLp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 2));this.TableLp.RowStyles.Add(new RowStyle(SizeType.Percent, 2));}else{this.TableLp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 10));this.TableLp.RowStyles.Add(new RowStyle(SizeType.Percent, 10));}}this.TableLp.Padding = new Padding(1);//约束控件内边距this.TableLp.CellBorderStyle = TableLayoutPanelCellBorderStyle.None;    // 不显示边框线long tick = DateTime.Now.Ticks;// 用于产生不重复随机数Random r = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));int[,] linkBoard = new int[hang + 2, hang + 2];linkBoard = this.LinkObj.LinkBoard;//将这个对象的数组值赋给这个变量,便于操作// 将内置元素进行随机贴图等操作int rnd, rnd2;// 打乱二维数组顺序for (int row = 1; row < hang + 1; row++){for (int col = 1; col < hang + 1; col++){// 产生随机数rnd = r.Next(1, 1000) % hang + 1;rnd2 = r.Next(1, 1000) % hang + 1;int temp = linkBoard[rnd, rnd2];linkBoard[rnd, rnd2] = linkBoard[row, col];linkBoard[row, col] = temp;}}this.LinkObj.LinkBoard = linkBoard;//初始化棋盘for (int row = 1; row < hang + 1; row++){for (int col = 1; col < hang + 1; col++){if (linkBoard[row, col] != 0){if (comboBox2.Text=="图片"){string img = string.Format("_1_{0}", linkBoard[row, col].ToString("00"));//给图片命名Button btnLinker = new Button() // 设置每个小按钮的相关属性{BackgroundImage = GetImageByName(img),//给按钮填充背景图片//Text = rnd.ToString(),BackgroundImageLayout = ImageLayout.Stretch,//背景图片拉伸Tag = img,//标签Name = string.Format("{0},{1}", row, col),//将按钮的名字分成为x,yDock = DockStyle.Fill,//调整控件的层次,填充Margin = new Padding(1),//调整外边距};btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件this.TableLp.Controls.Add(btnLinker, col, row);}else{string img = string.Format("_1_{0}", linkBoard[row, col].ToString("00"));//给图片命名Button btnLinker = new Button() // 设置每个小按钮的相关属性{Text = linkBoard[row, col].ToString(),Tag = img,//标签Name = string.Format("{0},{1}", row, col),//将按钮的名字分成为x,yDock = DockStyle.Fill,//调整控件的层次,填充Margin = new Padding(1),//调整外边距};btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件this.TableLp.Controls.Add(btnLinker, col, row);}}}}score -= 400;lableScore.Text = "分数:" + score.ToString();}// 判断成功private void Judge(){int count=0;// 将内置元素进行随机数分配等操作for (int row = 1; row < hang + 1; row++){for (int col = 1; col < hang + 1; col++){if (this.LinkObj.LinkBoard[row,col]==0){count++;}}}if (count==hang*hang){MessageBox.Show("拼完了,你可真厉害!","提示",MessageBoxButtons.OK,MessageBoxIcon.Asterisk);this.TableLp.Enabled = false;btnStart.Enabled = true;timetemp = this.curDelay;this.curDelay = 0;lblTimeDelay.Text = timetemp.ToString();}}// 事件// 每个小图片按钮单击事件private void BtnLinker_Click(object sender, EventArgs e){Button btnLinker = (Button)sender;                  // 事件参数btnLinker.FlatStyle = FlatStyle.Flat;               // 决定按钮的外观btnLinker.FlatAppearance.BorderColor = Color.Red;   // 按钮外边框颜色btnLinker.FlatAppearance.BorderSize = 2;            // 按钮外边框的大小string[] loc = btnLinker.Name.Split(',');           // 将按钮的名字分成x,yif (!LinkObj.IsChecked(first))                      // 第一次点给第一个点赋值{first = new Point(int.Parse(loc[0]), int.Parse(loc[1]));}else// 第二次点给第二个参数赋值{second = new Point(int.Parse(loc[0]), int.Parse(loc[1]));// 如果就第二次点击,则进行判断LinkObj.LinkLine(first, second);}}// 计时器线程Thread timeDelay;       // 线程变量int timetemp = 0;       // 计时器中间变量// 开始按钮事件private void btnStart_Click(object sender, EventArgs e){if (btnStart.Text=="开始游戏"){// 先初始化连连看板InitLinkGameOne();// 打乱顺序Reset();}else if (btnStart.Text == "继续游戏"){//ma.Set();//信号打开,不阻塞当前线程btnStart.Text = "开始游戏";btnStart.Enabled = false;this.TableLp.Enabled = true;this.curDelay= timetemp;// 将中间变量赋值给新的当前时间lblTimeDelay.Text = this.curDelay.ToString();timeDelay = new Thread(new ThreadStart(this.timeDelay_Tick));//新的计时器timeDelay.Start();}}// 结束按钮private void btnStop_Click(object sender, EventArgs e){this.curDelay = 0;Thread.Sleep(100);btnStart.Text = "开始游戏";}// 重新排列private void btnReset_Click(object sender, EventArgs e){// 扣分if (score<= -400){MessageBox.Show("你已经负债累累,不能再扣分了!","提示",MessageBoxButtons.OK,MessageBoxIcon.Stop);}else{Reset();}}// 暂停按钮private void btnPause_Click(object sender, EventArgs e){this.TableLp.Enabled = false;btnStart.Enabled = true;btnStart.Text = "继续游戏";timetemp = this.curDelay;this.curDelay = 0;lblTimeDelay.Text = timetemp.ToString();}// 定时器事件:时间剩余void timeDelay_Tick(){//ma.WaitOne(); // 根据是否收到信号判断是否阻塞当前线程while (this.curDelay > 0 )   // 当前时间大于零{if (!isClosing){this.curDelay--;this.pbTimeDelay.Value = this.curDelay;this.lblTimeDelay.Text = this.curDelay.ToString();}Thread.Sleep(1000);}if (!isClosing){this.btnStart.Enabled = true;this.TableLp.Enabled = false;}}// 连接成功事件private void Link_SucClick(object sender, EventArgs e){//连接成功,则删除,重新初始化this.TableLp.GetControlFromPosition(this.first.Y, this.first.X).Visible = false;//第一块消失this.TableLp.GetControlFromPosition(this.second.Y, this.second.X).Visible = false;//第二块消失this.first = new Point(-1, -1);//初始化第一块的位置this.second = new Point(-1, -1);//初始化第一块的位置this.score += 100;  this.lableScore.Text = string.Format("分数:{0}", score);// 消除成功加时,保证进度条不会消失if (curDelay <= 98){curDelay += 2;}else if (curDelay == 99){curDelay += 1;}// 判断是否连完Judge();}// 连接失败事件private void Link_FailClick(object sender, EventArgs e){//连接失败,则第一个按钮取消选择Control c1 = this.TableLp.GetControlFromPosition(this.first.Y, this.first.X);((Button)c1).FlatAppearance.BorderSize = 0;//第一块的外边框消失this.first = new Point(this.second.X, this.second.Y);this.second = new Point(-1, -1);}// 界面关闭事件private void MainForm_FormClosing(object sender, FormClosingEventArgs e){this.isClosing = true;this.btnStop_Click(null, null);// 界面关闭,终止线程if (this.timeDelay != null && this.timeDelay.ThreadState == ThreadState.Running){this.timeDelay.Abort();}}}
}

C#窗体连连看小游戏(超详细)相关推荐

  1. 飞机大战小游戏(超详细)

    偷学Python之最后的项目二:飞机大战小游戏(超详细) 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志.--苏轼 甜甜先说 这次用Python中的pygame模块来完成一个飞机大战的小游戏:基本思 ...

  2. Python实现烧脑猜数字小游戏——超详细注解

    文章目录 一.需求分析 二.编程思路 三.完整代码 引用源自   本例是我在中国MOOC学习基础语法时做到的小例子,这里加上我的理解和一些注释分享给大家希望能够对大家有所帮助~ 一.需求分析 案例:烧 ...

  3. JS实现飞机大战小游戏 超详细 附源码和原图

    目录 效果展示 源码 HTML CSS JS 原图 效果展示 HTML+CSS+JS做打飞机小游戏 开始界面 选择你喜欢的飞机开始游戏 按下空格键 开始/暂停  有三种敌机 可以设置飞机生命值,生命值 ...

  4. 用Python做个打飞机小游戏超详细教程

    我知道你们一定想先看效果如何 00 目录 整体框架 开始之前-精灵类Sprite 子弹类class Bullet 玩家飞机类class Player 敌机类class Enemy 游戏主体循环以及帧率 ...

  5. python飞机游戏视频教程_10分钟教你用Python做个打飞机小游戏超详细教程

    01 前言 这次还是用python的pygame库来做的游戏.关于这个库的内容,读者可以上网了解一下.本文只讲解用到的知识.代码参考自网上,自己也做了一点代码简化.尽量把最核心的方面用最简单的方式呈现 ...

  6. 教你用Python做个打飞机小游戏超详细教程

    01 前言 这次还是用python的pygame库来做的游戏.关于这个库的内容,读者可以上网了解一下.本文只讲解用到的知识.代码参考自网上,自己也做了一点代码简化.尽量把最核心的方面用最简单的方式呈现 ...

  7. C语言实现三子棋小游戏---超详细讲解

    实现步骤 第1步---打印一个菜单提示选择是否进入游戏 第2步---创建棋盘 第3步---初始化棋盘 第4步---打印棋盘 第5步---下棋,并判断输赢 第6步---将上述函数做成项目 完整代码 第1 ...

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

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

  9. 基于QT实现简单的连连看小游戏

    资源下载地址:https://download.csdn.net/download/sheziqiong/85788216 资源下载地址:https://download.csdn.net/downl ...

  10. 基于stm32、0.96寸OLED实现的贪吃蛇小游戏(详细源码注释)

    简介:本实验基于stm32最小系统.0.96寸OLED(68*128)和摇杆实现一个经典的贪吃蛇小游戏.项目源码地址:点击下载. 硬件设计: 普通摇杆,0.96寸OLED 单色屏幕(SPI协议通讯), ...

最新文章

  1. 顺序表-有序顺序表表示集合-并集(两者比较,小者放入C,未遍历继续放入)
  2. make memuconfig的问题:'make menuconfig' requires the ncurses libraries
  3. 2018 Spring Team Contest D HDU - 6023、HDU - 6024、HDU - 6025 、HDU - 6027 、HDU - 6029
  4. mysql笔记(windows上安装)
  5. python写一个自动登录脚本_Python 脚本登录交换机实现自动配置备份
  6. hibernate级联的概念
  7. DOM节点中属性nodeName、nodeType和nodeValue的区别 Delphi
  8. java String 常用方法集合
  9. 全新卡盟系统PHP版 集成易支付_2020年全新PHP全开源易支付系统完整源码 带多个支付接口...
  10. w ndows7安不上HP1020,惠普1020打印机驱动程序
  11. 数字图像处理冈萨雷斯——开始
  12. React移动端h5日历组件react-h5-calendar周日历 月日历 周视图 月视图自定义标记点
  13. 如何打造一个高效的研发团队
  14. 计算机打开后任务栏有但桌面不显示不出来,任务栏不显示打开的窗口,教您打开的窗口在任务栏上显示不出来...
  15. python 读取文件报错:UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xb5 in position 0: invalid start
  16. ios html5 audio mp3,H5 audio 微信端 在IOS上不能播放音乐
  17. codeforcesf382D-厉害的我的哥(德巴赫)
  18. 介绍Java -D 命令行参数
  19. Reflexive公司小游戏下载地址
  20. python中实现微信登录

热门文章

  1. ContentProvider跨进程通信
  2. IPC进程间通信/跨进程通信
  3. 验证性因子分析(一)
  4. 小米路由r2d论坛_小米路由器R2D固件 V2.24.10 官方稳定版
  5. 电信光纤友华PT921G,烽火HG220光猫破解关闭自带路由改桥接拨号教程
  6. 多媒体计算机辅助英语教学,多媒体计算机辅助英语教学初探
  7. Labview温度采集系统
  8. 如何使用MSGEQ7音频频谱分析仪芯片
  9. RS485通信和Modbus通信协议
  10. Binder机制(非常好理解)