C#如何开发扫雷游戏

简介:

简单的总结一下,如何利用C#进行WinForm 扫雷小游戏的开发

扫雷游戏的主要设计的类有三个: MainPane MineField

1Main 是主控窗体,负责项目的启动和关闭;并协调内部各个组建模块的协调工作。

2Pane是一个方格的封装,是雷区的重要组建;它表示一个方格的当前状态,以及是否布雷等信息。

3MineField是雷区的封装,是游戏的核心组建;它负责方格的布局以及地雷的分布;并控制玩家的基本操作以及正确的响应。

类的实现:

一、 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,有待调试!

附属插图:


























本文转自蓬莱仙羽51CTO博客,原文链接:http://blog.51cto.com/dingxiaowei/1366816,如需转载请自行联系原作者


相关文章
|
2天前
|
开发框架 前端开发 JavaScript
采用C#.Net +JavaScript 开发的云LIS系统源码 二级医院应用案例有演示
技术架构:Asp.NET CORE 3.1 MVC + SQLserver + Redis等 开发语言:C# 6.0、JavaScript 前端框架:JQuery、EasyUI、Bootstrap 后端框架:MVC、SQLSugar等 数 据 库:SQLserver 2012
|
1月前
|
存储 C# 开发工具
22.C# 中使用变量记录玩家创建的角色名:实现与游戏角色的互动
22.C# 中使用变量记录玩家创建的角色名:实现与游戏角色的互动
14 0
|
1月前
|
数据挖掘 C# 开发工具
采用C#语言开发的全套医院体检系统PEIS源码功能介绍
体检系统,是专为体检中心/医院体检科等体检机构,专门开发的全流程管理系统,通过软件实现检测仪器数据自动提取,内置多级医生工作台,细化工作将体检检查结果汇总,生成体检报告登记到计算机系统中。通过软件系统进行数据分析统计与评判以及建立体检相关的体检档案。从而实现体检流程的信息化,提高工作效率,减少手动结果录入的一些常犯错误。 在实际应用中,医院体检系统能够解决传统体检中手工操作带来的问题,如工作量大、效率低下、易漏检、重检或错检等。通过与医院信息系统(如HIS、LIS、PACS等)的连接,系统能够满足体检中心的日常工作流程,提供更好的管理、统计和查询分析功能。同时,基于网络基础的系统可以在网上传输
23 1
|
3月前
|
定位技术 C# 图形学
Unity和C#游戏编程入门:创建迷宫小球游戏示例
Unity和C#游戏编程入门:创建迷宫小球游戏示例
71 2
|
3月前
|
存储 C#
C#入门开发(Hello World,运算符)
C#入门开发(Hello World,运算符)
27 0
|
4月前
|
JavaScript C#
【傻瓜级JS-DLL-WINCC-PLC交互】2.wincc使用C#开发的.net控件
【傻瓜级JS-DLL-WINCC-PLC交互】2.wincc使用C#开发的.net控件
39 0
|
4月前
|
SQL 关系型数据库 MySQL
C#程序设计案例开发教程
C#程序设计案例开发教程
47 0
|
4月前
|
网络协议 C# C++
BytesIO | C# 超简洁的TCP服务端开发(完整源码+视频教程)
本章将继续利用BytesIO开发TCP的服务端,简洁明了依然是主旋律,我们要在三十行代码内除了实现一个TCP服务端以外,使其支持聊天室(消息转发)、连接数限制、心跳超时检测等功能。 现在,一起跟着视频敲一敲吧!
159 0
BytesIO | C# 超简洁的TCP服务端开发(完整源码+视频教程)
|
4月前
|
存储 算法 数据处理
C# | 上位机开发新手指南(十一)压缩算法
流式压缩 流式压缩是一种能够实时处理数据流的压缩方式,例如音频、视频等实时传输的数据。 通过流式压缩算法,我们可以边读取边压缩数据,并能够随时输出已压缩的数据,以确保数据的实时性和减少存储和传输所需的带宽。 块压缩 块压缩则是将数据划分为固定大小的块,在每个块内进行独立的压缩处理。块压缩通常适用于文件、存储、传输等离线数据处理场景。 字典压缩 字典压缩是一种基于字典的压缩算法,通过建立一个字典来存储一组重复出现的字符串,并将这些字符串替换成字典中相应的索引,从而减少数据的存储和传输。字典压缩算法可以更好地处理数据中的重复模式,因为它们可以通过建立字典来存储和恢复重复出现的字符串。
44 0
C# | 上位机开发新手指南(十一)压缩算法
|
4月前
|
算法 C# 数据安全/隐私保护
C# | 上位机开发新手指南(十)加密算法——ECC
本篇文章我们将继续探讨另一种非对称加密算法——ECC。 严格的说,其实ECC并不是一种非对称加密算法,它是一种基于椭圆曲线的加密算法,广泛用于数字签名和密钥协商。 与传统的非对称加密算法(例如RSA)不同,ECC算法使用椭圆曲线上的点乘法来生成密钥对和进行加密操作,而不是使用大数分解等数学算法。这使得ECC算法具有相同的安全性和强度,但使用更少的位数,因此在资源受限的环境中具有优势。 ECC算法虽然使用公钥和私钥进行加密和解密操作,但是这些操作是基于点乘法实现的,而不是基于大数分解等算法实现的。因此,ECC算法可以被视为一种非对称加密算法的变体,但是它与传统的非对称加密算法有所不同。
130 0
C# | 上位机开发新手指南(十)加密算法——ECC