簡單的總結一下,如何利用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,如需轉載請自行聯系原作者