天天看點

【C#】掃雷遊戲程式設計

一、概述

最近買了心機NOKIA6300,上面有一款掃雷遊戲,閑暇時光總是在玩,玩難度的級别還挺難過的。呵呵

後來就想使用C#寫個掃雷程式玩玩,遊戲玩多了,遊戲的思想就不知不覺的有了,下面所有的代碼是沒有任何參考的情況下,自己寫出來的,有許多瑕疵,程式也沒經過什麼優化,主要的算法思想其實就那麼幾行。

先給出程式的運作界面,要添加一些控件,這裡就不啰嗦了,程式本來有源碼,但是,為了友善,我将所有的代碼都寫在了Form1中(這個習慣不好,嘿嘿)。

【C#】掃雷遊戲程式設計

二、程式設計思路

程式主要的是Bound這個類,繼承了表單中的Button類,我感覺就用按鈕來模拟類比較快,僅僅是考慮快,也許有其他的方法,主要是因為Button控件本身就有單擊事件,掃雷就需要這兩個事件,如果自己寫,可能要寫這兩個事件。

雷陣需要有一個數組雷存儲其狀态,代碼中都有說明了,數組中的每個下标都和雷有對應關系,是以,我給所有的雷添加了X,Y的屬性,這個做是友善雷的定位,在計算周圍雷數時非常有幫助。

添加了雷陣數組後,有兩個事情比較重要:一、随機生成雷的分布,這個算法有很多,我是用自己感覺比較簡單的算法,将所有的雷随機的放在沒有雷的位置上,就這樣。二、計算周圍雷數;在放置了雷後,要計算這個雷周圍的數字,一共是八個位置,其實很簡單,隻要有雷的位置,它的周圍框都加一就好了,但是注意判斷條件就好了,隻要這些框在0-MAX的範圍内就好了。

在初始化了雷陣後,剩下的就簡單了,添加雷的左擊時間和右擊時間,判斷是否踩到雷,是否掃到雷等等。

在掃雷中還有一個比較重要的算法是對掃雷的遞歸,在掃雷遊戲中,如果一個雷上的周圍雷數是零,就可以将它周圍的所有的格子翻開,(因為沒有雷嘛),和計算周圍雷數的方法一樣,隻是這裡調用的是遞歸的方法,隻要給按鈕設定好是否已經翻開的狀态就好了,翻開的按鈕就不進行遞歸操作了。

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}

//炸彈類

public class Bound : System.Windows.Forms.Button

{

private int _Flag; //标志 0未翻開 1翻開 2标記

private int m_status; //狀态 0無炸彈 1有炸彈

private int m_Nabar; //周圍的炸彈數量

private int _x;

private int _y;

public Bound(int x, int y)

{

_Flag = 0;

status = 0;

Nabar = 0;

this._x = x;

this._y = y;

}

public int Flag

{

get { return _Flag; }

set { _Flag = value; }

}

public int status

{

get { return m_status; }

set { m_status = value; }

}

public int Nabar

{

get { return m_Nabar; }

set { m_Nabar = value; }

}

public int x

{

get { return _x; }

set { _x = value; }

}

public int y

{

get { return _y; }

set { _y = value; }

}

}

//矩陣大小

private const int quart = 12;

private static int[,] bounds = new int[quart, quart]; //存放雷的數組

private static Bound[,] bound;

private static int Max_Bound_Count = 15; //炸彈的總量

private static int ClearBounds = 0; //成功清除炸彈數量

private static DateTime startTime; //遊戲開始時間

private void Form1_Load(object sender, EventArgs e)

{

startTime = DateTime.Now;

int i, j;

int bound_count;

//産生雷陣數組

Random rnd = new Random();

for (i = 0; i < quart; i++)

for (j = 0; j < quart; j++)

{

bounds[i, j] = 0;

}

bound_count = 0;

while (bound_count < Max_Bound_Count)

{

i = rnd.Next(quart);

j = rnd.Next(quart);

if (bounds[i, j] == 0)

{

bounds[i, j] = 1;

bound_count++;

}

}

bound = new Bound[quart, quart];

//初始化所有雷

for (i = 0; i < quart; i++)

for (j = 0; j < quart; j++)

{

bound[i, j] = new Bound(i, j);

bound[i, j].status = bounds[i, j];

//面闆的寬度不變,修改每個雷的長和寬

bound[i, j].Width = panel1.Width / quart;

bound[i, j].Height = panel1.Height / quart;

float currentSize;

//currentSize = bound[i,j].Font.Size;

currentSize = 9F;

bound[i, j].Font = new Font(bound[i, j].Font.Name, currentSize,

bound[i, j].Font.Style, bound[i, j].Font.Unit);

//分布雷的位置

bound[i, j].Left = i * bound[i, j].Width;

bound[i, j].Top = j * bound[i, j].Height;

//bound[i, j].Text = i.ToString();

//掃雷事件注冊

bound[i, j].Click += new EventHandler(Btn_Click);

bound[i, j].MouseUp += new MouseEventHandler(BtnRight_Click);

this.panel1.Controls.Add(bound[i, j]);

}

//計算周圍雷的數量

for (i = 0; i < quart; i++)

for (j = 0; j < quart; j++)

{

if (bounds[i, j] == 1)

{

if (i - 1 >= 0 && j - 1 >= 0) bound[i - 1, j - 1].Nabar++;

if (j - 1 >= 0) bound[i, j - 1].Nabar++;

if (i + 1 <= quart - 1 & j - 1 >= 0) bound[i + 1, j - 1].Nabar++;

if (i - 1 >= 0) bound[i - 1, j].Nabar++;

if (i + 1 <= quart - 1) bound[i + 1, j].Nabar++;

if (i - 1 >= 0 && j + 1 <= quart - 1) bound[i - 1, j + 1].Nabar++;

if (j + 1 <= quart - 1) bound[i, j + 1].Nabar++;

if (i + 1 <= quart - 1 && j + 1 <= quart - 1) bound[i + 1, j + 1].Nabar++;

}

}

}

private void ReStartGame()

{

int i, j;

int bound_count;

//清除的雷數清0

ClearBounds = 0;

startTime = DateTime.Now;

//産生雷陣數組

Random rnd = new Random();

for (i = 0; i < quart; i++)

for (j = 0; j < quart; j++)

{

bounds[i, j] = 0;

}

bound_count = 0;

while (bound_count < Max_Bound_Count)

{

i = rnd.Next(quart);

j = rnd.Next(quart);

if (bounds[i, j] == 0)

{

bounds[i, j] = 1;

bound_count++;

}

}

for (i = 0; i < quart; i++)

for (j = 0; j < quart; j++)

{

bound[i, j].Nabar = 0;

bound[i, j].status = bounds[i, j];

bound[i, j].Flag = 0;

bound[i, j].FlatStyle = FlatStyle.Standard;

bound[i, j].BackColor = System.Drawing.SystemColors.Control;

bound[i, j].Text = "";

Image img = null;

bound[i, j].BackgroundImage = img;

}

//計算周圍雷的數量

for (i = 0; i < quart; i++)

for (j = 0; j < quart; j++)

{

if (bounds[i, j] == 1)

{

if (i - 1 >= 0 && j - 1 >= 0) bound[i - 1, j - 1].Nabar++;

if (j - 1 >= 0) bound[i, j - 1].Nabar++;

if (i + 1 <= quart - 1 & j - 1 >= 0) bound[i + 1, j - 1].Nabar++;

if (i - 1 >= 0) bound[i - 1, j].Nabar++;

if (i + 1 <= quart - 1) bound[i + 1, j].Nabar++;

if (i - 1 >= 0 && j + 1 <= quart - 1) bound[i - 1, j + 1].Nabar++;

if (j + 1 <= quart - 1) bound[i, j + 1].Nabar++;

if (i + 1 <= quart - 1 && j + 1 <= quart - 1) bound[i + 1, j + 1].Nabar++;

}

}

}

//左擊排雷

private void Btn_Click(object sender, EventArgs e)

{

Bound vbound = (Bound)sender;

//如果不是未翻開狀态,則退出

if (vbound.Flag != 0) return;

vbound.Flag = 1;

vbound.FlatStyle = FlatStyle.Flat;

if (vbound.status == 0)

{

//bound.BackColor = System.Drawing.Color.Red;

//目前的周圍沒有雷,就進行遞歸的排雷

if (vbound.Nabar == 0)

{

vbound.BackColor = System.Drawing.Color.White;

int i = vbound.x;

int j = vbound.y;

if (i - 1 >= 0 && j - 1 >= 0) Btn_Click(bound[i - 1, j - 1], e);

if (j - 1 >= 0) Btn_Click(bound[i, j - 1], e);

if (i + 1 <= quart - 1 & j - 1 >= 0) Btn_Click(bound[i + 1, j - 1], e);

if (i - 1 >= 0) Btn_Click(bound[i - 1, j], e);

if (i + 1 <= quart - 1) Btn_Click(bound[i + 1, j], e);

if (i - 1 >= 0 && j + 1 <= quart - 1) Btn_Click(bound[i - 1, j + 1], e);

if (j + 1 <= quart - 1) Btn_Click(bound[i, j + 1], e);

if (i + 1 <= quart - 1 && j + 1 <= quart - 1) Btn_Click(bound[i + 1, j + 1], e);

}

else

{

vbound.Text = vbound.Nabar.ToString();

}

}

else if (vbound.status == 1)

{

//vbound.BackColor = System.Drawing.Color.Red;

vbound.BackgroundImage = imageList1.Images[1];

vbound.BackgroundImageLayout = ImageLayout.Stretch;

MessageBox.Show("you lose!");

ReStartGame();

}

CheckGame();

}

//掃雷事件

private void BtnRight_Click(object sender, MouseEventArgs e)

{

Bound vbound = (Bound)sender;

//右擊滑鼠

if (e.Button == MouseButtons.Right)

{

//如果已經翻開過,則傳回

if (vbound.Flag == 1) return;

//未翻開

if (vbound.Flag == 0)

{

//vbound.BackColor = System.Drawing.Color.Red;

vbound.BackgroundImage = imageList1.Images[0];

vbound.BackgroundImageLayout = ImageLayout.Stretch;

if (vbound.status == 1) ClearBounds++;

vbound.Flag = 2;

}

else //标記掃雷

{

Image img = null;

vbound.BackgroundImage = img;

if (vbound.status == 1) ClearBounds--;

vbound.Flag = 0;

}

CheckGame();

}

}

//判斷遊戲狀态

private void CheckGame()

{

int i, j;

//清除所有雷

if (ClearBounds == Max_Bound_Count)

{

//所有雷排完

for (i = 0; i < quart; i++)

for (j = 0; j < quart; j++)

{

if (bound[i, j].Flag == 0)

{

return;

}

}

MessageBox.Show("you win!");

}

//toolStripStatusLabel1.Text = ClearBounds.ToString() + "/" + Max_Bound_Count.ToString();

}

private void 開始ToolStripMenuItem_Click(object sender, EventArgs e)

{

startTime = DateTime.Now;

ReStartGame();

}

private void timer1_Tick(object sender, EventArgs e)

{

TimeSpan ts = new TimeSpan();

ts = DateTime.Now.Subtract(startTime);

toolStripStatusLabel2.Text = DateTime.Parse(ts.ToString()).ToString("HH:mm:ss");

}

}

三、小結

遊戲設定了遊戲的時間,這個是遊戲擴充趣味性的問題了,可以根據這個做個排行榜,遊戲還可以設定一些難度分級,我看了手機遊戲,雷數和陣列數比例為1:9的難度較低,比例為1:4的難度就較大了。遊戲要根據這些參數來動态的變化這些資料,增加遊戲的可玩性。

自己在C#上寫的第一個遊戲程式,有點信心了,呵呵,和新手共勉。

文章原創位址:http://blog.csdn.net/much0726/archive/2009/04/20/4093519.aspx

繼續閱讀