如何用C++写扫雷?
用VC++写扫雷游戏
王洪雅
分析了windows扫雷游戏的功能特点,利用面向对象的可视化编程语言visual c++,给出了各功能模块的具体实现方法,并提供了编写小游戏程序的一般方法和使用visual c++的一些技巧。
首先分析了扫雷最基本的功能。
在未知区域单击鼠标左键。如果未知区域有雷,游戏会停止,所有地雷都会显示出来。如果没有雷声,将显示周围雷声的数量。如果没有打雷,那么检查一下周围八个区域有没有打雷,直到有打雷为止,并显示出来。这其实是一个递归的过程。
在未知区域点击鼠标右键,不管是不是真的雷霆都会设置为雷霆。可以选择初级、中级、高级,可以自定义地雷数量和区域大小。
雷场的左上方显示的是地雷总数减去标示雷区的数量。
一个按钮显示在雷区上部的中间,用于打开和显示鼠标动作的结果。
雷场右上方显示扫雷时间。
清除所有雷霆后,会出现一个对话框,记录你在排行榜中的名字。按时间排序。
为了完成上述功能,应用visual c++的具体技术细节如下:
1.应用appwizard创建一个基于sdi的应用cbombapp,去掉对打印和状态栏的支持,在资源编辑器中修改菜单和对应的加速键,使其与windows扫雷游戏一致。具体来说就是开始(id-game-begin)、初级(id-game-junior)、中级(id-game-middle)、高级(id-game-senior)、自定义(id-game-custom)、颜色(id-game-color)和英雄榜(id-game)。
2.13资源编辑器中雷区每个小区域对应的属性。用画刷或其他画图工具画出对应的13 10 16彩色位图,反派表情对应的20乘20的三个16彩色位图,换色对应的第一个16的一组单色位图,显示时间和雷数的0 ~。
自定义客户对话框,包含三个静态文本控件和三个编辑控件,三个编辑控件分别对应成员M _ I Rownum、M _ iColumnum和M _ Ibombnum。该对话框用于自定义雷数、行数和列数,其对应的mfc类为ccustomer。自定义排序对话框,包含9个静态文本控件,其中6个显示排行榜的名称和时间,对应的mfc类是csort。自定义输入对话框,其中包含一个静态文本控件和一个编辑控件。编辑控件用于游戏成功结束时输入名字,其对应的mfc类为Qinput。
3.定义炸弹,封装每个地雷的相关属性。
班级炸弹
{
公共:
int isbomb//先决定是不是雷。
布尔·伊塞尔;//判断该区域是否经过处理,周围是否有雷。
bool isdone//判断递归是否处理过。
int num//周围的雷数
bool findbomb//当排雷人员认为是地雷时,他们就会是地雷(但不一定是地雷)。
} ;
4.在cmainframe中重载precreatewindow,设置相应的属性使其窗体大小固定,从而固定初始大小为10乘以10的显示区域和雷区上方的控制区域。部分代码如下。
cs . style = ws _ overlapped | ws _ sysmenu | ws _ border | ws _ minimize box;
cs . cy = 10 * 15+6;
cs . CX = 10 * 15+60;//6和60分别是水平和垂直的附加值,用于边框、菜单、标题栏和控件区域。
5.游戏的主要工作是呈现不断变化的图形或动画,并根据用户的输入进行交互显示。windows在Windows document-window框架中的作用是接受用户的输入并负责显示,所以cview类完成了大部分的扫雷工作。在cbombview中定义以下成员变量,以记录相关操作的结果或对象的状态。
炸弹m _炸弹[30][30];//最大矿区
cstring m _ currenttime//用于记录和显示扫雷时间。
ctime m _ begintime//记录游戏开始的时间。
bool m _ timerbegin//定时器开了吗?
int m _ ibomnum//地雷的数量
int m _ irow//雷霆的行数
int m _ icolumn//雷声的行数
int m _ ibombfound//表示考虑的地雷数量。
cbitmapbutton m _ bitbutton//控制区域中的位图按钮
int m _ currentlevel//表示当前游戏的级别。
bool m _ biscolor//指示当前是彩色还是单色。
cbit map m _ BM bomb[12];//用于存储12小位图。
int m _ igameover//游戏结束前设置0。所有的地雷都被清除了。被杀时设置2。
在cbombview中重新加载oncreate函数来创建一个位图按钮。这个位图按钮的两个位图分别对应正常和扫雷正确状态。当要显示被杀状态时,要动态销毁按钮,重新创建一个正常和被杀状态对应的位图,并将这个位图按钮的id号设置为id_game_begin,这样当按钮被点击时,就可以重新启动游戏。部分代码如下。
crect rcclient
getclientrect(& amp;RC client);
crect rect(rcclient.cx/2-8,10,rcclient.cx/2+8,20);
m_button.create("new ",bs _ defpushbutton | ws _ visible |
bs_ownerdraw,rect,this,id _ game _ begin);
m _ button . load bitmaps(IDB _ face 1,IDB _ face 2);
显示时间的功能比较简单。当响应第一条wm_lbuttomdown消息时,启动定时器,记录游戏开始时间。在wm_time消息的响应函数ontimer中获取当前时间,减去游戏开始时的时间,在显示时间的客户区显示获取的时差(使用数字位图)。当游戏结束时(完全雷霆或杀死),关闭计时器,停止显示。
wm_lbuttomdown消息响应函数onlbuttomdown是处理用户输入的主要执行器。该函数首先判断点中的位置是否为雷霆,如果是,则关闭定时器,销毁原来的位图按钮,新建一个对应正常状态和炸裂状态的位图按钮,调用setstate将其设置为pushdown(反派的哭闹状态),并设置m_bgameover,设置为true标志游戏结束,否则,先调用setstate将位图按钮设置为pushdown, 并在onlbuttomup中将位图按钮设置为正常状态,然后调用caculate函数记录周围的矿数,最后调用invalidate使客户区无效,强制ondraw函数重绘客户区。 调用Invalidate时,不要重画背景,以免闪烁,从而完成雷区按左键的响应动作。
wm_rbuttomdown消息响应函数onlbuttomdown将被认为有地雷位置的m_ibombnum.findbomb设置为减少左上角的地雷数量,然后判断是否已经排完所有地雷。如果是,游戏结束,弹出输入对话框,允许扫雷舰在响应idok通知代码时输入名字并写入注册表。如果不是所有的地雷都被排出,客户区将无效,迫使ondraw函数重新绘制客户区,并在雷区中按下右键。
每次点击左键或右键,ondraw函数将被调用到重雷区和控制区。由于点击情况的复杂性和地雷属性的多样化,需要精心设计ondraw函数。
函数calculate计算某个雷周围的雷数。根据前面的分析可知,计算某个雷周围的雷数是一个递归的过程。编译时要注意递归的边界条件。如果不重视,就会陷入无限递归,耗尽系统的资源。
6.菜单命令的响应是游戏交互的另一种重要方式。以下九个命令响应功能分别对应九个菜单项,用于完成用户的更新和设置命令。
Ongamebegin完成清除初始时间、随机布雷、根据颜色指示加载12小位图、调用ondraw重绘雷区的工作。随机矿就是多次调用rand(),确定m_bomb[i][j]的值。is根据其返回值进行装箱。
Ongamecustom首先弹出ccustomer对话框。当用户输入设置并响应idok通知代码时,将用户输入的雷数和行数、列数分别赋给cview的数据成员m_ibombnum、m_irow和m_icolumn,得到框架窗口的指针,用它调用movewindow将窗口调整到需要的大小,破坏原位置的位图按钮,在X轴坐标1/2处的新窗口宽度减去8。最后调用ongamebegin重启游戏。
ongamejunior、ongamemiddle和ongamesenior三个函数与ongamecustom类似,只是赋给cview的数据成员m_ibombnum、m _ arrow和m_icolumn被赋予了固定值,它们的大小可以由程序员决定。作者把他们定义为初级(20,8,8)和中级(40,65438+。
ongamecolor函数破坏原有的位图按钮,根据重新加载位图的标志m_iscolor创建新的位图按钮,反转加载12单色位图的标志,调用ongamebegin重新启动游戏。
ongamesort函数根据当前游戏级别从注册表中读取排名,并弹出排序对话框显示结果。到现在为止,自己编的一个扫雷游戏已经基本完成了。编译几百行代码,找出小错误,最后重新构建运行。好了,一个可爱的扫雷游戏就会出现在你面前。反正你的作品也不比微软差,还可以用各种方式画小位图,当然你得自己认。