• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#如何开发扫雷游戏

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

简单的总结一下,如何利用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,有待调试!

附属插图:


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#复习①发布时间:2022-07-10
下一篇:
C#开源系统大汇总发布时间:2022-07-10
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap