[C#] [作品] 小游戏之扫雷 (Mine Sweeper) 开发过程及算法分析 扫雷原理
前几天突然想到做一个扫雷游戏来练练手,记得我刚接触程序的时候,就有一个同学做了个扫雷作为期末作品交上去的,当时还觉得这个扫雷还是比较复杂的,但到了现在这个时候就感觉是很简单的一个小游戏。
扫雷游戏大概流程如下:
- 创建地图。可以用控件的方式来做小格子,也可以用GDI+,但是对于绘图方面不太熟,就采用控件的方式。
- 根据地图的大小来决定地雷数,随机生成地雷。如果不好决定地图大小与地雷数之间的关系,也可以固定地雷数目。
- 计算每个地雷周围格子的数字。这个简单,遍历每个地雷,然后在周围8个格子上各加1就好。
- 事件绑定。处理鼠标左右键的点击,右击标记,左击打开格子。还要处理左右键同时按下的情况,自动打开格子。
- 胜负的判断。
还有其它的一些杂项:
- 格子几个状态下的样式处理(按下、踩中的地雷、标记错误的地雷、旗子、关闭、打开,打开状态下又分为地雷,空白和数字,还有不同数字的颜色处理)。
- 打开格子的处理,数字和地雷直接打开,空白的话自动打开周围8个格子,8个格子中有空白的话继续打开,如此反复。
- 计时。
还有这些由于时间关系还没做:
- 第一次按下时一定不是地雷。这个可以在第一次点击之后再渲染地图,并且在生成地雷时避开点击的坐标。
- 自定义地图及地雷数。
- 重置地图。
使用C#面向对象编程:
MineCell.cs 格子对象
属性:格子底下的数字,-1代表地雷;坐标;格子当前状态。
核心方法:
ChangeStatus 改变格子状态
C#
//改变状态(打开,关闭,按下,旗子,问号,错误标记,踩中地雷)
public void ChangeStatus(string status)
{
this.Status = status;
if (status == "Open")
{
FlatAppearance.MouseDownBackColor = Color.LightGray;
FlatAppearance.MouseOverBackColor = FlatAppearance.MouseDownBackColor;
BackColor = FlatAppearance.MouseDownBackColor;
if ( Number>0 ) ForeColor = MineMap.NumColors[Number-1];
if (Number == -1)
{
Image = boom;
}
else if (Number == 0)
{
Text = "";
}
else
{
Text = Number.ToString();
}
}
else if(status == "Close")
{
FlatAppearance.MouseDownBackColor = Color.LightGray;
FlatAppearance.MouseOverBackColor = Color.FromArgb(250, 250, 250);
BackColor = FlatAppearance.MouseOverBackColor;
Text = "";
Image = null;
}
else if(status == "Down")
{
BackColor = Color.LightGray;
}
else if(status == "Flag")
{
FlatAppearance.MouseDownBackColor = Color.FromArgb(250, 250, 250);
FlatAppearance.MouseOverBackColor = FlatAppearance.MouseDownBackColor;
BackColor = FlatAppearance.MouseDownBackColor;
Image = Properties.Resources.flag;
}
else if(status == "Unknown")
{
FlatAppearance.MouseDownBackColor = Color.FromArgb(250, 250, 250);
FlatAppearance.MouseOverBackColor = FlatAppearance.MouseDownBackColor;
BackColor = FlatAppearance.MouseDownBackColor;
Image = Properties.Resources.boom;
}
else if(status == "Error")
{
FlatAppearance.MouseDownBackColor = Color.LightGray;
FlatAppearance.MouseOverBackColor = FlatAppearance.MouseDownBackColor;
BackColor = FlatAppearance.MouseDownBackColor;
Image = Properties.Resources.error;
}
else if(status == "Hit")
{
FlatAppearance.MouseDownBackColor = Color.Red;
FlatAppearance.MouseOverBackColor = FlatAppearance.MouseDownBackColor;
BackColor = FlatAppearance.MouseDownBackColor;
}
}
MineMap.cs 地图对象
属性:地图数据,地雷总数,地雷坐标,错误标记坐标,以及一些初始化的数据:地图大小,格子大小,标记总数,未打开格子总数,数字对应的颜色,鼠标状态:左键按下,右键按下,左右键同时按下,左右键曾经同时按下。
核心方法:
计算地雷周围的数字
C#
Point[] aroundPoints = GetAroundPoint(mine);
foreach (Point aroundPoint in aroundPoints)
{
MineCell cell = Map[aroundPoint.X, aroundPoint.Y];
if (cell.Number != -1) cell.Number += 1;
}
GetAroundPoint 获取周围8个点
使用Where过滤边界情况
C#
public Point[] GetAroundPoint(Point p)
{
Point[] points = new Point[8];
points[0] = new Point(p.X - 1, p.Y - 1);
points[1] = new Point(p.X , p.Y - 1);
points[2] = new Point(p.X + 1, p.Y - 1);
points[3] = new Point(p.X - 1, p.Y );
points[4] = new Point(p.X + 1, p.Y );
points[5] = new Point(p.X - 1, p.Y + 1);
points[6] = new Point(p.X , p.Y + 1);
points[7] = new Point(p.X + 1, p.Y + 1);
points = points.Where(point => point.X >= 0)
.Where(point => point.X < mapWidth)
.Where(point => point.Y >= 0)
.Where(point => point.Y < mapHeight).ToArray();
return points;
}
左右键同时点击并释放时自动打开周围格子
C#
if (isBothButtonDowned)
{
int aroundFlags = 0;
Point[] aroundPoints = GetAroundPoint(((MineCell)sender).Point);
foreach (Point point in aroundPoints)
{
MineCell aroundCell = Map[point.X, point.Y];
if (aroundCell.Status == "Down") aroundCell.ChangeStatus("Close");
if (aroundCell.Status == "Flag") aroundFlags++;
}
if (cell.Number == aroundFlags && cell.Status == "Open")
{
foreach (Point point in aroundPoints)
{
OpenCell(point);
}
}
}
胜利的判断
当标记的旗子数加上未打开的格子数刚好等于地雷数时,即可判断为完成。
C#
if(CloseCellTotal+FlagsTotal == MineTotal)
{
}
程序及源码下载:
版权声明:本文为原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
本文链接:https://www.qwqoffice.com/article.php?mod=view&tid=26
本文链接:https://www.qwqoffice.com/article.php?mod=view&tid=26
