Android 扫雷游戏

来源:互联网 发布:死刑 知乎 编辑:程序博客网 时间:2024/05/24 01:46

 

基于Android平台的扫雷游戏

Download MinesweeperForAndroid_Source – 1.16 MB



扫雷-开始的截图

扫雷-新游戏的截图

扫雷-胜利的截图

扫雷-失败的截图

简介

扫雷是一个简单的单人游戏。游戏的目的是在没有触碰任何一个地雷的情形下清空一个雷区。扫雷不仅有Windows版本,也有其他平台的版本(包括大多数Linux的变体)。扫雷在Windows的用户中十分流行,自从Windows 3.1以来一直绑定在里面。

在这篇文章中我们创建一个Android版本的扫雷。我们将尝试Windows扫雷的大多数功能。这篇文章面向中高级别的开发者,并且需要熟悉Java 和Android开发。

关于游戏

在扫雷游戏中,游戏者要面对一片砖块组成的格栅,有些砖块包含随机设置的地雷。我们将实现一个典型的初学者级别扫雷游戏:行和列的数量都是9,一共有10颗地雷。扩展成中级和高级游戏很容易(只需要改编代码中的上述三个值)。

好吧,在这篇文章中我不会教你怎么玩游戏,但我们需要在实现前了解这个游戏的一些特点(Windows版本):

  1. 点击左键打开一个砖块
  2. 点击右键对砖块进行标记:插旗帜(表示下面有雷),标问号(不确定是否有雷),取消标记
  3. 第一个砖块下面永远没有雷;避免了一开始就要猜测的烦恼。
  4. 如果打开的砖块是空的,周围的砖块都会依次打开直到遇到数字砖块;类似涟漪的原理。
  5. 在一个砖块上同时点击左右键或者中键,如果周围的地雷都被标出来了,则会打开周围所有未打开的砖块。
  6. 点击第一个砖块时开始计时,在选择一个新游戏后不会开始计时。

已经啰嗦够多了,我们现在开始编写这个游戏

一步一步实现

解释和实现任何复杂系统的最好方法是一次一步进行。首先,我们探讨GUI,布局和应用的视觉。我们也会探讨创建布局时用到的一些技术。然后,我们讨论鼠标、点击和触摸事件的区别,以及应用的反应。最后一步关于实现整个游戏以及上面介绍过的功能。

视觉和触碰的感知体验

我们先讨论设计GUI的不同方面,包括总体的应用布局和一些创建游戏时需要用到的技术。

应用的布局

我们使用TableLayout进行扫雷的布局,在TableLayout上加入三行:

  1. 第一行包括用于计时的三列数字,新游戏按钮和剩余地雷数。我们使用TextView显示计时和地雷数。新游戏按钮使用ImageButton
  2. 第二行是50像素高的空的TextView,将第一行和雷区隔离开
  3. 第三行是另一个TableLayout,用来显示雷区。我们将动态的向这个区域添加按钮。

布局的代码如下(删去了一些额外的属性来节省空间):

 

使用外部字体

我们使用外部字体LCD mono(见源代码部分)来显示时间和地雷数。在Android中使用外部字体很容易,分为两步:

  1. 在工程的assets文件夹下创建字体文件夹,复制TTF(True Type Font)文件至字体文件夹。
  2. 通过调用createFromAsset 和传递TTF文件名创建一个Typeface对象。在这个对象中设置TextView的Typeface。代码如下:
 
使用风格

在我们的扫雷游戏里,点击新游戏的笑脸按钮会让这个笑脸变成紧张的表情。当按钮处于按压状态时(紧张的笑脸),我们需要一个不同的图像。当它处于正常状态时需要另一张图像。为了实现此功能,我们使用风格,效果如下:

使用风格也需要两步:

  1. 创建一个XML文件(风格定义文件),指定相应的按钮状态使用的图片。比如在按压状态下,我们需要惊讶(紧张)的图片,正常状态下需要微笑的图片。当然,这两幅图片已经复制到 res/drawable文件夹中了。风格文件的代码如下:
 
  1. 为新游戏按钮更新/添加背景的属性值,将它的值设定为上面创建的风格文件。更新后的ImageButton代码如下:
 
TableLayout中的动态行

动态添加砖块的原理相同(砖块是从按钮类继承的类,包含支持实现的额外功能)。我们希望它们像预想的那样工作,分为如下的几步:

  1. 创建一个TableRow对象,设置布局的参数值。
  2. 将砖块添加到上面创建的行对象中。
  3. 使用 findViewById 函数获得TableLayout(雷区)的样本。
  4. 将上边创建的行添加到TableLayout中

代码如下:

 

设置扫雷图标

改变应用的图标(图标在主页和启动窗口中显示)非常简单,因为这个工程有默认的图标,在 res/drawable 文件夹中名为 icon.png,改变/更新/替换工程中的icon.png,则新图标将在启动窗口中显示出来。了解关于Android图标设计的更多细节,请阅读图标设计指南。

用于多个像素版本的图像

正如我们都知道的,Android支持并能够在不同尺寸的设备上运行。这些设备可能有不同的屏幕尺寸,不同的高宽比,不同的分辨率,不同的密度和不同的像素支持。为了支持各种设备,我们必须定制不同设备所用的图像。而现在这是不可能的。Google推荐使用为三种普遍的屏幕密度设置不同图像的方法,这三种屏幕分别是 Low DPI,Medium DPI和High DPI。图像按照各自的密度分别复制到文件夹 res/drawable-hdpi,res/drawable-mdpi和res/drawable-ldpi中。如果希望Android自动调整图像(这种方法一点都不给力),那么仅需要设置一个图像(扫雷就使用这种方法),并将其复制到 res/drawable-nodpi文件夹。更多信息请阅读支持多种屏幕。

计算时间

开发游戏的最重要的事情就是跟踪时间。Java世界里的标准方法是使用java.util.Timer或者java.util.TimerTask 保持时间。这里麻烦的地方在于我们需要创建一个新的线程,一些情况下我们并不想这样。Android有更好的解决方案。我们可以使用类 android.os.Handler实现目标。

用Handler取代Timer

可以用两种方法使用Handler,一种是给Handler传递消息并在消息收到时执行特定的操作,另一种是在Handler中调度一个Runable对象。我们在扫雷中使用第二种方法,使用Handler的好处在于它和creator线程中的thread/message队列相联系。了解更多关于Handler的信息,请阅读Handler官方文档,实现handler的代码如下:

 

处理用户操作

在扫雷中,我们接受点击和长点击事件。点击事件表示点击鼠标左键,长点击事件表示点击鼠标右键。我们没有对触碰事件进行同样的设定,虽然我们也可以这样做,但是这样做的话应用就只能限制于触摸设备。而在可触摸设备上,可以用触摸来表示点击和长点击事件,而在不可触摸设备上,可以用滚轮和按键来表示点击事件和长点击事件。

理解鼠标的点击和电话的点击事件

Java提供实现鼠标按钮的功能,支持鼠标的点击/长按/释放,鼠标的移动/拖拽,鼠标的进入/退出,以及滚轮事件。对Android来说,在进入/退出事件和滚轮事件之上没有鼠标的概念。Android仅提供点击和触摸功能。点击包括普通的点击和长点击,点击事件不支持拖拽功能,触摸事件支持拖拽功能。

理解电话的点击和触摸事件

触碰事件将在以下状况中击发:用户在触碰模式下触碰一个项目,或者用导航键/跟踪球指向一个项目并按下进入键,或者用跟踪球指向一个项目并按下跟踪球。当用户完成符合触碰事件的操作时,触碰事件将被调用,包括按压、释放、或者屏幕上的任何移动操作(在该项目的范围内)。

理解电话的点击和长点击事件

点击事件将在以下状况中击发:用户在触碰模式下触碰一个项目,或者用导航键或跟踪球指向一个项目并按下进入键,或者用跟踪球指向一个项目并按下跟踪球。长点击事件将在下列状况下击发:用户在触碰模式下一直触摸一个项目,或者用导航键/跟踪球指向一个项目并一直按下进入键,或者用跟踪球指向一个项目并一直按下跟踪球(至少一秒)。

模拟左右键

Android不支持中间键的点击事件,我们使用长点击事件来模拟此功能。如果长点击事件在一个有数字的打开的砖块上发生,我们就能击发相应的功能。这部分的代码如下:

 
不可用按钮的事件

当一个按钮不可用时,我们不能在上面击发任何事件。为了克服这个缺陷,我们将这个键标为不可用并改变它的背景。事实上,一个键不可能真正丧失功能,它一直是可用的。在这种情况下,这个键仍然可用但假装不能用,而且可以接受事件。

决定策略

让我们回到游戏最重要的方面,来实现这个游戏。我们在这个部分讨论关于游戏部分中介过的功能。我们按照顺序一个一个实现(其中之一已经在模拟左右键中介绍过了)

完成流程图

扫雷游戏最重要的部分是处理用户操作。首先等待并接受用户输入,然后恰当的处理。我相信与其我用文字来解释,不如我用图表来解释的效果更好,图表的说服力更强。整个循环用下面的流程图表示:

从第一次点击开始

之前我们说过,计时器从第一次点击开始(打开第一个砖块),在按下新游戏按钮时不会开始计时。这一点对于控制时间来说非常重要,为了实现这个功能,我们只需创建一个布尔变量,只要接受到点击事件就检查此变量,开始调用Handler,然后改变此变量的值。这部分代码如下:

 

第一次点击没有地雷

用户的第一次点击不应该触发地雷,否则一开始就要猜测。因此我们在第一次点击后设置地雷。我们在用户刚刚打开的砖块之外的部分随机设置地雷(通过随机设置行和列的数字)。之后为所有砖块计算邻近的地雷数目生成数字砖块。这部分代码如下:

 

打开砖块的涟漪效果

用户在打开一个砖块后得到有关下一步的提示,如果打开的砖块是空的,用户就无法猜测地雷的情况并决定下一步操作。为了避免这种情况,我们打开空白砖块邻近的砖块,一直递归的打开直到遇见有数字的砖块。类似于涟漪的效果。递归打开空白砖块(涟漪效果)的代码如下:

 

在砖块上做标记

我们进一步讨论将砖块标记为旗子、问号和取消标记的方法。实现的方法直接明了,当我们收到非左右键点击的长点击事件时,我们检查砖块现在的状态。如果砖块上没有标记,就在上面插旗子(表示内有地雷),如果砖块上插着旗子,就在上面标问号(怀疑但不确定有地雷),而如果砖块上面有问号标记,就清除标记。我们只需要一些条件判断句就能实现:


在每一步检查游戏的输/赢

对滴,这一步非常重要。每次点击后都要检查游戏的状态,确保我们不会错过任何点击和任何砖块。当点击的砖块下边有地雷时,我们就输了。当所有包含地雷的砖块上插了旗子时,我们就赢了。代码如下:

 

测试/玩儿法

玩游戏的方法和Windows版是一样的。请留意以下关键点:

  • 点击微笑按钮,开始一个新游戏
  • 点击或者触摸一个砖块来打开它,这和Windows中的鼠标左键功能相同。
  • 点击或者保持触摸一个砖块达到一秒,则对其进行标记,包括旗帜、问号或者清除标记。这和Windows中的鼠标右键功能相同。
  • 点击或者保持触摸一个打开的数字砖块,则将周围的砖块都打开(周围的所有地雷已经插了旗子),这和Windows中的中间键功能相同。
  • 没有插旗砖块的图标,用F字母表示旗帜。
  • 亲自实现并试玩,反馈你的体验。

总结

用一篇文章解释一个完整的动画游戏不是非常容易。我已经尽力解释了扫雷的工作原理以及如何操作游戏中重要的部分。下边附有游戏的源代码,虽然我们一直使用初级程度的扫雷,但是实现中级和高级的扫雷非常容易,只需要在代码中做少数修改。请提供你使用的反馈和建议。

资源

创建扫雷GUI的图像属于各个作者,图像的资源为:

背景图像:Fractal Blue Abstract Mobile wallpaper

笑脸图像:GNOME Desktop Icon Pack by GNOME icon artists

地雷图像:Farm-Fresh Web Icon Pack by FatCow Web Hosting

LCD字体格式:LCD Mono by Samuel Reynolds

历史

初稿:2010年9月28日

授权

这篇文章及相关的源代码和文件,经过The Code Project Open License(CPOL)协议的授权。

关于作者

The Manoj Kumar

原文链接:http://www.codeproject.com/KB/android/MinesweeperForAndroid.aspx

原作者:The Manoj Kumar

 

原创粉丝点击