主题:[原创]C#扫雷
qiock
[专家分:50] 发布于 2008-12-19 10:54:00
这个扫雷,也是我自己做的,中间碰到了一些问题,比如最让我气愤的是,不知道怎么获得鼠标点击控件的LOCATION坐标,在此感谢,网上的同志,教会了我如果获得鼠标点击控件的句柄,从而解决了这个最大的难题.因为本程序没有给出详细的代码解释,我也正在给每个需要解释的代码段进行解释,在此仅仅将已经做好的程序发表上来//游戏整体思想没有抄袭他人.主要设计步奏没有抄袭他人.
昨天发了个贪吃蛇的,今天再发个扫雷的,准备做个RPG的游戏呢,但是还没有完全的规划好,等做出来了,一定再发过来给大家分享.
最后更新于:2008-12-19 16:54:00
回复列表 (共16个回复)
沙发
qiock [专家分:50] 发布于 2008-12-19 11:30:00
忘记说怎么玩了,没有设置左右一起点击,如果你已经在某个位置的周围8个位置,标记好了你的旗帜,那么你可以用鼠标中键来实现鼠标双击功能,其他的都和WINDOWS自带的扫雷游戏功能一致,在代码区域,我有的地方使用了数组的索引功能,有的地方没有使用,主要为了方便一些初学的能够明白我为什么这样设置,明白超出数组索引范围是个很严重的BUG.
板凳
qiock [专家分:50] 发布于 2008-12-19 11:57:00
还有一个问题,刚刚已经解决,就是在问号的上面点击鼠标中键的问题也已经解决.默认情况下是不可以在问号上方和旗帜上方点击鼠标中键的.
3 楼
qiock [专家分:50] 发布于 2008-12-19 16:56:00
namespace 扫雷
{
public partial class Form1 : Form
{
int[,] lnum;//用来初始化雷区分布的;
int[,] alsoflag = new int[20, 20];//定义一个数组,用来接收此位置是否被标记的,
int sbnum = 50;//定义总的雷数目
int flagnum;//定义您已经标记的雷数
int alsoopennum = 0;//定义已经打开了多少个空格
int mousex;//定义鼠标的X坐标;
int mousey;//定义鼠标的Y坐标
PictureBox[,] lei = new PictureBox[20, 20];//雷区的图片,用来显示的
Image[,] image = new Image[20, 20];//因为有些地方的可能被你标记旗帜,这个用来将你把旗帜取消后,恢复它的原来图片
PictureBox abc = null;//重要内容,句柄
bool rightdown = false;//定义鼠标右键是否按下
bool leftdown = false;//定义鼠标左键是否按下
bool boolwin = false;//是否获胜
bool havedead = false;//是否死亡
public Form1()
{
InitializeComponent();
}
4 楼
qiock [专家分:50] 发布于 2008-12-19 17:04:00
private void panel1_MouseDown(object sender, MouseEventArgs e)
{//这个是主要的函数,它实现了鼠标在控件中的按下动作,包括左键,右键,中键和左右都按下
if (!boolwin&&!havedead)//先看看你死没死,或者获胜了没,如果胜了或者死了就不产生任何动作
{
string a = sender.GetType().ToString();//获取鼠标在哪个控件中按下,即这个控件的句柄
if (e.Button == MouseButtons.Left)
{
this.leftdown = true;//如果按下左键,则左键为真
}
if (e.Button == MouseButtons.Right)
{
this.rightdown = true;//如果按下右键,则右键为真
}
if (this.rightdown &&!(this.leftdown ))//如果只按下了右键,有2种可能,一种是在PANel中按下右键,还有就是在旗帜或者问号上按下右键
{
int x = this.mousex / 20;//获取鼠标按下位置PICTRUE的坐标这样我们好操作
int y = this.mousey / 20;
if (a == "System.Windows.Forms.PictureBox")//如果在PICTRUEBOX中按下的
{
abc = (PictureBox)sender;//用我们定义的ABC来接收这个PICTRUEBOX,
if (alsoflag[x, y] == 1)//如果这个地方已经被标记了旗帜,
{
lei[x, y].Image = global ::扫雷.Properties.Resources.maybe;
alsoflag[x, y] = 2;//2是代表着当前的图片是?号
flagnum++;//您可能会纳闷,为什么由旗子变成别的了,为什么还要加呢因为你看到最后就可以了最后这个数有个取负的时候
}
else if (alsoflag[x, y] == 2)
{
lei[x, y].Visible = false;//这个是当这个由问号变成空白的时候,就不需要让它显示了,相当于没点它一样
lei[x, y].Image = image[x, y];//恢复它本来的图片
alsoflag[x, y] = 0;//这个点已经不被标记了
}
}
else//如果您不是在图片上点的,那么您就是在PANEL里点了,这里主要是在PANEL里点的事件
{
lei[x, y].Visible = true;//这里是将这个图片显示出来
lei[x, y].Image = global ::扫雷.Properties.Resources.flag;//将他的图片转换为旗帜
alsoflag[x, y] = 1;//用数组来表示这个点已经被标记了,因为我用图片比较会比用数字比较慢的多,尽量用简单的比较达到同一效果
flagnum--;//旗帜数加一,后面会取反
}
}
else if (this.leftdown &&!(this.rightdown ))//左键按下,但是右键没有按下,panel中按下
{
if (a == "System.Windows.Forms.PictureBox")//当时是想在以后可能会用到在图片中点击左键的事件,但是后来想,已经被我给排出了,因为在图片上单单按下左键,不起任何效果
{
abc = (PictureBox)sender;
}
else//在PANEL中按下
{
int x = this.mousex / 20;
int y = this.mousey / 20;//获取鼠标位置,并且将这个区域的图片显示
lei[x, y].Visible = true;
5 楼
qiock [专家分:50] 发布于 2008-12-19 17:04:00
if (dead(x, y))//判断这个地方是否是雷,如果是死,不是不启用
{
havedead = dead(x, y);//已经死亡
MessageBox.Show("游戏结束", "游戏结束", MessageBoxButtons.OK);
for (int i = 0; i < 20; i++)//将所有图片都显示
{
for (int j = 0; j < 20; j++)
{
lei[i, j].Visible = true;
lei[i, j].Image = image[i, j];
}
}
}
else//没有死的话,打开这个图片,并且显示
{
alsoopennum++;
iswin();
if (lnum[x, y] == 0)//如果这个点是空白点,那么就启用打开它周围的8个点
{
this.openpicturebox(x, y);
}
}
}
}
else if ((e.Button == MouseButtons.Middle)||(this .rightdown &&this.leftdown ))//这个是如果按下了鼠标中键,或者左右都按下。
{
if (a == "System.Windows.Forms.PictureBox")//是否在图片上按下
{
abc = (PictureBox)sender;
int x = this.mousex / 20;
int y = this.mousey / 20;
if ((alsoflag[x, y] != 2)&&(alsoflag [x,y]!=1))//判断这个图片是否是旗帜,或者是问号
{
openthispic(x, y);//不是就启用打开其周围的点。
}}}
}
else//如果已经死了,打开所有图片,并且在所有的雷位置标记为旗帜
{
for (int i = 0; i < 20; i++)
{
for (int j = 0; j < 20; j++)
{
if (lnum[i, j] == -1)
{
lei[i, j].Visible = true;
lei[i, j].Image = global ::扫雷.Properties.Resources.flag;
}}}}
this.label3.Text = "您的标记数目是" + (sbnum - flagnum).ToString();//显示你标记的旗帜数目
}
6 楼
qiock [专家分:50] 发布于 2008-12-19 17:05:00
private void Form1_Load(object sender, EventArgs e)
{
initializemap();//初始化地图,首先将所有的PICTRUEBOX控件都添加进PANEL
initializeleimap();//给每个位置的PICTRUEBOX控件添加图片。
this.label4.Text = "本难度总共有" + sbnum.ToString() + "雷";
this.label3.Text = "您已经标记了0个雷";
}
private void initializemap()
{//初始化地图
panel1 .Paint +=new PaintEventHandler(panel1_paint);//给PANEL画上网络线,就是你看到的黄绿色的网络线
for (int i = 0; i < 20; i++)
{
for (int j = 0; j < 20; j++)
{
lei[i, j] = new PictureBox();
lei[i, j].Location = new Point(i * 20, j * 20);
lei[i, j].Size = new Size(20, 20);
lei[i, j].BackColor = Color.White;
lei[i, j].SizeMode = PictureBoxSizeMode.StretchImage;
lei[i, j].MouseMove += new MouseEventHandler(panel1_MouseMove);//将他们的鼠标事件都加到PANEL的鼠标事件,
lei[i, j].MouseDown += new MouseEventHandler(panel1_MouseDown);//
lei[i, j].MouseUp += new MouseEventHandler(panel1_MouseUp);//
lei[i, j].Visible = false ;//都不显示
this.panel1.Controls.Add(lei[i, j]);//添加进容器panel
}
}
}
private void initializelei()
{//这个是初始化地雷的,因为我们先把雷的位置都安排好了,才能去安排其他位置的数字
lnum = new int[20, 20];//保存雷的信息,如果这个点是雷,那么它的数值就是-1
Random a=new Random ();//定义一个随机数对象
int num = sbnum ;//雷的总数目,即你要安排多少个雷在地图上
flagnum = sbnum;//初始化未标记的旗帜数目为总雷数
while (num > 0)//这个是确保一定安排进这么多的雷,会产生恶性循环,如果需要安排的雷数大于或者等于总的图片控件数目的时候这个是死循环
{
for (int i = 0; i < 20; i++)//进行完一次的安装后,不一定都能安装完,所以用了WHILE循环
{
for (int j=0;j<20;j++)
{
int b=a.Next (0,400);//一共有400个位置,如果我要安排50个雷,那么每个位置被安排是雷的位置就因该是50/400=1/8,这样安排会使雷分布的更加平均
if(b<sbnum &&(lnum[i,j]!=-1)&&(num>0))
{
lnum [i,j] =-1;
num--;
}
}
}
}
}
7 楼
qiock [专家分:50] 发布于 2008-12-19 17:08:00
private void initializeleimap()
{
initializelei();//这里首先调用了初始化地雷位置函数
initializenum();//也调用了将数字也安排进去的函数
for (int i = 0; i < 20; i++)//针对不同的数字,安放不同的图片
{
for (int j = 0; j < 20; j++)
{
if (lnum[i, j] == -1)
{
lei[i, j].Image = global ::扫雷.Properties.Resources.lei;
image[i, j] = lei[i, j].Image;//并且将该点的图片保存进这个图片数组里,方便后边咱们标记的时候恢复
}
else if (lnum[i, j] == 1)
{
lei[i, j].Image = global ::扫雷.Properties.Resources.Image1;
image[i, j] = lei[i, j].Image;
}
else if (lnum[i, j] == 2)
{
lei[i, j].Image = global ::扫雷.Properties.Resources.Image2;
image[i, j] = lei[i, j].Image;
}
else if (lnum[i, j] == 3)
{
lei[i, j].Image = global ::扫雷.Properties.Resources.Image3;
image[i, j] = lei[i, j].Image;
}
else if (lnum[i, j] == 4)
{
lei[i, j].Image = global ::扫雷.Properties.Resources.Image4;
image[i, j] = lei[i, j].Image;
}
else if (lnum[i, j] == 5)
{
lei[i, j].Image = global ::扫雷.Properties.Resources.Image5;
image[i, j] = lei[i, j].Image;
}
else if (lnum[i, j] == 6)
{
lei[i, j].Image = global ::扫雷.Properties.Resources.Image6;
image[i, j] = lei[i, j].Image;
}
else if (lnum[i, j] == 7)
{
lei[i, j].Image = global ::扫雷.Properties.Resources.Image7;
image[i, j] = lei[i, j].Image;
}
else if (lnum[i, j] == 8)
{
lei[i, j].Image = global ::扫雷.Properties.Resources.Image8;
image[i, j] = lei[i, j].Image;
}
else if (lnum[i, j] == 0)
{
lei[i, j].Image = global ::扫雷.Properties.Resources.zero;
image[i, j] = lei[i, j].Image;
}
}
}
}
8 楼
qiock [专家分:50] 发布于 2008-12-19 17:14:00
private void initializenum()
{//这里没有用数组索引,所以很大,这里是初始化lnum[x,y]这个数组的,即在不是雷的位置添加进数字,来表示它周围有几个雷
for (int i = 0; i < 20; i++)
{ for (int j = 0; j < 20; j++)
{ if (lnum[i, j] != -1)//已经标记雷的位置咱们就不用再标记了,下面是针对左上角,左下角,右上角和右下角,以及上底边,下底边,左底边,右底边,这些特殊的地方先判断,防止数组超出索引界限,最后就是正常点了,周围8个点都判断。
{
if (i == 0)
{
if (j == 0)
{
lnum [i,j]=0;
if (lnum[i + 1, j] == -1)
{lnum[i, j] += 1;}
if (lnum[i + 1, j + 1] == -1)
{lnum[i, j] += 1;}
if (lnum[i, j + 1] == -1)
{ lnum[i, j] += 1;}}
else if (j == 19)
{lnum[i, j] = 0;
if (lnum[i, j - 1] == -1)
{ lnum[i,j] += 1;}
if (lnum[i + 1, j - 1] == -1)
{lnum[i, j] += 1;}
if (lnum[i + 1, j] == -1)
{lnum[i, j] += 1;}}
else
{
lnum[i, j] = 0;
if (lnum[i, j - 1] == -1)
{
lnum[i, j] += 1;
}
if (lnum[i + 1, j - 1] == -1)
{
lnum[i, j] += 1;
}
if (lnum[i + 1, j] == -1)
{
lnum[i, j] += 1;
}
if (lnum[i + 1, j + 1] == -1)
{ lnum[i, j] += 1;}
if (lnum[i, j + 1] == -1)
{lnum[i, j] += 1;}}}
下面的就不发了基本和上面一样
9 楼
qiock [专家分:50] 发布于 2008-12-19 17:16:00
private void panel1_paint(object sender, PaintEventArgs e)
{//画网络线的
int i;
Graphics gr = e.Graphics;
Pen mypen = new Pen(Color.YellowGreen, 2);
for (i = 0; i <= 20; i++)
{
gr.DrawLine(mypen, i * 20, 0, i * 20, 400);
gr.DrawLine(mypen, 0, i * 20, 400, i * 20);
}
}
private bool dead(int x, int y)
{//是否死亡,死亡返回真,否则返回假
if (lnum[x, y] == -1)
{
return true;
}
return false;
}
private void iswin()
{//判断是否获胜
if (sbnum + alsoopennum == 400)//前提是雷的总数加上已经打开的(非旗帜,非问号点数)加一起等于总的点数
{
this.boolwin = true;
this.leftdown = false;//鼠标2个键是否被按下改为假,主要是防止出现问题
this.rightdown = false;//
MessageBox.Show("你赢了", "提示", MessageBoxButtons.OK);
for (int i = 0; i < 20; i++)//获胜了,就把所有的雷的位置都标记下。
{
for (int j = 0; j < 20; j++)
{
if (lnum[i, j] == -1)
{
lei[i, j].Visible = true;
lei[i, j].Image = global ::扫雷.Properties.Resources.flag;
}
}
}
}
else
{
this.boolwin = false;//没有获胜。
}
}
10 楼
qiock [专家分:50] 发布于 2008-12-19 17:17:00
private void openpicturebox(int x, int y)
{//如果你打开的位置是一个空白点的话,就把它周围的点都打开,如果打开周围的点还是有空白点,就继续调用这个函数,继续打开,打开的前提是,这个点没有被打开,而且在数组索引范围内。
if (lnum[x, y] == 0)//您可能会纳闷,打开这些点的时候为什么不判断是否死亡呢?这里明确的告诉你,没有必要,因为空白的点,周围不可能是雷!!!
{
if (x == 0 && y == 0)
{
if (!(lei[x + 1, y].Visible))
{
lei[x + 1, y].Visible = true;
openpicturebox(x + 1, y);
alsoopennum++;
}
if (!(lei[x + 1, y + 1].Visible))
{
lei[x + 1, y + 1].Visible = true;
openpicturebox(x + 1, y + 1);
alsoopennum++;
}
if (!(lei[x, y + 1].Visible))
{
lei[x, y + 1].Visible = true;
openpicturebox(x, y + 1);
alsoopennum++;
}
}下面的基本和上面一样
我来回复