C#如何开发扫雷游戏
简单的总结一下,如何利用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:源码:
//默认的构造方法
publicPane()
{InitializeComponent();
this.BackgroundImageLayout=ImageLayout.Stretch;
}
//公有属性:
PublicboolHasMine{get;set;}
PublicintAroundMineCount{get;set;}
PublicPaneStateState{get;set;}//由于它有几种状态,设置一个枚举类型属性
publicenumPaneState
{
Closed,//关闭状态
Opened,//打开状态
Marked,//标记状态
}
//共有方法:
publicvoidMark()//标记当前方格为又雷状态,插个小红旗
{
this.BackgroundImage=Properties.Resources.Marked;
this.State=PaneState.Marked;
}
publicvoidReset()//恢复标记状态,取消小红旗标记
{
this.BackgroundImage=null;
this.State=PaneState.Closed;
}
//打开方法
//打开后如果如果有雷,则显示地理图标;否则如果周围有相邻的地理,则显示地雷数量。
publicvoidOpen()
{
if(this.HasMine)
{
this.BackgroundImage=Properties.Resources.MineBomp;
this.Enabled=false;
}
else
{
switch(this.AroundMineCount)
{
case0:
this.BackgroundImage=null;
this.Enabled=false;
break;
case1:
this.BackgroundImage=Properties.Resources.Num1;
this.Enabled=false;
break;
case2:
this.BackgroundImage=Properties.Resources.Num2;
this.Enabled=false;
break;
case3:
this.BackgroundImage=Properties.Resources.Num3;
this.Enabled=false;
break;
case4:
this.BackgroundImage=Properties.Resources.Num4;
this.Enabled=false;
break;
case5:
this.BackgroundImage=Properties.Resources.Num5;
this.Enabled=false;
break;
case6:
this.BackgroundImage=Properties.Resources.Num6;
this.Enabled=false;
break;
case7:
this.BackgroundImage=Properties.Resources.Num7;
this.Enabled=false;
break;
case8:
this.BackgroundImage=Properties.Resources.Num8;
this.Enabled=false;
break;
}
}
}
二、MineField类
功能描述:CrlMineField是雷区的封装,是游戏的核心组建;它负责方格的布局以及地雷的分布;并控制玩家的基本操作以及正确的响应。
One.它所具有的公有方法
名称
可见性
返回值类型
参数
功能描述
InitMineField
Public
Void
intpaneNumber,intmineNumber
初始化雷区。布局方格并随机分布地理。
DisplayAll
Public
Void
无
明示雷区的全部方块里的内容。当踩雷以后,给玩家显示所有地雷位置。
DisplayAround
Pubic
Void
Panepane
明示与给定方格相关联的无地雷的方格。玩家点击一个无雷方格后使用。
Two.它有具有的私有方法
名称
可见性
返回值类型
参数
功能描述
GetPanesAround
Private
List<Pane>
Panepane
获取与当前方格相邻的所有方格。
GetMineCountAround
Private
int
Panepane
获取周围地雷的总数量
GetPaneSize
Private
Size
无
获取每个小方格的大小
LayoutPanes
Private
Void
无
排列有所方格,完成布雷
LayMines
Private
Void
intmineNumber
随机布雷
IsAllMineSweeped
Private
Bool
无
判断是否扫雷成功
Three.事件处理
名称
可见性
返回值类型
参数
功能描述
MineField_SizeChanged
Private
Void
objectsender,EventArgse
如果雷区面板尺寸有变化,则重新进行布局。
OnPaneMouseDown
Private
Void
objectsender,EventArgse
仅处理鼠标左键和右键事件,忽略其他按键。
Four.源码
//事件的委托
publicdelegatevoidMineSweepingCompletedEventHandler(objectsender,EventArgse);
publicdelegatevoidMineSweepingFailedEventHandler(objectsender,EventArgse);
publicpartialclassCrlMineField:UserControl
{
//成功和失败两个委托事件的申明
publiceventMineSweepingCompletedEventHandlerMineSweepingCompleted;
publiceventMineSweepingFailedEventHandlerMineSweepingFailed;
publicCrlMineField()
{
InitializeComponent();
}
///<summary>
///初始化雷区
///</summary>
///<paramname="paneNumber">每排方块的数量</param>
///<paramname="mineNumber">地雷的数量</param>
publicvoidInitMineField(intpaneNumber,intmineNumber)
{
if(mineNumber>=paneNumber*paneNumber)
{
thrownewApplicationException("地雷太多了,不合法游戏规则。");
}
//清空现有的所有方块
if(this.Controls.Count>0)
{
this.Controls.Clear();
}
//添加雷区方格
for(inti=0;i<paneNumber*paneNumber;i++)
{
Panepane=newPane();
pane.MouseDown+=newMouseEventHandler(OnPaneMouseDown);
this.Controls.Add(pane);
}
//布局方格
this.LayoutPanes();
//随机部署地雷
this.LayMines(mineNumber);
//设置每个方格周边的地雷数
foreach(Panepinthis.Controls)
{
p.AroundMineCount=this.GetMineCountAround(p);
}
}
///<summary>
///明示雷区的全部方块里的内容
///</summary>
publicvoidDisplayAll()
{
foreach(Panepaneinthis.Controls)
{
if(pane.State!=PaneState.Opened)
{
pane.Open();
}
}
}
///<summary>
///明示与给定方格相关联的无地雷的方格
///</summary>
///<paramname="pane"></param>
publicvoidDisplayAround(Panepane)
{
if(pane.State==PaneState.Opened||pane.HasMine)
{
return;
}
//明示当前方格本身
pane.Open();
//通过递归明示当前方格四周的所有方格
List<Pane>panesAround=this.GetPanesAround(pane);
foreach(PanepinpanesAround)
{
//如果该方格四周没有相邻的地雷,则递归
if(this.GetMineCountAround(p)==0)
{
this.DisplayAround(p);
}
else
{
if(p.State!=PaneState.Opened&&!p.HasMine)
{
p.Open();
}
}
}
}
#region私有方法
///<summary>
///获取与当前方格相邻的所有方格。
///</summary>
///<paramname="pane">当前方格</param>
///<returns></returns>
privateList<Pane>GetPanesAround(Panepane)
{
List<Pane>result=newList<Pane>();
//通过递归明示当前方格四周的所有方格
intpaneHeight=this.GetPaneSize().Height;
intpaneWidth=this.GetPaneSize().Width;
foreach(Panepinthis.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);
}
}
returnresult;
}
///<summary>
///获取当前方格四周地雷数量
///</summary>
///<paramname="pane">当前方格</param>
///<returns></returns>
privateintGetMineCountAround(Panepane)
{
intresult=0;
List<Pane>panes=this.GetPanesAround(pane);
foreach(Panepinpanes)
{
if(p.HasMine)
{
result++;
}
}
returnresult;
}
///<summary>
///获取当前每个方格的尺寸
///</summary>
///<returns></returns>
privateSizeGetPaneSize()
{
if(this.Controls.Count==0)
{
returnnewSize();
}
else
{
intpaneNumber=(int)Math.Sqrt(this.Controls.Count);
intpaneWidth=this.Width/paneNumber;
intpaneHeight=this.Height/paneNumber;
returnnewSize(paneWidth,paneHeight);
}
}
///<summary>
///排列所有雷区的方格,完成布局。
///</summary>
privatevoidLayoutPanes()
{
if(this.Controls.Count<=0)
{
return;
}
intpaneNumber=(int)Math.Sqrt(this.Controls.Count);
intpaneHeight=this.GetPaneSize().Height;
intpaneWidth=this.GetPaneSize().Width;
intpaneIndex=0;
//绘制雷区方块
intpaneLeft=0;
intpaneTop=0;
for(intcolNum=0;colNum<paneNumber;colNum++)
{
paneTop=colNum*paneHeight;
for(introwNum=0;rowNum<paneNumber;rowNum++)
{
paneLeft=rowNum*paneWidth;
Panepane=this.Controls[paneIndex]asPane;
pane.Location=newPoint(paneLeft,paneTop);//设置方块位置
pane.Size=newSize(paneWidth,paneHeight);//设置方块大小
paneIndex++;
}
}
}
///<summary>
///随机部署地雷
///</summary>
///<paramname="mineNumber"></param>
privatevoidLayMines(intmineNumber)
{
Randomrdm=newRandom();
for(inti=0;i<mineNumber;i++)
{
while(true)
{
intindex=rdm.Next(0,this.Controls.Count);
Panepane=this.Controls[index]asPane;
if(!pane.HasMine)
{
pane.HasMine=true;
break;
}
}
}
}
///<summary>
///是否扫雷成功。即:所有地雷都已经正确作出标记
///</summary>
///<returns></returns>
privateboolIsAllMineSweeped()
{
intmarkedCount=0;
intmineCount=0;
foreach(Panepaneinthis.Controls)
{
if(pane.HasMine)
{
mineCount++;
}
if(pane.State==PaneState.Marked)
{
markedCount++;
if(!pane.HasMine)
{
//存在做了标记但没有地雷的方格,扫雷没有正确完成。
returnfalse;
}
}
}
returnmineCount==markedCount;
}
#endregion
#region事件处理
///<summary>
///如果雷区面板尺寸有变化,则重新进行布局。
///使得通过改变方格大小来整体适应雷区面板的尺寸。
///</summary>
///<paramname="sender"></param>
///<paramname="e"></param>
privatevoidCrlMineField_SizeChanged(objectsender,EventArgse)
{
try
{
this.LayoutPanes();
}
catch(Exceptionex)
{
ExceptionHandler.OnException(ex);
}
}
privatevoidOnPaneMouseDown(objectsender,MouseEventArgse)
{
//仅处理鼠标左键和右键事件,忽略其他按键。
if(e.Button!=MouseButtons.Left&&e.Button!=MouseButtons.Right)
{
return;
}
try
{
Panepane=senderasPane;//获取当前被鼠标点中的方格
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);
}
}
elseif(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(Exceptionex)
{
ExceptionHandler.OnException(ex);
}
}
#endregion
三、Main类
还未完善,负责各个模块的协调工作,如导航菜单里面设置游戏的开始,结束,时间计时,保存结果,查看排行榜,游戏的级别等,由于周末两天的时间还未能将其完善,只实现了一个大体的功能,当然还存在一些Bug,有待调试!
附属插图:
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。