C#窗体连连看小游戏(超详细)
2021/6/10 20:21:38
本文主要是介绍C#窗体连连看小游戏(超详细),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章目录
- 成品链接
- 项目展示
- 各部分功能解释
- 一、项目分析
- 二、项目设计
- 全部代码
- 窗体控件命名
- 连连看功能类
- 窗体功能实现
https://download.csdn.net/download/weixin_45525272/13713452
项目展示一、项目分析
系统设计思路
(1)设计连连看功能类:LinkClass,Line
(2)设计两个事件:成功事件和失败事件
(3)成功事件:1.垂直事件2.水平事件3.一个拐点事件4.两个拐点事件
(4)失败事件:
- 一个按钮点击两次
- 点击的两个按钮所带图片不一样
- 点击两个具有相同图案的按钮但没有通路
(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,里面内容10 if (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,y Point 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(); } #endregion private 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 { // 如果点击的不是同一个图案,直接返回false return 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,y Point 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)); // 棋盘周围空出一圈0 int[,] 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,里面内容10 if (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,y Dock = 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,y Dock = 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,y if (!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#窗体连连看小游戏(超详细)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-03-01沐雪多租宝商城源码从.NetCore3.1升级到.Net6的步骤
- 2024-11-18微软研究:RAG系统的四个层次提升理解与回答能力
- 2024-11-15C#中怎么从PEM格式的证书中提取公钥?-icode9专业技术文章分享
- 2024-11-14云架构设计——如何用diagrams.net绘制专业的AWS架构图?
- 2024-05-08首个适配Visual Studio平台的国产智能编程助手CodeGeeX正式上线!C#程序员必备效率神器!
- 2024-03-30C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】
- 2024-03-29c# datetime tryparse
- 2024-02-21list find index c#
- 2024-01-24convert toint32 c#
- 2024-01-24Advanced .Net Debugging 1:你必须知道的调试工具