(转载)WinformGDI+入门级实例——扫雷游戏(附源码)

时间:2022-09-08 20:57:35

(转载)WinformGDI+入门级实例——扫雷游戏(附源码)

本文将作为一个入门级的、结合源码的文章,旨在为刚刚接触GDI+编程或对相关知识感兴趣的读者做一个入门讲解。游戏尚且未完善,但基本功能都有,完整源码在文章结尾的附件中。

整体思路:

扫雷的游戏界面让我从一开始就想到了二维数组,事实上用二维数组来定义游戏数据确实是最符合人类思维的方式。(Square类会在后面解释)

 //游戏数据
private readonly Square[,] _gameData;

有了这个开头,接下来就是填充二维数组的数据了,对于数据,我最初的想法是用int或枚举,当然,这是可行的,但涉及一个问题就是高耦合,所有操作将都在高层执行,难以维护。

于是我们用一个Square类表示一个小方块区。

 /// <summary>
/// 表示游戏中一个方块区
/// </summary>
public sealed class Square
...

以枚举表示方块区的状态:

 /// <summary>
/// 方块区状态
/// </summary>
public enum SquareStatus
{
/// <summary>
/// 闲置
/// </summary>
Idle,
/// <summary>
/// 已打开
/// </summary>
Opened,
/// <summary>
/// 已标记
/// </summary>
Marked,
/// <summary>
/// 已质疑
/// </summary>
Queried,
/// <summary>
/// 游戏结束
/// </summary>
GameOver,
/// <summary>
/// 标记失误(仅在游戏结束时用于绘制)
/// </summary>
MarkMissed
}

用Game类来表示一局游戏,其中包含游戏数据、游戏等级、雷区数、布雷方法等。

 /// <summary>
/// 表示一局游戏
/// </summary>
public sealed class Game : IDisposable
...

难点攻破:

游戏不大,涉及的难点也就不多,但对于刚接触GDI+的读者,一些地方还是比较麻烦的。

逻辑难点1:布雷

扫雷游戏有一个附加规则,就是第一次单击不论如何都不会踩到雷区,由于这个规则的存在,我们不能将布雷操作做在第一次单击之前。所以我们在游戏开局时假设所有方块区都没有雷。

 /// <summary>
/// 开始游戏
/// </summary>
public void Start()
{
//假设所有方块区均非雷区
for (int i = ; i < _gameData.GetLength(); i++)
for (int j = ; j < _gameData.GetLength(); j++)
_gameData[i, j] = new Square(new Point(i, j), false, );
}

随后,在开局后第一次单击时布雷。

 /// <summary>
/// 布雷
/// </summary>
/// <param name="startPt">首次单击点</param>
private void Mine(Point startPt)
{
Size area = new Size(_gameData.GetLength(), _gameData.GetLength());
List<Point> excluded = new List<Point> { startPt }; //随机创建雷区
for (int i = ; i < _minesCount; i++)
{
Point pt = GetRandomPoint(area, excluded);
_gameData[pt.X, pt.Y] = new Square(pt, true, );
excluded.Add(pt);
} //创建非雷区
for (int i = ; i < _gameData.GetLength(); i++)
for (int j = ; j < _gameData.GetLength(); j++)
if (!_gameData[i, j].Mined)//非雷区
{
int minesAround = EnumSquaresAround(new Point(i, j)).Cast<Square>().Count(square => square.Mined);//周围雷数 _gameData[i, j] = new Square(new Point(i, j), false, minesAround);
} _gameStarted = true;
}

先创建雷区,再创建非雷区,以便我们在创建非雷区时可以计算出非雷区周围的雷数,枚举周围方块的方法我们用yield创建一个枚举器。

 /// <summary>
/// 枚举周围所有方块区
/// </summary>
/// <param name="squarePt">原方块区</param>
/// <returns>枚举数</returns>
private IEnumerable EnumSquaresAround(Point squarePt)
{
int i = squarePt.X, j = squarePt.Y; //周围所有方块区
for (int x = i - ; x <= i + ; ++x)//横向
{
if (x < || x >= _gameData.GetLength())//越界
continue; for (int y = j - ; y <= j + ; ++y)//纵向
{
if (y < || y >= _gameData.GetLength())//越界
continue; if (x == squarePt.X && y == squarePt.Y)//排除自身
continue; yield return _gameData[x, y];
}
}
}

逻辑难点2:当单击区周围无雷区(空白)时,自动批量打开周围所有非雷区

 //如果是空白区,则递归相邻的所有空白区
if (_gameData[logicalPt.X, logicalPt.Y].MinesAround == )
AutoOpenAround(logicalPt);
 /// <summary>
/// 自动打开周围非雷区方块(递归)
/// </summary>
/// <param name="squarePt">原方块逻辑坐标</param>
private void AutoOpenAround(Point squarePt)
{
//遍历周围方块
foreach (Square square in EnumSquaresAround(squarePt))
{
if (square.Mined || square.Status == Square.SquareStatus.Marked || square.Status == Square.SquareStatus.Opened)
continue; square.LeftClick();//打开
//周围无雷区
if (square.MinesAround == )
AutoOpenAround(square.Location);//递归打开
}
}

绘图难点1:双缓冲以克服闪烁

从二维数组的结构来看,我们需要遍历整个二维数组,然后把每个Square绘制到winform上,但这会造成强烈的闪烁效果。因为是实时绘图,绘制的每一步都会实时显示在窗口上,所以我们看到的效果就是一个方块区一个方块区的出现在窗口上。

为了克服这种不友好的闪烁,双缓冲出现了,思路就是创建一个缓冲区(通常是一个内存中的位图),先将所有方块区绘制到这张位图上,绘制完成后,将位图贴到窗体上,最终效果将不再出现闪烁的情况。

 //窗口图面
private readonly Graphics _wndGraphics;
//缓冲区
private readonly Bitmap _buffer;
//缓冲区图面
private readonly Graphics _bufferGraphics;
 /// <summary>
/// 绘制一帧
/// </summary>
public void Draw()
{
for (int i = ; i < _gameData.GetLength(); i++)
for (int j = ; j < _gameData.GetLength(); j++)
_gameData[i, j].Draw(_bufferGraphics); _wndGraphics.DrawImage(_buffer, new Point(_gameFieldOffset.Width, _gameFieldOffset.Height));
}

总结:

至此,所有难点基本攻破,完整代码大家参考附件,代码基于Windows XP版扫雷做的模仿,笔者能力有限,不足之处请大家多多指点。

附件:

附件下载

 
---------------------
作者:CoffeeMX
来源:博客园
原文:https://www.cnblogs.com/CoffeeMX/p/5864974.html

(转载)WinformGDI+入门级实例——扫雷游戏(附源码)的更多相关文章

  1. WinformGDI&plus;入门级实例——扫雷游戏&lpar;附源码&rpar;

    写在前面: 本文将作为一个入门级的.结合源码的文章,旨在为刚刚接触GDI+编程或对相关知识感兴趣的读者做一个入门讲解.游戏尚且未完善,但基本功能都有,完整源码在文章结尾的附件中. 整体思路: 扫雷的游 ...

  2. 业务类接口在TCP,HTTP,BLL模式下的实例 设计模式混搭 附源码一份

    业务类接口在TCP,HTTP,BLL模式下的实例 设计模式混搭 附源码一份 WinForm酒店管理软件--框架这篇随笔可以说是我写的最被大家争议的随笔,一度是支持和反对是一样的多.大家对我做的这个行业 ...

  3. Python:游戏:扫雷(附源码)

    这次我们基于 pygame 来做一个扫雷,上次有园友问我代码的 python 版本,我说明一下,我所有的代码都是基于 python 3.6 的. 先看截图,仿照 XP 上的扫雷做的,感觉 XP 上的样 ...

  4. C&num;共享内存实例 附源码

    原文 C#共享内存实例 附源码 网上有C#共享内存类,不过功能太简单了,并且写内存每次都从开头写.故对此进行了改进,并做了个小例子,供需要的人参考. 主要改进点: 通过利用共享内存的一部分空间(以下称 ...

  5. (转)干货|这篇TensorFlow实例教程文章告诉你GANs为何引爆机器学习?(附源码)

    干货|这篇TensorFlow实例教程文章告诉你GANs为何引爆机器学习?(附源码) 该博客来源自:https://mp.weixin.qq.com/s?__biz=MzA4NzE1NzYyMw==& ...

  6. 超详细的php用户注册页面填写信息完整实例(附源码)

    这篇文章主要介绍了一个超详细的php用户注册页面填写信息完整实例,内容包括邮箱自动匹配.密码强度验证以及防止表单重复等,小编特别喜欢这篇文章,推荐给大家. 注册页面是大多数网站必备的页面,所以很有必要 ...

  7. HTML5与CSS3实例教程&lpar;第2版&rpar; 附源码 中文pdf扫描版

    HTML5和CSS3技术是目前整个网页的基础.<HTML5与CSS3实例教程(第2版)>共分3部分,集中讨论了HTML5和CSS3规范及其技术的使用方法.这一版全面讲解了最新的HTML5和 ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  9. C&num;进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)

    前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...

随机推荐

  1. Bootstrap 栅格系统

    1.简介 Bootstrap内置了一套响应式.移动设备优先的流式栅格系统,随着屏幕设备或视口(viewport)尺寸的增加,系统会自动分为最多12列.它包含了易于使用的预定义classe,还有强大的m ...

  2. ACM 盗梦空间

    盗梦空间 时间限制:3000 ms  |  内存限制:65535 KB 难度:2   描述 <盗梦空间>是一部精彩的影片,在这部电影里,Cobb等人可以进入梦境之中,梦境里的时间会比现实中 ...

  3. IT外包行业与职业发展

          在IT行业,总是有一些IT外包公司的存在,凡是存在的都是合理的.当你做为IT从业人员应该尽量避免去外包公司工作 .特别是你从事软件开发工作.     先来说说缘由,一些外包公司本来是从事软 ...

  4. SSH框架总结(框架分析&plus;环境搭建&plus;实例源码下载) 《转》

    这篇文章比较易懂,易理解: 首先,SSH不是一个框架,而是多个框架(struts+spring+hibernate)的集成,是目前较流行的一种Web应用程序开源集成框架,用于构建灵活.易于扩展的多层W ...

  5. python编码问题&lpar;1&rpar;

    一.字符编码基础 字符编码是计算机对字符的格式化,从而能够在计算机系统中存储与传输. 1.ASCII码 在计算机内部,所有的信息最终都表示为一个二进制的字符串.每一个二进制位(bit)有0和1两种状态 ...

  6. 使用Simple DNS plus 构建自己的DNS

    1.下载并安装Simple DNS plus 2.界面例如以下: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2tfYm9zcw==/font/5a6L ...

  7. Mybatis第一篇【介绍、快速入门、工作流程】

    什么是MyBatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为 ...

  8. iOS 屏幕方向

    参考文章:http://www.tuicool.com/articles/e2q6zi 一般的应用,只会支持竖屏正方向一个方向,支持多个屏幕方向的应用还是比较少的. 当时也没搞明白,所以直接就设置了正 ...

  9. linux 部署之路 修行不够全靠悟

    考虑到很多孩子不会Linux或Mysql,所以我这里提示一下, 这篇教程里 "有多行代码" 的是给你展示结果的,不用你敲 只有一行的才是要你自己敲进去的.   1.首先更新一下仓库 ...

  10. 关于QT中&OpenCurlyDoubleQuote;崩溃”问题

    经常会遇到一个问题,程序运行崩溃! 1.release.debug直接运行崩溃. 2.程序可以运行但是点击界面崩溃. 3.debug模式崩溃,release正常. 4.软件里面的release和deb ...