您的位置:首页 > 编程语言 > C#

C#扫雷游戏详细介绍

2014-01-14 11:05 387 查看


可有可无的废话

刚接触一门语言,做一个小游戏,不仅有利于你对这门语言的产生浓厚的兴趣,同时也能摆脱总是记不住的方法,单词。

我刚接触C#也总喜欢找一些源码的小项目来自我研究,但是总会遇到以下问题。

1,一些源码下载过后,莫名其妙在自己电脑无法运行,

2,因为不懂其中的一个知识点,而项目中又多次出现,导致自己无法继续研究下去。

3,别人的注释总是看不懂。

4,别人写了博客,没有放上自己的源码。自己跟着敲,运行的效果总是出不来。

当然这些问题可能你没有遇到过,你也可能很快就能解决。但对于小白们来说,任何一个小问题,都可能是致命的,可能花上几个小时,甚至几天解决也说不定。

废话不多说了,下面直接和大家一起做扫雷这个经典游戏。

游戏两个阶段

我将游戏分为两个层次


扫雷实例下载地址


一共包括两个阶段,和一个双缓冲的小实例。

基本功能实现(没有标旗)2. 图片效果展示

1.1基本功能实现,展示图





1.2 代码展示

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MineGame
{
public partial class FrmMain : Form
{
Graphics gra;       //画板
Brush mine_no;      //无雷画刷
Brush mine_default; //初始化界面画刷
Brush mine_color;   //地雷画刷

public FrmMain()
{
InitializeComponent();
gra = panGame.CreateGraphics();
mine_no = Brushes.White;
mine_default = Brushes.Gray;
mine_color = Brushes.Red;
}

public struct Box   //表示每一个格子
{
public bool IsMine;//是否为雷
public bool IsShow;//被点击时,是否显示
public int MineNum;//雷周围8格地雷数
}

private Box[,] m_Boxs = new Box[20, 20];      //地图大小
private int mineCount;    //炸弹数
private Point mousePoint; //鼠标当前位置
private bool bFirstClick; //是否第一次点击

private bool IsStart;       //判断游戏是否开始
private bool IsOver=true;   //判断是否踩雷
private int countTime = 0;  //开始后计时用

private int ShowCount = 0;  //显示的box格子数量

//加载事件
private void FrmMain_Load(object sender, EventArgs e)
{
bFirstClick = true;   //第一次点击
mineCount = int.Parse(txtMineCount.Text);       //炸弹的数目

}

//panle的paint事件
private void panGame_Paint(object sender, PaintEventArgs e)
{
DrawMap();  //画地图
}

//画游戏界面事件
private void DrawMap()
{
int showCount2 = 0;//显示格子数量
int i, j;
for (i = 0; i < m_Boxs.GetLength(0); i++)//行数
{
for (j = 0; j < m_Boxs.GetLength(1); j++)//列数
{
if (bFirstClick)
{
m_Boxs[i, j].MineNum = 0;   //初始化格子周围的地雷数
m_Boxs[i, j].IsShow = false;
m_Boxs[i, j].IsMine = false;
gra.FillRectangle(mine_default, j * 25, i * 25, 24, 24);    //初始化游戏界面
}

if (m_Boxs[i, j].IsShow)
{
if (m_Boxs[i, j].IsMine)
{
gra.FillRectangle(mine_color, i * 25, j * 25, 24, 24);//地雷显示红色
}
else
{
showCount2++;
gra.FillRectangle(mine_no, i * 25, j * 25, 24, 24); //无雷区
if (m_Boxs[i, j].MineNum > 0)
{//画出周围地雷数量
Rectangle rect = new Rectangle(i * 25, j * 25, 24, 24);
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
Brush[] brushes = new Brush[] { Brushes.Blue, Brushes.DarkCyan, Brushes.Red, Brushes.Fuchsia, Brushes.DarkTurquoise, Brushes.DarkGoldenrod, Brushes.DarkViolet, Brushes.Black };

gra.DrawString(m_Boxs[i, j].MineNum.ToString(), this.Font, brushes[m_Boxs[i, j].MineNum - 1], rect, sf);
}
}

}

}
ShowCount = showCount2;
if (!IsOver && i == m_Boxs.GetLength(0) - 1) MessageBox.Show("踩雷了");
}
}

//鼠标在panle上的移动事件
private void panGame_MouseMove(object sender, MouseEventArgs e)
{
mousePoint = e.Location;
}

//鼠标在pnale上的点击事件
private void panGame_MouseClick(object sender, MouseEventArgs e)
{
if (!IsStart) return;   //游戏没点击开始,则返回
int x = mousePoint.X / 25;
int y = mousePoint.Y / 25;
if (bFirstClick)
{
FirstClick(x, y);
}
if (m_Boxs[x, y].IsMine)//点到雷了
{ //显示全地图
for (int i = 0; i < m_Boxs.GetLength(0); i++)
{
for (int j = 0; j < m_Boxs.GetLength(1); j++)
{
m_Boxs[i, j].IsShow = true;
}
}
IsStart = false;    //游戏结束,鼠标无法继续点击
IsOver = false;     //表示踩雷了
trmTime.Stop();     //计时停止
}
if (!m_Boxs[x, y].IsShow)
{
Clear(x,y);
}
DrawMap();
//判断是否扫完雷
if (IsSuccess() && IsOver)
{
MessageBox.Show("恭喜你胜利了");
trmTime.Stop();
IsStart = false;
return;
}

}

//判断游戏是否结束
private bool IsSuccess()
{
int count = m_Boxs.GetLength(0) * m_Boxs.GetLength(1);
return count - ShowCount == mineCount ? true : false;
}

//第一次点击事件
private void FirstClick(int x, int y)
{
bFirstClick = false;  //下次不再是第一次点击
#region 给每个格子赋值

#endregion
Random ran = new Random();
int count = 0;
while (count < mineCount) //随机生成不一样的地雷坐标
{
int rx = ran.Next(m_Boxs.GetLength(0)); //列
int ry = ran.Next(m_Boxs.GetLength(1)); //行
if (rx == x && ry == y || m_Boxs[rx, ry].IsMine) continue;
m_Boxs[rx, ry].IsMine = true;
#region 将雷周围8个格子的数字加1
if (rx - 1 >= 0)
m_Boxs[rx - 1, ry].MineNum++;
if (ry - 1 >= 0)
m_Boxs[rx, ry - 1].MineNum++;
if (rx + 1 < m_Boxs.GetLength(0))
m_Boxs[rx + 1, ry].MineNum++;
if (ry + 1 < m_Boxs.GetLength(1))
m_Boxs[rx, ry + 1].MineNum++;

if (rx - 1 >= 0 && ry - 1 >= 0)
m_Boxs[rx - 1, ry - 1].MineNum++;
if (rx + 1 < m_Boxs.GetLength(0) && ry - 1 >= 0)
m_Boxs[rx + 1, ry - 1].MineNum++;
if (rx + 1 < m_Boxs.GetLength(0) && ry + 1 < m_Boxs.GetLength(1))
m_Boxs[rx + 1, ry + 1].MineNum++;
if (rx - 1 >= 0 && ry + 1 < m_Boxs.GetLength(1))
m_Boxs[rx - 1, ry + 1].MineNum++;
#endregion
count++;
}
Clear(x,y);
}

//找出无雷区事件
private void Clear(int x, int y)
{
if (m_Boxs[x, y].IsMine) return;    //如果是炸弹,不要显示
if (m_Boxs[x, y].IsShow) return;    //如果已经被显示,不需要再显示
m_Boxs[x, y].IsShow = true;         //显示此格
if (m_Boxs[x, y].MineNum == 0)      //表示周围没有地雷,则自动清除周围的格子
{
if (y + 1 < m_Boxs.GetLength(1)) Clear(x, y + 1);  //下
if (x + 1 < m_Boxs.GetLength(0)) Clear(x + 1, y);  //右
if (y - 1 >= 0) Clear(x, y - 1);        //上
if (x - 1 >= 0) Clear(x - 1, y);        //左
//斜着的方向
if (x - 1 >= 0 && y - 1 >= 0) Clear(x - 1, y - 1);
if (x + 1 < m_Boxs.GetLength(0) && y - 1 >= 0) Clear(x + 1, y - 1);
if (x + 1 < m_Boxs.GetLength(0) && y + 1 < m_Boxs.GetLength(1)) Clear(x + 1, y + 1);
if (x - 1 >= 0 && y + 1 < m_Boxs.GetLength(1)) Clear(x - 1, y + 1);

}

}

//开始事件
private void btnStart_Click(object sender, EventArgs e)
{
IsStart = true;         //游戏开始,panel可以点击了
trmTime.Start();
}

//游戏开始后,计时开始
private void trmTime_Tick(object sender, EventArgs e)
{
countTime++;
if (countTime.ToString().Length == 1) lbTime.Text ="00"+ countTime.ToString();
else if (countTime.ToString().Length == 2) lbTime.Text = "0" + countTime.ToString();
else lbTime.Text = countTime.ToString();
}

//重新开始
private void btnReset_Click(object sender, EventArgs e)
{
bFirstClick = true;   //第一次点击
mineCount = int.Parse(txtMineCount.Text) ;       //炸弹的数目
IsStart = true;         //游戏界面可以点击
IsOver = true;          //表示没踩雷
lbTime.Text = "000";    //计时清空
countTime = 0;          //计时累加清空
trmTime.Start();        //计时器 打开
DrawMap();
}

}
}

1.3代码解析

游戏规则是:第一次点击,是不能踩到雷的,应该随机产生一定数量的雷,雷附近的格子会显示相应数量的地雷数目,玩家根据格子数量分析,排雷。当排出的格子数=总格子数-地雷数,就算玩家胜利。

•DrawMap(),用来画出游戏界面

•public struct Box,表示界面上的每一个格子。

分别有三个属性,IsMineI:是否为地雷;IsShow:是否显示;MineNum;周围地雷的数量

在其他方法中分别设置每个Box中的这三个属性,然后通过DrawMap()方法,就能画出不同的游戏界面了。

其他方法设置Box属性,只有执行DrawMap()方法,才能在窗体界面上显示。

•Clear(),递归实现;当点击的格子周围没有地雷时,游戏自动寻找四周(上,下,左,右,左上,右上,左 下,右下)没有地雷数的格子,然后显示出来。即把这些Box的IsShow设置为true;

•IsSuccess(),判断游戏是否结束,当排出的格子数=总格子数-地雷数,就算玩家胜利(当然还有其他方法去
判断,由自己喜好决定)

•FirstClick(),表示第一次点击扫雷,此处一定不会是雷,然后随机产生不同坐标的地雷数量。

地雷的Box,属性为IsMineI=true;另外两个属性在其他地方设置。

实现图片显示效果(有标旗)
这个实例代码和上面的基本代码区别不大,所以也不用一一分析。源码在上面也给出了连接。

这都是我用美图秀秀亲自弄的,没什么美术功底,可能看起来不协调,先凑合着用。

因为牵扯到了图片,所以我用的是双缓冲(不懂得可以百度下,我也为大家提供了一个小实例,上面的下载连接有)做的,避免画地图时出现闪烁现象。(这个游戏小,闪烁效果不明显)





3、 代码解析

3.1、基础阶段

a.方法有:

DrawMap:画游戏界面

FirstClick:第一次点击,保证不能点到雷,然后随机生成所有地雷的位置。

panGame_MouseClick:鼠标点击事件

Clear:点击到空白处,则清空周围无雷格子

trmTime_Tick:计时器

IsSuccess:判断是否将雷扫完

b. public struct(结构) Box表示游戏界面的每一个格子,

public bool IsMine;//是否为地雷

public bool IsShow;//格子是否显示

public int MineNum;//周围地雷数(1-8)


每一个格子,都有这三个属性。方便操作地雷的状态。

在源码中的注释很多,基本上一看就懂。

3.2、用图片显示地图和地雷

方法与基础方法类似,只是多了个双缓冲。

附上几张图,给大家看看。









过些时间我想做个,局域网的五子棋对战,感兴趣的朋友,可以关注下我哦。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: