在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
简单的总结一下,如何利用C#进行WinForm 扫雷小游戏的开发: 扫雷游戏的主要设计的类有三个: Main、Pane 、MineField 1)Main 是主控窗体,负责项目的启动和关闭;并协调内部各个组建模块的协调工作。 2)Pane是一个方格的封装,是雷区的重要组建;它表示一个方格的当前状态,以及是否布雷等信息。 3)MineField是雷区的封装,是游戏的核心组建;它负责方格的布局以及地雷的分布;并控制玩家的基本操作以及正确的响应。 类的实现: 一、 Pane类 功能描述:Pane是一个方格的封装,是雷区的重要组建;它表示一个方格的当前状态,以及是否布雷等信息 One.它所具有的公共属性:
Two.它所具有的公共方法:
Three:源码: //默认的构造方法 public Pane() {InitializeComponent(); this.BackgroundImageLayout = ImageLayout.Stretch; } //公有属性: Public bool HasMine { get; set; } Public int AroundMineCount { get; set; } Public PaneState State { get; set; } //由于它有几种状态,设置一个枚举类型属性 public enum PaneState { Closed, //关闭状态 Opened, //打开状态 Marked, //标记状态 } //共有方法: public void Mark() //标记当前方格为又雷状态,插个小红旗 { this.BackgroundImage = Properties.Resources.Marked; this.State = PaneState.Marked; } public void Reset() //恢复标记状态,取消小红旗标记 { this.BackgroundImage = null; this.State = PaneState.Closed; } //打开方法 //打开后如果如果有雷,则显示地理图标;否则如果周围有相邻的地理,则显示地雷数量。 public void Open() { if (this.HasMine) { this.BackgroundImage = Properties.Resources.MineBomp; this.Enabled = false; } else { switch (this.AroundMineCount) { case 0: this.BackgroundImage = null; this.Enabled = false; break; case 1: this.BackgroundImage = Properties.Resources.Num1; this.Enabled = false; break; case 2: this.BackgroundImage = Properties.Resources.Num2; this.Enabled = false; break; case 3: this.BackgroundImage = Properties.Resources.Num3; this.Enabled = false; break; case 4: this.BackgroundImage = Properties.Resources.Num4; this.Enabled = false; break; case 5: this.BackgroundImage = Properties.Resources.Num5; this.Enabled = false; break; case 6: this.BackgroundImage = Properties.Resources.Num6; this.Enabled = false; break; case 7: this.BackgroundImage = Properties.Resources.Num7; this.Enabled = false; break; case 8: this.BackgroundImage = Properties.Resources.Num8; this.Enabled = false; break; } } } 二、 MineField类 功能描述:CrlMineField是雷区的封装,是游戏的核心组建;它负责方格的布局以及地雷的分布;并控制玩家的基本操作以及正确的响应。 One.它所具有的公有方法
Two.它有具有的私有方法
Three.事件处理
Four.源码 //事件的委托 public delegate void MineSweepingCompletedEventHandler(object sender, EventArgs e); public delegate void MineSweepingFailedEventHandler(object sender, EventArgs e); public partial class CrlMineField : UserControl { //成功和失败两个委托事件的申明 public event MineSweepingCompletedEventHandler MineSweepingCompleted; public event MineSweepingFailedEventHandler MineSweepingFailed; public CrlMineField() { InitializeComponent(); }
/// <summary> /// 初始化雷区 /// </summary> /// <param name="paneNumber">每排方块的数量</param> /// <param name="mineNumber">地雷的数量</param> public void InitMineField(int paneNumber, int mineNumber) { if (mineNumber >= paneNumber * paneNumber) { throw new ApplicationException("地雷太多了,不合法游戏规则。"); } // 清空现有的所有方块 if (this.Controls.Count > 0) { this.Controls.Clear(); } //添加雷区方格 for (int i = 0; i < paneNumber * paneNumber; i++) { Pane pane = new Pane(); pane.MouseDown += new MouseEventHandler(OnPaneMouseDown); this.Controls.Add(pane); }
// 布局方格 this.LayoutPanes(); // 随机部署地雷 this.LayMines(mineNumber); // 设置每个方格周边的地雷数 foreach (Pane p in this.Controls) { p.AroundMineCount = this.GetMineCountAround(p); } } /// <summary> /// 明示雷区的全部方块里的内容 /// </summary> public void DisplayAll() { foreach (Pane pane in this.Controls) { if (pane.State != PaneState.Opened) { pane.Open(); } } } /// <summary> /// 明示与给定方格相关联的无地雷的方格 /// </summary> /// <param name="pane"></param> public void DisplayAround(Pane pane) { if (pane.State == PaneState.Opened || pane.HasMine) { return; } // 明示当前方格本身 pane.Open(); // 通过递归明示当前方格四周的所有方格 List<Pane> panesAround = this.GetPanesAround(pane); foreach (Pane p in panesAround) { // 如果该方格四周没有相邻的地雷,则递归 if (this.GetMineCountAround(p) == 0) { this.DisplayAround(p); } else { if (p.State != PaneState.Opened && !p.HasMine) { p.Open(); }
} } } #region 私有方法 /// <summary> /// 获取与当前方格相邻的所有方格。 /// </summary> /// <param name="pane">当前方格</param> /// <returns></returns> private List<Pane> GetPanesAround(Pane pane) { List<Pane> result = new List<Pane>(); // 通过递归明示当前方格四周的所有方格 int paneHeight = this.GetPaneSize().Height; int paneWidth = this.GetPaneSize().Width; foreach (Pane p in this.Controls) { 逐个扫描当前方格四周地雷数目 if (Math.Abs(p.Top - pane.Top) == 0 && Math.Abs(p.Left - pane.Left) == paneWidth || Math.Abs(p.Left - pane.Left) == 0 && Math.Abs(p.Top - pane.Top) == paneHeight || Math.Abs(p.Top - pane.Top) == paneHeight && Math.Abs(p.Left - pane.Left) == paneWidth || Math.Abs(p.Left - pane.Left) == paneWidth && Math.Abs(p.Top - pane.Top) == paneHeight) { result.Add(p); } } return result; } /// <summary> /// 获取当前方格四周地雷数量 /// </summary> /// <param name="pane">当前方格</param> /// <returns></returns> private int GetMineCountAround(Pane pane) { int result = 0; List<Pane> panes = this.GetPanesAround(pane); foreach (Pane p in panes) { if (p.HasMine) { result++; } } return result; } /// <summary> /// 获取当前每个方格的尺寸 /// </summary> /// <returns></returns> private Size GetPaneSize() { if (this.Controls.Count == 0) { return new Size(); } else { int paneNumber = (int)Math.Sqrt(this.Controls.Count); int paneWidth = this.Width / paneNumber; int paneHeight = this.Height / paneNumber; return new Size(paneWidth, paneHeight); } } /// <summary> /// 排列所有雷区的方格,完成布局。 /// </summary> private void LayoutPanes() { if (this.Controls.Count <= 0) { return; } int paneNumber = (int)Math.Sqrt(this.Controls.Count); int paneHeight = this.GetPaneSize().Height; int paneWidth = this.GetPaneSize().Width; int paneIndex = 0; // 绘制雷区方块 int paneLeft = 0; int paneTop = 0; for (int colNum = 0; colNum < paneNumber; colNum++) { paneTop = colNum * paneHeight; for (int rowNum = 0; rowNum < paneNumber; rowNum++) { paneLeft = rowNum * paneWidth; Pane pane = this.Controls[paneIndex] as Pane; pane.Location = new Point(paneLeft, paneTop);//设置方块位置 pane.Size = new Size(paneWidth, paneHeight);//设置方块大小 paneIndex++; } } } /// <summary> /// 随机部署地雷 /// </summary> /// <param name="mineNumber"></param> private void LayMines(int mineNumber) { Random rdm = new Random(); for (int i = 0; i < mineNumber; i++) { while (true) { int index = rdm.Next(0, this.Controls.Count); Pane pane = this.Controls[index] as Pane; if (!pane.HasMine) { pane.HasMine = true; break; } } } } /// <summary> /// 是否扫雷成功。即:所有地雷都已经正确作出标记 /// </summary> /// <returns></returns> private bool IsAllMineSweeped() { int markedCount = 0; int mineCount = 0; foreach (Pane pane in this.Controls) { if (pane.HasMine) { mineCount++; } if (pane.State == PaneState.Marked) { markedCount++; if (!pane.HasMine) { //存在做了标记但没有地雷的方格,扫雷没有正确完成。 return false; } } } return mineCount == markedCount; } #endregion #region 事件处理 /// <summary> /// 如果雷区面板尺寸有变化,则重新进行布局。 /// 使得通过改变方格大小来整体适应雷区面板的尺寸。 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void CrlMineField_SizeChanged(object sender, EventArgs e) { try { this.LayoutPanes(); } catch (Exception ex) { ExceptionHandler.OnException(ex); }
} private void OnPaneMouseDown(object sender, MouseEventArgs e) { // 仅处理鼠标左键和右键事件,忽略其他按键。 if (e.Button != MouseButtons.Left && e.Button != MouseButtons.Right) { return; } try { Pane pane = sender as Pane; //获取当前被鼠标点中的方格 if (e.Button == MouseButtons.Left)//鼠标左键 { if (pane.HasMine) //踩地雷了 { pane.Open(); this.DisplayAll(); if (this.MineSweepingFailed != null) { this.MineSweepingFailed(this, EventArgs.Empty); }
} else { //明示当前方格相邻的所有无地雷的方格 this.DisplayAround(pane); } } else if (e.Button == MouseButtons.Right)//鼠标右键 { if (pane.State == PaneState.Marked) { pane.Reset();//取消小红旗标记 } else { pane.Mark(); //插个小红旗做标记 } } // 所有地雷扫除成功 if (this.IsAllMineSweeped()) { if (this.MineSweepingCompleted != null) { this.MineSweepingCompleted(this, EventArgs.Empty); } } } catch (Exception ex) { ExceptionHandler.OnException(ex); } } #endregion 三、Main类 还未完善,负责各个模块的协调工作,如导航菜单里面设置游戏的开始,结束,时间计时,保存结果,查看排行榜,游戏的级别等,由于周末两天的时间还未能将其完善,只实现了一个大体的功能,当然还存在一些Bug,有待调试! 附属插图: |
请发表评论