天天看點

C#如何開發掃雷遊戲

簡單的總結一下,如何利用C#進行WinForm 掃雷小遊戲的開發:

掃雷遊戲的主要設計的類有三個: Main、Pane 、MineField

1)Main 是主要窗體,負責項目的啟動和關閉;并協調内部各個組模組化塊的協調工作。

2)Pane是一個方格的封裝,是雷區的重要組建;它表示一個方格的目前狀态,以及是否布雷等資訊。

3)MineField是雷區的封裝,是遊戲的核心組建;它負責方格的布局以及地雷的分布;并控制玩家的基本操作以及正确的響應。

類的實作:

一、 Pane類

功能描述:Pane是一個方格的封裝,是雷區的重要組建;它表示一個方格的目前狀态,以及是否布雷等資訊

One.它所具有的公共屬性:

名稱

可見性

傳回值類型

功能描述

AroundMineCount

public

int

擷取或設定目前方塊周圍地雷的數量

HasMine

Public

bool

擷取或設定目前方塊是否又雷

State

PaneState

擷取或設目前方塊掃雷的狀态

Two.它所具有的公共方法:

傳回類型

參數

Mark

void

把目前方塊标記為【有雷】狀态,即:插上一個小紅旗。

Open

打開該方塊。

打開後如果如果有雷,則顯示地理圖示;否則如果周圍有相鄰的地理,則顯示地雷數量。

Reset

恢複關閉狀态,即:取消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;

                    case 2:

                        this.BackgroundImage = Properties.Resources.Num2;

                    case 3:

                        this.BackgroundImage = Properties.Resources.Num3;

                    case 4:

                        this.BackgroundImage = Properties.Resources.Num4;

                    case 5:

                        this.BackgroundImage = Properties.Resources.Num5;

                    case 6:

                        this.BackgroundImage = Properties.Resources.Num6;

                    case 7:

                        this.BackgroundImage = Properties.Resources.Num7;

                    case 8:

                        this.BackgroundImage = Properties.Resources.Num8;

                }

       }

二、 MineField類

功能描述:CrlMineField是雷區的封裝,是遊戲的核心組建;它負責方格的布局以及地雷的分布;并控制玩家的基本操作以及正确的響應。

One.它所具有的公有方法

InitMineField

Void

int paneNumber, int mineNumber

初始化雷區。布局方格并随機分布地理。

DisplayAll

明示雷區的全部方塊裡的内容。當踩雷以後,給玩家顯示所有地雷位置。

DisplayAround

Pubic

Pane pane

明示與給定方格相關聯的無地雷的方格。玩家點選一個無雷方格後使用。

Two.它有具有的私有方法

GetPanesAround

Private

List<Pane>

擷取與目前方格相鄰的所有方格。

GetMineCountAround

擷取周圍地雷的總數量

GetPaneSize

Size

擷取每個小方格的大小

LayoutPanes

排列有所方格,完成布雷

LayMines

int mineNumber

随機布雷

IsAllMineSweeped

Bool

判斷是否掃雷成功

Three.事件處理

MineField_SizeChanged

object sender, EventArgs e 

如果雷區面闆尺寸有變化,則重新進行布局。

OnPaneMouseDown

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);

        /// 明示雷區的全部方塊裡的内容

        public void DisplayAll()

            foreach (Pane pane in this.Controls)

                if (pane.State != PaneState.Opened)

                    pane.Open();

        /// 明示與給定方格相關聯的無地雷的方格

        /// <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 私有方法

        /// 擷取與目前方格相鄰的所有方格。

        /// <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;

                逐個掃描目前方格四周地雷數目

                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;

        /// 擷取目前方格四周地雷數量

        private int GetMineCountAround(Pane pane)

            int result = 0;

            List<Pane> panes = this.GetPanesAround(pane);

            foreach (Pane p in panes)

                if (p.HasMine)

                    result++;

        /// 擷取目前每個方格的尺寸

        private Size GetPaneSize()

            if (this.Controls.Count == 0)

                return new Size();

                int paneNumber = (int)Math.Sqrt(this.Controls.Count);

                int paneWidth = this.Width / paneNumber;

                int paneHeight = this.Height / paneNumber;

                return new Size(paneWidth, paneHeight);

        /// 排列所有雷區的方格,完成布局。

        private void LayoutPanes()

            if (this.Controls.Count <= 0)

            int paneNumber = (int)Math.Sqrt(this.Controls.Count);

            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++;

        /// 随機部署地雷

        /// <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;

        /// 是否掃雷成功。即:所有地雷都已經正确作出标記

        private bool IsAllMineSweeped()

            int markedCount = 0;

            int mineCount = 0;

                if (pane.HasMine)

                    mineCount++;

                if (pane.State == PaneState.Marked)

                    markedCount++;

                        //存在做了标記但沒有地雷的方格,掃雷沒有正确完成。

                        return false;

            return mineCount == markedCount;

        #endregion

        #region 事件處理

        /// 如果雷區面闆尺寸有變化,則重新進行布局。

        /// 使得通過改變方格大小來整體适應雷區面闆的尺寸。

        /// <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)

                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();//取消小紅旗标記

                        pane.Mark(); //插個小紅旗做标記

                // 所有地雷掃除成功

                if (this.IsAllMineSweeped())

                    if (this.MineSweepingCompleted != null)

                        this.MineSweepingCompleted(this, EventArgs.Empty);

               ExceptionHandler.OnException(ex);

三、Main類

      還未完善,負責各個子產品的協調工作,如導航菜單裡面設定遊戲的開始,結束,時間計時,儲存結果,檢視排行榜,遊戲的級别等,由于周末兩天的時間還未能将其完善,隻實作了一個大體的功能,當然還存在一些Bug,有待調試!

附屬插圖:

本文轉自蓬萊仙羽51CTO部落格,原文連結:http://blog.51cto.com/dingxiaowei/1366816,如需轉載請自行聯系原作者