C#扫雷游戏详细介绍


  1. 可有可无的废话

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

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

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

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

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

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

     

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

     

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

  2. 游戏两个阶段

    我将游戏分为两个层次

     扫雷实例下载地址

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

  1. 基本功能实现(没有标旗)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;另外两个属性在其他地方设置。

  1. 实现图片显示效果(有标旗)

    这个实例代码和上面的基本代码区别不大,所以也不用一一分析。源码在上面也给出了连接。

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

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

    



   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、用图片显示地图和地雷

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

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

      


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


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值