当前位置:首页 » 《随便一记》 » 正文

C#编写一个扫雷游戏

20 人参与  2022年11月09日 12:34  分类 : 《随便一记》  评论

点击全文阅读


基础思路

像原版一样把扫雷分为三个难度:初级难度9*9,中级难度16*16,高级难度16*30

做一个游戏需要图片资源,去网上下载了关于 扫雷的有关文件,放在了项目中

扫雷的核心是用一个二维数组存储游戏区。在开始游戏时随机生成雷区,然后根据鼠标点击触发事件,思路是比较简单清晰的。雷的标记可以利用button类的Tag属性区分,0代表自身非雷周围8格有雷,1代表雷,2代表自身非雷但周围有雷。

1、首先是界面的设计

初始界面:有两个按钮,每个按钮中有三个选项

“难度”按钮中的选项应该只有一个被选中

private void ToolStripMenuItem1_CheckedChanged(object sender, EventArgs e)        {            foreach (ToolStripMenuItem item in 难度ToolStripMenuItem1.DropDownItems)            {                item.Checked = false;            }            ToolStripMenuItem checkItem = sender as ToolStripMenuItem;            checkItem.Checked = true;        }

开始游戏按钮是游戏的入口,需进行窗体大小的调整、提示信息刷新、雷区大小调整、雷区初始化、生成地雷。

        //游戏入口        private void 开始游戏ToolStripMenuItem_Click(object sender, EventArgs e)        {            //先清空显示及btn数组            if (btns != null)            {                foreach(ButtonWithPos btn in btns) Controls.Remove(btn);                Array.Clear(btns, 0, WIDTH * HEIGHT);            }            //难度选择            if (初级ToolStripMenuItem1.Checked)            {                level = 1;                mineNum = 10;                WIDTH = HEIGHT = 9;            }            else if (中级ToolStripMenuItem1.Checked)            {                level = 2;                mineNum = 40;                WIDTH = HEIGHT = 16;            }            else if (高级ToolStripMenuItem1.Checked)            {                level = 3;                mineNum = 99;                WIDTH = 30;                HEIGHT = 16;            }            else Application.Exit();            //对象初始化            mines = new Coord[mineNum];            for (int i = 0; i < mineNum; i++) mines[i] = new Coord();            btns = new ButtonWithPos[HEIGHT, WIDTH];            visited = new bool[HEIGHT, WIDTH];            BoardInit();            //Reveal()函数DFS递归算法所需visited数组初始化            for (int i = 0; i < HEIGHT; ++i)            {                for (int j = 0; j < WIDTH; ++j)                {                    visited[i, j] = false;                }            }            Generate();        }

BoardInit函数用于窗口大小调整、提示信息刷新以及button数组(即雷区)的初始化:

public void BoardInit()        {            int x0 = 20, y0 = 120;            if (level == 1)            {                Height = 500;                Width = 375;            }            else if (level == 2)            {                Height = 730;                Width = 620;            }            else if (level == 3)            {                Height = 730;                Width = 1110;            }            else Application.Exit();            lblMineSum.Left = x0;            lblMineChecked.Left = x0;            btnShow.Left = Width / 2 - 20;            int  d = 35;            Console.WriteLine(WIDTH + " " + HEIGHT);            for (int i = 0; i < HEIGHT; ++i)            {                for (int j = 0; j < WIDTH; ++j)                {                    ButtonWithPos button = new ButtonWithPos();                    button.Width = d;                    button.Height = d;                    button.Top = y0 + i * d;                    button.Left = x0 + j * d;                    button.BackgroundImage = imgNormal;                    button.BackgroundImageLayout = ImageLayout.Stretch;                    button.X = j;                    button.Y = i;                    button.Tag = 0;//Tag = 0,周围无雷;1,地雷;2,周围有雷;                    button.Visible = true;                    button.Enabled = true;                    button.MouseDown += Button_MouseDown;                    btns[i, j] = button;                    Controls.Add(button);                }            }            btnShow.BackgroundImage = imgSmile;            btnShow.BackgroundImageLayout = ImageLayout.Stretch;            lblMineSum.Text = "地雷总数:" + mineNum.ToString();            lblMineChecked.Text = "已标记地雷数:" + mineMarked.ToString();        }

Generate函数用随机数来生成雷区

void Generate()        {            Random random = new Random();            bool flag;            for (int i = 0; i < mineNum; ++i)            {                int newX = random.Next(0, HEIGHT);                int newY = random.Next(0, WIDTH);                flag = true;                while (true)                {                    flag = true;                    for (int j = 0; j < i; ++j)                    {                        if (mines[j].X == newX && mines[j].Y == newY)                        {                            flag = false;                            break;                        }                    }                    if (flag)                    {                        mines[i].X = newX;                        mines[i].Y = newY;                        btns[newX, newY].Tag = 1;                        int left = newX - 1, right = newX + 1;                        int top = newY - 1, bottom = newY + 1;                        for (int k = left; k <= right; ++k)                        {                            for (int l = top; l <= bottom; ++l)                            {                                if (k >= 0 && k < HEIGHT && l >= 0 && l < WIDTH)                                {                                    if (k == newX && l == newY) continue;                                    if ((int)btns[k, l].Tag != 1) btns[k, l].Tag = 2;                                }                            }                        }                        break;                    }                    else                    {                        newX = random.Next(0, HEIGHT);                        newY = random.Next(0, WIDTH);                    }                }            }        }


2、鼠标点击处理

左键是翻开,右键是标记地雷。右键只需要进行显示的更新、标记地雷数的刷新以及检查是否胜利。左键要分翻开的类型。如果踩雷直接输掉,打开周围有雷的格子显示对应周围的雷数

        private void Button_MouseDown(object sender, MouseEventArgs e)        {            ButtonWithPos button = sender as ButtonWithPos;            if (e.Button == MouseButtons.Right)            {                if (button.BackgroundImage != imgMarked)                {                    button.BackgroundImage = imgMarked;                    button.BackgroundImageLayout = ImageLayout.Stretch;                    ++mineMarked;                }                else                {                    button.BackgroundImage = imgNormal;                    --mineMarked;                }                lblMineChecked.Text = "已标记地雷数:" + mineMarked.ToString();                bool win = CheckWin();                if (win) Win();            }            else if (e.Button == MouseButtons.Left)            {                int row = button.Y, col = button.X;                Console.WriteLine("BUTTON DOWN: " + row + col);                if ((int)button.Tag == 1)                {                    Lose(button);                }                else if ((int)button.Tag == 2)                {                    int mineAroundSum = CalcMineAround(row, col);                    MineAroundShow(button, mineAroundSum);                }                else if ((int)button.Tag == 0)                {                    button.BackgroundImage = imgNull;                    Reveal(row, col);                }            }        }

打开周围无雷的格子要复杂一些。我们实际玩扫雷的时候有时候会一下翻开一片区域,这是因为当翻开了一个周围无雷的格子,程序会继续尝试翻周围的格子,直到翻开一个周围有雷的格子。这可以用DFS算法递归实现

        void Reveal(int row, int col)        {            visited[row, col] = true;            int mineAroundSum;            int left = row - 1, right = row + 1;            int top = col - 1, bottom = col + 1;            if ((int)btns[row, col].Tag == 0) btns[row, col].BackgroundImage = imgNull;            else if ((int)btns[row, col].Tag == 1) return;            else if ((int)btns[row, col].Tag == 2)            {                mineAroundSum = CalcMineAround(row, col);                MineAroundShow(btns[row, col], mineAroundSum);                return;            }            for (int i = left; i <= right; ++i)            {                for (int j = top; j <= bottom; ++j)                {                    if (i >= 0 && i < HEIGHT && j >= 0 && j < WIDTH)                    {                        if (!visited[i, j])                        {                            //Console.WriteLine("REVEAL: " + i + j);                            Reveal(i, j);                        }                    }                }            }        }

一些判断胜利失败以及胜利失败的表示的函数

 void Lose(ButtonWithPos button)        {            btnShow.BackgroundImage = imgLose;            btnShow.BackgroundImageLayout = ImageLayout.Stretch;            foreach (ButtonWithPos btn in btns)            {                if ((int)btn.Tag == 1) btn.BackgroundImage = imgMineNotChosen;            }            button.BackgroundImage = imgMineChosen;            MessageBox.Show("正中地雷!", "Oops");            开始游戏ToolStripMenuItem.Enabled = true;        }        //检查是否胜利        bool CheckWin()        {            bool flag = true;            foreach (ButtonWithPos btn in btns)            {                if ((int)btn.Tag == 1)                {                    if (btn.BackgroundImage != imgMarked)                    {                        flag = false;                        break;                    }                }            }            if (mineMarked != mineNum) flag = false;            return flag;        }        //胜利表示        void Win()        {            btnShow.BackgroundImage = imgVictory;            btnShow.BackgroundImageLayout = ImageLayout.Stretch;            MessageBox.Show("排雷成功!干得漂亮", "You Win!");        }    }

游戏截图

总结 

总体来讲用C#编写一个小游戏的过程不算困难,最主要的设计的思路,这个扫雷小游戏就利用了一个随机数来生成地雷和翻格子的时候用DFS递归算法来实现,具体界面的设计在平时作业“编写一个游戏客户端”的时候已经学习到了,利用了C#窗体应用开发。

这次实验主要是了解了.NET框架

.NET的特点:

多平台:可用于开发 服务器程序,桌面程序,PDA,移动电话程序。

行业标准:支持多种通信协议,XML,HTTP,SOAP,WSDL

C#与C++和C都不同,它更面向对象

C#窗体应用开发运用在这次实验上

理解事件驱动程序设计窗体属性、方法和事件基本控件的属性、方法和事件消息对话框的使用

这次实验学到了很多


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/47642.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1