您好,登錄后才能下訂單哦!
簡單的總結一下,如何利用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,有待調試!
附屬插圖:
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。