C#如何开发扫雷游戏
简单的总结一下,如何利用C#进行WinForm 扫雷小游戏的开发:
扫雷游戏的主要设计的类有三个: Main、Pane 、MineField
1)Main 是主控窗体,负责项目的启动和关闭;并协调内部各个组建模块的协调工作。
2)Pane是一个方格的封装,是雷区的重要组建;它表示一个方格的当前状态,以及是否布雷等信息。
3)MineField是雷区的封装,是游戏的核心组建;它负责方格的布局以及地雷的分布;并控制玩家的基本操作以及正确的响应。
类的实现:
一、 Pane类
功能描述:Pane是一个方格的封装,是雷区的重要组建;它表示一个方格的当前状态,以及是否布雷等信息
One.它所具有的公共属性:
名称 |
可见性 |
返回值类型 |
功能描述 |
AroundMineCount |
public |
int |
获取或设置当前方块周围地雷的数量 |
HasMine |
Public |
bool |
获取或设置当前方块是否又雷 |
State |
Public |
PaneState |
获取或设当前方块扫雷的状态 |
Two.它所具有的公共方法:
名称 |
可见性 |
返回类型 |
参数 |
功能描述 |
Mark |
public |
void |
无 |
把当前方块标记为【有雷】状态,即:插上一个小红旗。 |
Open |
public |
void |
无 |
打开该方块。 打开后如果如果有雷,则显示地理图标;否则如果周围有相邻的地理,则显示地雷数量。 |
Reset |
public |
void |
无 |
恢复关闭状态,即:取消Mark()的操作结果。 |
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.它所具有的公有方法
名称 |
可见性 |
返回值类型 |
参数 |
功能描述 |
InitMineField |
Public |
Void |
int paneNumber, int mineNumber |
初始化雷区。布局方格并随机分布地理。 |
DisplayAll |
Public |
Void |
无 |
明示雷区的全部方块里的内容。当踩雷以后,给玩家显示所有地雷位置。 |
DisplayAround |
Pubic |
Void |
Pane pane |
明示与给定方格相关联的无地雷的方格。玩家点击一个无雷方格后使用。 |
Two.它有具有的私有方法
名称 |
可见性 |
返回值类型 |
参数 |
功能描述 |
GetPanesAround |
Private |
List<Pane> |
Pane pane |
获取与当前方格相邻的所有方格。 |
GetMineCountAround |
Private |
int |
Pane pane |
获取周围地雷的总数量 |
GetPaneSize |
Private |
Size |
无 |
获取每个小方格的大小 |
LayoutPanes |
Private |
Void |
无 |
排列有所方格,完成布雷 |
LayMines |
Private |
Void |
int mineNumber |
随机布雷 |
IsAllMineSweeped |
Private |
Bool |
无 |
判断是否扫雷成功 |
Three.事件处理
名称 |
可见性 |
返回值类型 |
参数 |
功能描述 |
MineField_SizeChanged |
Private |
Void |
object sender, EventArgs e |
如果雷区面板尺寸有变化,则重新进行布局。 |
OnPaneMouseDown |
Private |
Void |
object sender, EventArgs e |
仅处理鼠标左键和右键事件,忽略其他按键。 |
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,有待调试!
附属插图:
C#如何开发扫雷游戏相关推荐
- javaScript开发扫雷游戏
前言 扫雷是windows自带的游戏,通过翻开小方块,来推理雷的位置 本文讲解如何通过javaScript制作扫雷游戏,并运用canvas画布绘制windows扫雷效果 一.技术拆分 canvas画图 ...
- java开发扫雷游戏
我又来水博客了--把之前的库存发一发~ 开发工具:IDEA 不到三百行代码即可开发一个简单的扫雷小游戏,只需掌握Java基础语法知识以及部分swing知识点即可~ 运行效果如图: 项目中图片可自行从网 ...
- 如何利用C#开发“扫雷”小游戏
本文详细说明了利用C#语言开发"扫雷"小游戏的开发过程.
- 利用java开发简易版扫雷游戏
1.简介 学了几周的Java,闲来无事,写个乞丐版的扫雷,加强一下Java基础知识. 2.编写过程 编写这个游戏,一共经历了三个阶段,编写了三个版本的游戏代码. 第一版:完成了扫雷游戏的基本雏形,实现 ...
- HTML5游戏开发-扫雷及其算法研究
吕蒙曰:士隔三月[1],当刮目相看.所以,在下在这三月中发奋图强,花了约莫8节信息课的时间研究扫雷.呜呼,由于在下才能尚且不足,所以也就只能勉强打过中级难度的吧.不过,一边玩的同时,我还一边对扫雷这个 ...
- 基于html扫雷游戏毕业论文,扫雷游戏的设计与开发毕业设计毕业论文正稿
扫雷游戏的设计与开发毕业设计毕业论文正稿 (18页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.9 积分 word格式扫雷游戏的设计与开发论文作者 ...
- 扫雷游戏开发基本思路
推荐阅读: 我的CSDN 我的博客园 QQ群:704621321 今天给大家介绍传统扫雷游戏的制作思路吧,为什么选择这个游戏呢? 1.它太金典了,金典到我们都知道,都玩过,都会玩: 2.它也是一个轻量 ...
- C/C++技术总监带你打造经典扫雷游戏,从未见过如此详细的开发解析!
<扫雷>是一款大众类的益智小游戏,于1992年发行.游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输. VC++ 扫雷游戏空壳源代码,代码 ...
- python3扫雷代码_GitHub - pantaduce/minesweeper: Python代码编写的扫雷游戏
Minesweeper(扫雷) 这是一个由Python编写的扫雷游戏,基于tkinter/Tkinter开发,支持python2和python3. 功能 概述 基本功能:左键扫雷,右键标记 记录游戏步 ...
最新文章
- android 动态获取权限有哪些,Android 6.0+ 动态获取权限
- 微信红包的架构设计简介
- 黄聪:如何用代码设置控制自己网站的网页在360浏览器打开时强制优先使用极速模式,而非兼容模式...
- 计算机没有autoCAD_挑战在一年内用晚上业余时间学会灵活运用CAD(1)|cad|autocad|图学|计算机|电子电路...
- 重庆大学计算机学院就读,唐远炎(计算机学院)老师 - 重庆大学 - 院校大全
- linux缓存代码,Linux使用的缓存
- Springboot 使用Mybatis对postgreSQL实现CRUD
- webpack——快速入门【一】
- 离开中国多年后,Google 搜索想回来了!
- Linux 命令(5)—— shutdown 命令
- 【洛谷1527】 [国家集训队]矩阵乘法(整体二分)
- 浅析天际网、IT260、微人脉等中国职业社交网站
- sql字段合并mysql_sql合并字段
- 微波雷达感应模块,智能马桶传感方案,智能化生活
- mysql 查询父子关系_查询父子关系 · dotnetcore/FreeSql Wiki · GitHub
- ie浏览器透明背景设置
- 举个栗子!Tableau技巧(7):如何做帕累托图
- 小巧简便且美观的桌面便签记事备忘工具
- 【上传】Nginx 上传文件
- JavaWeb购物车项目二