第 3 章 打地鼠

第 3 章 打地鼠

本章将创建一个“打地鼠”游戏。游戏的灵感来自一款经典的街机游戏 Whac-A-Mole ™。游戏中,玩家手执木槌,每当有小动物从洞中冒出来时,就用木槌击打它们,击中得分。“打地鼠”的创作者是 App Inventor 开发团队的成员之一,她号称是为了测试精灵组件的功能(她做到了),其实她自己是个不折不扣的游戏迷。

{%}

当 Ellen Spertus 加入谷歌公司的 App Inventor 团队时,她热切地希望 App Inventor 能够增加对游戏的支持,因此她自告奋勇地承担起实现精灵组件的任务。精灵原本用来表示神话中的角色,如仙女、妖精等,到了 20 世纪 70 年代,它被计算界用来代表那些能够在电脑屏幕上移动的影像(在电子游戏中)。Ellen 第一次使用精灵是在 20 世纪 80 年代早期,当时她参加了电脑训练营并使用 TI 99/41 编程。她在精灵组件以及“打地鼠”游戏上所做的努力,受到了对计算机以及游戏双重怀旧情绪的驱使,它们是她童年时代的最爱。

1由美国德州仪器生产的一款早期的家用电脑,发布于 1979 年 11 月,价值 1150 美元,CPU 为 TI TMS9900,主频 3MHz,16KB 内存,26KB 只读存储器,192 像素 ×256 像素、16 色、13 英寸监视器,内置 TI BASIC 语言。——译者注

3.1 作品描述

如图 3-1 所示的“打地鼠”应用将实现以下功能。

  • 一只地鼠随机出现在屏幕上,每秒钟移动一次。

  • 玩家用手指触摸地鼠,如果碰到地鼠,则让设备振动,并显示命中数增加 1,然后地鼠立即移动到一个新位置。

  • 如果手指触摸到屏幕但没点击中地鼠,则显示失败数增加 1。

  • 点击“重新开始”按钮,游戏重新开始,命中和失败的计数都归零。

{%}

图 3-1:“打地鼠”游戏的用户界面

3.2 学习要点

本章内容覆盖了以下组件及概念。

  • 精灵组件:具有触感的可移动图像。

  • 画布组件:精灵的舞台。

  • 计时器组件:用来计时,让精灵每秒钟随机移动一次。

  • 音效播放器组件:击中地鼠时产生振动。

  • 按钮组件:开始新游戏。

  • 过程:用来实现一系列指令的一段代码,可以重复调用,如“移动地鼠”过程。

  • 产生随机数。

  • 使用加法块(+)及减法块(-)。

3.3 准备开始

登录 App Inventor 网站,新建“打地鼠 ”项目,将屏幕的标题属性设为“打地鼠”,连接测试设备或模拟器,以便进行实时测试。

从下面的网址下载地鼠图片 mole.png:http://appinventor.org/bookFiles/MoleMash/mole.png。下载成功后,在设计视图中组件列表下方的素材区,单击“上传文件…”,找到刚下载的文件 mole.png,将其上传到项目中。

3.4 设计组件

创建“打地鼠”游戏需要以下组件。

  • 画布组件:用来限定游戏中地鼠的活动范围。

  • 精灵组件:用来显示地鼠图片,可以随机移动,并且可以感知触摸。

  • 音效播放器组件:当地鼠被触摸到时,发出振动。

  • 标签组件:用来显示文字“命中:”“失败:”以及命中、失败的次数。

  • 水平布局组件:用来放置标签组件,使组件的布局合理。

  • 按钮组件:用来将命中及失败次数归零(重新开始游戏)。

  • 计时器组件:使地鼠每秒钟随机移动一次。

表 3-1 显示了应用中用到的全部组件。

表3-1:“打地鼠”应用中的全部组件列表

组件类型

组件种类

命名

作用

画布

绘图动画

画布 1

精灵的容器

精灵

绘图动画

地鼠

用户击打的目标

按钮

用户界面

重新开始按钮

重新设置得分:得分归零

计时器

传感器

计时器 1

控制地鼠的移动频率

音效播放器

多媒体

音效播放器 1

当地鼠被击中时产生振动

标签

用户界面

命中提示标签

显示文字“命中:”

标签

用户界面

命中次数标签

显示命中次数(数字)

水平布局组件

布局组件

水平布局 1

放置命中提示标签及命中次数标签

标签

用户界面

失败提示标签

显示文字“失败:”

标签

用户界面

失败次数标签

显示失败次数(数字)

水平布局组件

布局组件

水平布局 2

放置失败提示标签及失败次数标签

3.4.1 设置行为组件

本小节将设置游戏中所需的行为组件,下一个小节再来设置显示分数的组件。

(1) 从组件面板的绘图动画分组中,拖动画布组件,并放置在预览窗口中,采用其默认名称“画布 1”,设置其宽度属性为“充满”,即与屏幕等宽,设置其高度属性为 300 像素。

(2) 同样,从组件面板的绘图动画分组中,拖动精灵组件,将其放置在画布 1 中,位置随意。在组件列表底部单击重命名按钮,将其改名为“地鼠”,设置其图片属性为之前上传的 mole.png。

(3) 从组件面板的用户界面分组中,拖入一个按钮,放置在画布 1 下方,将其重新命名为“重新开始按钮”,并设置其显示文本属性为“重新开始”。

(4) 从组件面板的传感器分组中,找到计时器组件,将其拖放在预览窗口中,它将自动落入预览窗口下方的“非可视组件”区域。

(5) 从组件面板的多媒体分组中,找到音效播放器组件,将其拖放到预览窗口中,它也将落在“非可视组件”区域。

现在设计视图看起来应该如图 3-2 所示(地鼠的位置有可能不同)。

{%}

图 3-2:设计视图中的行为组件

3.4.2 设置标签组件

现在设置标签组件来显示用户的游戏得分:命中次数标签与失败次数标签。

(1) 从布局组件中拖出水平布局组件,将其放置在重新启动按钮的下方,保留水平布局 1 的默认名称。

(2) 从用户界面分组中拖出两个标签,放置在水平布局 1 中。

  • 将左侧的标签改名为“命中提示标签”,设置其显示文本属性为“命中:”(确保冒号后面有一个空格)。

  • 将右侧的标签改名为“命中次数标签”,设置其显示文本属性为“0”。

(3) 拖入第二个水平布局组件,将其放在水平布局 1 下方。

(4) 将两个标签拖放到水平布局 2 中。

  • 左侧标签改名为“失败提示标签”,设置其显示文本属性为“失败:”(确保冒号后面有一个空格)。

  • 右侧标签改名为“失败次数标签”,设置其显示文本属性为“0”。

你的屏幕看起来如图 3-3 所示。

{%}

图 3-3:设计视图中的全部组件

3.5 为组件添加行为

组件已经创建完成,下面切换到编程视图来设定组件的行为。具体的设定目标为:(1) 让地鼠每秒钟在画布 1 上随机地移动一次;(2) 无论地鼠出现在哪里,用户的目标是拍打它(建议用手指而不是木槌!):如果拍到地鼠,显示命中次数加 1,如果没拍到,显示失败次数加 1;(3) 按下重新启动按钮,将命中及失败次数归零。

3.5.1 移动地鼠

在目前已经完成的应用中,我们曾经调用过内置过程 2,如“你好猫咪”应用中的“让音效播放器 1 振动”3。假如 App Inventor 中有一个内置过程,可以将精灵随机地移动到屏幕的某个位置,那岂不是很好?可惜没有,不过我们可以自己来创建过程!就像内置过程一样,自己创建的过程会显示在内置块的过程抽屉中,在需要的时候,可以随时随地调用它。

2隶属于组件的紫色代码块,可以理解为组件具备的功能或组件可以执行的指令。在本书中,“调用某个组件的 ×× 过程”等同于“执行某个组件的 ×× 指令”或“实现某个组件的 ×× 功能”,其中“调用过程”的说法为编程技术中的术语。——译者注

3等同于“执行音效播放器 1 的振动指令”或“实现音效播放器 1 的振动功能”或“调用音效播放器 1 的振动过程”。——译者注

具体来说,我们将创建一个名为“移动地鼠”的过程,让地鼠在屏幕上移动到某个随机位置。共有三处需要调用该过程:(1) 游戏开始时调用一次;(2) 当用户成功地点击到地鼠后调用一次;(3) 每秒钟调用一次。

3.5.2 创建移动地鼠过程

要理解地鼠如何移动,需要首先了解安卓设备屏幕的定位机制。

画布(以及屏幕)可以看作是由 x(水平)坐标和 y(垂直)坐标织成的网格,其左上角的 (x, y) 坐标为 (0, 0)。x 坐标向右为增大,y 坐标向下为增大,如图 3-4 所示。一个精灵的 x 坐标、y 坐标属性表示它的左上角所在的位置。因此,当地鼠位于屏幕左上角时,它的 x 坐标和 y 坐标都是 0。

{%}

图 3-4:地鼠在画布上的位置:坐标、高度及宽度(蓝色表示 x 坐标及宽度,棕色表示 y 坐标及高度)

为了将地鼠的移动限制在屏幕之内,要确定 x 坐标和 y 坐标的最大值,这要用到地鼠和画布 1 的宽度及高度属性。(地鼠的宽度和高度属性值与上传图片的大小相同,而在创建画布 1 时,画布的高度被设定为 300 像素,宽度为“充满”,即等于它的“父”容器——屏幕的宽度。)如果地鼠图片的宽度是 36 像素,画布的宽度是 200 像素,那么地鼠的 x 坐标最小可以为 0(靠近屏幕左侧边缘),而最大为 164(200-36,或画布 1 的宽度减去地鼠的宽度),这样才能保证地鼠不超出屏幕的右侧边缘。同样,地鼠顶部的 y 坐标范围可以从 0 到(画布 1 的高度 - 地鼠的高度)。

图 3-5 显示了创建的移动地鼠过程,图中标有详细注释(可以有选择地添加到过程中)。

为了随机设定地鼠出现的位置,x 坐标要在 0 到(画布宽度 - 地鼠宽度)范围内选择,同样,y 坐标要在 0 到(画布高度 - 地鼠高度)范围内。使用数学抽屉里的“1 到 100 之间的随机整数”块生成一个随机数,将最小值从默认的 1 改为 0,同样修改最大值,如图 3-5 所示。

{%}

图 3-5:移动地鼠过程,用于将地鼠放在一个随机的位置上

按照下述步骤创建过程。

(1) 在编程视图中点击内置块分组中的过程抽屉。

(2) 拖出“定义过程……执行”块,而非“定义过程……返回”块。

(3) 设置过程名称:选中过程块中的“我的过程”并输入“移动地鼠”。

(4) 移动地鼠:单击地鼠抽屉,将“让地鼠移动到指定位置”块拖到过程块中“执行”的右侧。注意:该块右侧的开放插槽提示我们需要为其提供 x 坐标及 y 坐标。

(5) 设定地鼠的 x 坐标:如前所述,x 坐标范围在 0 与(画布宽度 - 地鼠宽度)之间,具体操作如下。

  • 点击内置块中的数学抽屉,拖出“1 到 100 之间的随机整数”块,让其左侧的插头(突起)与“让地鼠移动到指定位置”块的 x 坐标插槽吻合。

  • 将随机整数块中的第一个数字 1 改为 0。

  • 丢弃第二个数字 100:点击该块,再按键盘上的 Del 或 Delete 键,或直接将其拖入垃圾箱。

  • 点击数学抽屉,将一个减法块(-)拖入到原来数字 100 所在的插槽中。

  • 点击画布抽屉,向下滚动直到看见“画布 1 的背景颜色”块,将其拖入到减法块的左侧,然后从“背景颜色”所在的下拉菜单中选择宽度选项。

  • 同样,点击地鼠抽屉并拖入“地鼠的启用”块,然后从“启用”所在的下拉菜单中选择宽度选项,并将其拖入减法块的右侧。

(6) 按类似步骤设定 y 坐标,应该是一个从 0 到(画布高度 - 地鼠高度)之间的随机整数。

(7) 对照图 3-5 检查操作结果。

3.6 在应用启动时调用移动地鼠过程

移动地鼠过程已经创建完成,现在该调用它了。对于程序员来说,最熟悉的事情就是在应用启动的同时执行某些指令,“当 Screen1 初始化时”块就是专为这个目的而设计的。

(1) 点击 Screen1 抽屉,并拖出“当 Screen1 初始化时”块。

(2) 单击过程抽屉,你会看到一个“调用移动地鼠”块(这很有趣:你自己创建了一个新块,不是吗?)。把它拖入屏幕初始化程序中,如图 3-6 所示。

{%}

图 3-6:在应用启动时调用移动地鼠过程

3.6.1 每秒钟调用一次移动地鼠过程

要让地鼠每秒移动一次,需要用到计时器组件。设置计时器 1 的计时间隔属性为其默认值 1000(毫秒),即 1 秒。这意味着,每隔 1 秒钟,计时事件处理程序中的所有代码块都将被执行一次。以下是具体的实现步骤:

(1) 单击计时器 1 抽屉,并拖出“当计时器 1 到达计时点时”块;

(2) 单击过程抽屉,将“调用移动地鼠”块拖到计时事件块中,如图 3-7 所示。

{%}

图 3-7:每当计时器达到计时点,执行一次移动地鼠过程

如果觉得地鼠移动得太快或太慢,可以在设计视图中改变计时器 1 的计时间隔属性,来增加或减小地鼠的移动频率。

3.6.2 记录成绩

刚才我们创建了两个标签:命中次数标签及失败次数标签,并设置它们在初始状态时的显示文本属性为 0。我们希望以此来保存用户的成绩:用户每命中地鼠一次,或失败一次(直接拍打到屏幕),对应标签中的数字递增,为此要用到画布 1 的触摸事件处理程序。触摸事件中携带了三个参数,不过我们并不关心前两个参数(触摸点的 xy 坐标),我们关心的是第三个参数——碰到任意精灵。在这里,如果用户碰到了地鼠(精灵),则该值为真;否则,如果用户直接碰到了画布,则该值为假。图 3-8 显示了具体的代码。

{%}

图 3-8:触摸画布 1 时,让命中次数或失败次数递增

图 3-8 可以理解为:当用户触摸画布时,判断是否同时碰到了精灵。应用中只有一个精灵组件,因此只可能碰到地鼠。如果碰到地鼠,则命中次数标签的显示文本值增加 1,否则,失败次数标签的显示文本值增加 1。

下面介绍如何创建这些块。

(1) 点击画布 1 抽屉,并拖出“当画布 1 被触摸时”。

(2) 单击控制抽屉,拖出“如果……则”块,点击其左上角的蓝色方块,为其添加“否则”分支,并

放入“当画布 1 被触摸时”块中(此时,“如果……则”块变为“如果……则……否则”块)。

(3) 将鼠标悬停在触摸事件块的“碰到任意精灵”参数上,从中拖出“碰到任意精灵”块,并放入“如果……则……否则”块的判断插槽中(“如果”右侧)。

(4) 按照我们的设想,如果判断结果为真(即地鼠被触摸到),则命中次数递增。

  • 从命中次数标签的抽屉中拖出“设命中次数标签的显示文本为”块,并放入“则”的右边。

  • 点击数学抽屉,拖出一个加号(+),将其放在上一个块的插槽中。

  • 点击命中次数标签的抽屉,将“命中次数标签的显示文本”块拖到“+”的左边。

  • 点击数学抽屉,拖动一个数字块“0”块到“+”的右边,将 0 改为 1。

(5) 重复步骤 4,在“如果……则……否则”块的“否则”分支中,设定失败次数标签的相关代码。

 测试:在测试设备上触摸画布,命中或错过地鼠,看看分数有什么变化。

3.6.3 过程抽象

编写程序的重要手段之一,就是编写并命名一段代码,然后在需要的时候调用这段代码(如移动地鼠)。这种编程方法被称为过程抽象。之所以叫作“抽象”,是因为过程的调用者(在实际项目中,过程的调用者可能不是过程的开发者)只需要知道过程的名称以及具体功能(如移动地鼠),而不需要知道过程的实现方法(如生成两个随机整数)。如果没有过程抽象,那些大型程序是不可能实现的,因为开发者个人无法同时处理过多的代码,这一点与现实世界中的劳动分工相类似。例如,在生产汽车的企业中,不同的工程师负责设计不同的汽车部件,没有人了解所有部件的细节;同样,司机只需要知道具体的操作方法(例如,踩下制动踏板把车停下来),而无需了解这些功能是如何实现的。

与复制和粘贴代码相比,过程抽象的优势如下。

  • 由于过程的代码完全独立于程序的其他部分,因此更易于对代码进行测试。

  • 如果代码中有错误,只需要对局部进行修改。

  • 如果需要改变过程的功能,如确保地鼠不连续出现在同一个位置,只需要修改一处的代码。

  • 可以将写好的过程汇集到一个程序库中,以便在不同的程序中使用。(遗憾的是 App Inventor 暂时不支持这项功能。)

  • 将大块代码拆分成代码片段,这将有助于我们理解应用功能,并逐步加以实现(“分而治之”)。

  • 给过程一个有意义的名称,将有助于提高代码的可读性,使其更易被别人(或一个月后的自己)读懂。

在后面的章节中,还将学到过程更加强大的功能:添加参数,提供返回值,以及调用过程本身。有关内容请参见第 21 章。

3.6.4 重置分数

你的朋友看到你玩“打地鼠”,说不定他也想试试身手,一较高下,这时最好能将分数重新归零。根据前面学过的内容,不经提示你也有能力把它做出来。继续阅读之前,动脑筋试试看。

我们要在重新开始按钮的点击事件中,将命中次数标签以及失败次数标签的显示文本设置为 0,如图 3-9 所示。

{%}

图 3-9:按下重新开始按钮时,让命中次数及失败次数归零

此处提供一个技巧,来快速建立重新开始按钮的事件处理程序:在设置标签的显示文本时,可以在工作区直接输入 0 并回车,此时将生成数字块 0,这等同于从数学抽屉中拖出数字块。(这种输入方式对其他代码块也同样有效。)

 测试:开始游戏,尝试多次命中及错过地鼠,然后点击重新启动按钮。

3.6.5 碰到地鼠引起振动

我们希望在用户触摸到地鼠时,地鼠会移动到另一个随机位置,同时设备发出振动。这要用到音效播放器组件的振动功能,如图 3-10 所示。

{%}

图 3-10:碰到地鼠时让设备短暂振动(100 毫秒)

 测试:当你在测试设备上真实地触摸到地鼠时,体会一下振动的效果。如果觉得振动时间过长或过短,可以修改“让音效播放器 1 振动”块的毫秒数。

3.7 完整的“打地鼠”应用

图 3-11 中描述了完整的“打地鼠”应用中所有的代码块。4

4移动地鼠过程里参数 xy 坐标接受的随机数块采用了“外挂输入项”的显示方式,以缩减代码宽度,对该块点击右键,在弹出的菜单中进行选择设置。——译者注

{%}

图 3-11:完整的“打地鼠”应用

3.8 改进

对“打地鼠”应用还可以做如下补充。

  • 添加两个按钮,让用户可以控制地鼠移动的时间间隔。

  • 添加一个标签,随时显示地鼠出现(或移动)的累计次数。

  • 再添加一个精灵,把它装扮成一朵花:用户不许碰到它,如果碰到将会受到惩罚,减少得分或结束游戏。

  • 利用图片选择框组件,让用户从手机中选择图片,来替代地鼠图片。

3.9 小结

本章介绍了一些非常有用的技巧,适用于普通应用,更适用于游戏。

  • 画布组件使用了直角坐标系,其中 x 坐标表示水平方向(从左边的 0 到右边的画布宽度 -1),y 坐标表示垂直方向(从顶部的 0 到底部的画布高度 -1)。从画布的高度和宽度中减去某个精灵的高度和宽度,这个范围可以确保精灵在画布上完整地显示。

  • 利用画布及精灵组件的触摸感知功能,可以充分发挥设备触屏的优势。

  • 创建实时交互应用:不仅可以对用户的行为作出响应,也可以对设备内部的计时器作出响应。具体地说,计时器组件的计时间隔属性用于设定计时事件的触发频率,这个频率也可以用于控制精灵或其他组件的移动。

  • 标签组件可以用于显示得分,根据玩家的操作结果,得分会相应升高(或下降)。

  • 通过音效播放器的振动功能,对用户的触摸事件进行反馈,让设备振动一定的毫秒数。

  • 不仅可以调用内置过程,也可以通过给一段代码设定名称(移动地鼠)来创建自己的过程。这些过程也可以像内置过程一样被调用,这就是所谓的过程抽象。在计算机程序设计中这是一个非常重要的思想,可以实现代码的复用,也使得创建复杂应用成为可能。

  • 利用数学抽屉中的取随机整数块,可以产生不可预知行为,让游戏每次开始时都有所不同。

在第 5 章(瓢虫快跑)中,你将了解更多的游戏制作技巧,包括移动中的精灵组件之间的碰撞检测。

目录

  • 版权声明
  • O'Reilly Media, Inc. 介绍
  • 前言
  • 第一部分 App Inventor 2 教程
  • 第 1 章 你好猫咪
  • 第 2 章 油漆桶
  • 第 3 章 打地鼠
  • 第 4 章 开车不发短信
  • 第 5 章 瓢虫快跑
  • 第 6 章 巴黎地图旅游
  • 第 7 章 安卓,我的车在哪儿
  • 第 8 章 总统问答
  • 第 9 章 木琴
  • 第 10 章 出题与答题
  • 第 11 章 广播中心
  • 第 12 章 遥控机器人
  • 第 13 章 亚马逊掌上书店
  • 第二部分 Inventor 指南
  • 第 14 章 理解应用的结构
  • 第 15 章 软件工程与应用测试
  • 第 16 章 应用的存储
  • 第 17 章 创建动画应用
  • 第 18 章 程序中的决策
  • 第 19 章 数据列表编程
  • 第 20 章 循环
  • 第 21 章 定义过程与代码复用
  • 第 22 章 数据库
  • 第 23 章 传感器
  • 第 24 章 网络通信
  • 关于作者