穆翔

## 整体思路：

``````//游戏数据

``````/// <summary>
/// 表示游戏中一个方块区
/// </summary>
public sealed class Square
...``````

``````/// <summary>
/// 方块区状态
/// </summary>
public enum SquareStatus
{
/// <summary>
/// 闲置
/// </summary>
Idle,
/// <summary>
/// 已打开
/// </summary>
Opened,
/// <summary>
/// 已标记
/// </summary>
Marked,
/// <summary>
/// 已质疑
/// </summary>
Queried,
/// <summary>
/// 游戏结束
/// </summary>
GameOver,
/// <summary>
/// 标记失误(仅在游戏结束时用于绘制)
/// </summary>
MarkMissed
}``````

``````/// <summary>
/// 游戏对象
/// </summary>
public sealed class Game
...``````

## 难点攻破：

### 逻辑难点1：布雷

``````/// <summary>
/// 开始游戏
/// </summary>
public void Start()
{
//假设所有方块区均非雷区
for (int i = 0; i < _gameData.GetLength(0); i++)
for (int j = 0; j < _gameData.GetLength(1); j++)
_gameData[i, j] = new Square(new Point(i, j), false, 0);
}``````

``````/// <summary>
/// 布雷
/// </summary>
/// <param name="startPt">首次单击点</param>
private void Mine(Point startPt)
{
Size area = new Size(_gameData.GetLength(0), _gameData.GetLength(1));
List<Point> excluded = new List<Point> { startPt };

//随机创建雷区
for (int i = 0; i < _minesCount; i++)
{
Point pt = GetRandomPoint(area, excluded);
_gameData[pt.X, pt.Y] = new Square(pt, true, 0);
}

//创建非雷区
for (int i = 0; i < _gameData.GetLength(0); i++)
for (int j = 0; j < _gameData.GetLength(1); j++)
if (!_gameData[i, j].Mined)//非雷区
{
int minesAround = EnumSquaresAround(new Point(i, j)).Cast<Square>().Count(square => square.Mined);//周围雷数

_gameData[i, j] = new Square(new Point(i, j), false, minesAround);
}

_gameStarted = true;
}``````

``````/// <summary>
/// 枚举周围所有方块区
/// </summary>
/// <param name="squarePt">原方块区</param>
/// <returns>枚举数</returns>
private IEnumerable EnumSquaresAround(Point squarePt)
{
int i = squarePt.X, j = squarePt.Y;

//周围所有方块区
for (int x = i - 1; x <= i + 1; ++x)//横向
{
if (x < 0 || x >= _gameData.GetLength(0))//越界
continue;

for (int y = j - 1; y <= j + 1; ++y)//纵向
{
if (y < 0 || y >= _gameData.GetLength(1))//越界
continue;

if (x == squarePt.X && y == squarePt.Y)//排除自身
continue;

yield return _gameData[x, y];
}
}
}``````

### 逻辑难点2：当单击区周围无雷区(空白)时，自动批量打开周围所有非雷区

``````//如果是空白区，则递归相邻的所有空白区
if (_gameData[logicalPt.X, logicalPt.Y].MinesAround == 0)
AutoOpenAround(logicalPt);``````
``````/// <summary>
/// 自动打开周围非雷区方块(递归)
/// </summary>
/// <param name="squarePt">原方块逻辑坐标</param>
private void AutoOpenAround(Point squarePt)
{
//遍历周围方块
foreach (Square square in EnumSquaresAround(squarePt))
{
if (square.Mined || square.Status == Square.SquareStatus.Marked || square.Status == Square.SquareStatus.Opened)
continue;

square.LeftClick();//打开
//周围无雷区
if (square.MinesAround == 0)
AutoOpenAround(square.Location);//递归打开
}
}``````

### 绘图难点1：双缓冲以克服闪烁

``````//窗口图面
//缓冲区
//缓冲区图面
``````/// <summary>
/// 绘制一帧
/// </summary>
public void Draw()
{
for (int i = 0; i < _gameData.GetLength(0); i++)
for (int j = 0; j < _gameData.GetLength(1); j++)
_gameData[i, j].Draw(_bufferGraphics);

_wndGraphics.DrawImage(_buffer, new Point(_gameFieldOffset.Width, _gameFieldOffset.Height));
}``````

## 源码：

http://git.oschina.net/muxiangovo/Mine

### 评论(6)

#### 引用来自“蓝水晶飞机”的评论

5年前用vb6 做了个相似的，现在都忘了当初怎么实现的哈哈……
😂 闲着没事写着玩的

#### 引用来自“JenkinZhou”的评论

😄 谢谢阅读
5年前用vb6 做了个相似的，现在都忘了当初怎么实现的哈哈……

#### 引用来自“红薯”的评论

DJY1992
2016/12/21
0
0
Weex 开发小游戏是件很 high 的事儿

2017/10/21
0
0

“微信应用号”就像平地里炸响的一声春雷，在互联网圈内炸开了锅，小代码小程序即将成为主流，H5迎来了自己的第二春。废话少说，关于微信应用号开发技能，你都掌握了吗？ 没掌握的看这里，干...

2016/09/23
0
0
Unity 3D游戏开发 pdf高清下载

2018/10/28
0
0
React-Native学习资料

2016/04/06
298
0

Kettle自定义jar包供javascript使用

CREATE_17

102
0

javail

79
0

73
0

SongAlone

124
0

shzwork

81
0