Scratch-编程学习指南-全-
Scratch 编程学习指南(全)
原文:
zh.annas-archive.org/md5/21876022919100b848f73fe56613ad11译者:飞龙
第一章。入门
你是否曾经想过自己制作一款电脑游戏、动画故事、教程或科学模拟?Scratch是一种图形化编程语言,它让创建这些应用变得轻松而有趣。在本章节中,你将:
-
探索 Scratch 的编程环境
-
了解不同类型的命令块
-
在 Scratch 中创建你的第一个游戏
当你制作一个 Scratch 应用时,你可以将它保存在电脑上或上传到 Scratch 网站,其他人可以对它进行评论,并将它改编成新的项目。
激动了吗?那么让我们开始吧!
什么是 Scratch?
计算机程序就是一组指令,用来告诉计算机该做什么。你通过编程语言来编写这些指令,这就是 Scratch 的作用。
大多数编程语言是基于文本的,这意味着你需要用看起来像是隐晦的英语命令来给计算机下指令。例如,要在屏幕上显示“Hello!”,你可能会写:
print('Hello!') (in the Python language)
std::cout << "Hello!" << std::endl; (in the C++ language)
System.out.print("Hello!"); (in the Java language)
学习这些语言并理解它们的语法规则对于初学者来说可能具有挑战性。另一方面,Scratch 是一种视觉编程语言。它是由麻省理工学院(MIT)媒体实验室开发的,目的是让编程变得更加容易和有趣。
在 Scratch 中,你不需要输入任何复杂的命令。相反,你将连接图形块来创建程序。感到困惑吗?看一下图 1-1 中的简单程序,我会为你解释。
图 1-1。当你运行这个 Scratch 块时,猫会在对话框中说“Hello!”。
在图 1-1 中看到的那只猫被称为精灵。精灵会理解并服从你给它们的指令。左侧的紫色块告诉猫在对话框中显示“Hello!”。在本书中,你将创建许多包含多个精灵的应用,并通过命令块让精灵移动、旋转、说话、播放音乐、做数学运算等等。
你可以通过将那些颜色编码的块像拼图块或乐高积木一样拼接在一起,在 Scratch 中进行编程。你创建的这些块堆叠称为脚本。例如,图 1-2 展示了一个改变精灵颜色四次的脚本。
图 1-2。使用脚本改变猫精灵的颜色
这个脚本在每次颜色变化之间等待一秒钟,屏幕上看到的四只猫展示了每次变化后精灵的新颜色。
试一试 1-1
尽管我们还没有讨论图 1-2 中的积木,但请阅读它们,观察它们的形状,并尝试弄清楚这个脚本是如何让猫变成青色的。(提示:第一个紫色积木会将猫恢复到原来的颜色。)如果我们从脚本中移除等待积木,你认为会发生什么?
本书涵盖了 2013 年 5 月发布的 Scratch 2 版本。这个版本允许你直接在网页浏览器中创建项目,因此你无需在计算机上安装任何软件,我们将依赖 Scratch 的网页界面来讲解本书的内容。
现在,既然你对这种编程语言有所了解,是时候开始我们的编程之旅,并学习如何使用它了。
Scratch 编程环境
要启动 Scratch,访问 Scratch 网站 (scratch.mit.edu/), 然后点击TRY IT OUT链接。这将把你带到 Scratch 的项目编辑器界面,如图 1-3 所示。
图 1-3. Scratch 用户界面,你将在此构建程序
你应该看到一个单一的窗口,其中至少包含以下三个面板:舞台(左上),精灵列表(左下),以及脚本标签页(右侧),该标签页包含积木标签页和脚本区域。右侧面板还包含两个额外的标签页,分别是服装和声音,稍后将在本节中讨论。如果你已登录 Scratch 网站的账户,你还应该看到背包(右下角),它有一些按钮,让你可以分享项目,并使用已有项目中的精灵和脚本。
我们快速看一下三个主要面板。
舞台
舞台是你的角色(精灵)移动、绘图和交互的地方。舞台宽度为 480 步,高度为 360 步,如图 1-4")所示。舞台的中心点* x 坐标为 0, y *坐标也为 0。
图 1-4. 舞台像一个坐标平面,中心点为(0,0)。
你可以通过将鼠标光标移到舞台上的任何点,观察鼠标(x, y)显示区域中的数字,来找到该点的(x,y)坐标,该区域位于舞台正下方。
位于舞台上方的小工具栏有多个控件。演示模式图标 ① 隐藏所有脚本和编程工具,使舞台区域几乎占据整个显示器。编辑框 ② 显示当前项目的名称。绿色旗帜 ③ 和停止 ④ 图标让你开始和结束程序。
尝试 1-2
移动鼠标到舞台周围并观察鼠标显示区域。当你将鼠标移出舞台区域时会发生什么?现在,切换到演示模式并观察屏幕如何变化。点击屏幕左上角的
图标,或按下键盘上的 ESC 键退出演示模式。
精灵列表
精灵列表显示项目中所有精灵的名称和缩略图。新项目开始时会有一个白色的舞台和一个穿着猫服的精灵,如 图 1-5 所示。
图 1-5. 新项目的精灵列表
精灵列表上方的按钮让你从四个地方将新精灵添加到项目中:Scratch 的精灵库 ①,内置的绘图编辑器 ②(你可以在这里绘制自己的服装),连接到你计算机的摄像头 ③,或你的计算机 ④。
尝试 1-3
使用位于精灵列表上方的一些按钮将新精灵添加到项目中。通过拖动相应的缩略图来重新排列精灵列表中的精灵。
项目中的每个精灵都有自己的脚本、服装和声音。你可以选择任何精灵来查看其属性。你可以通过 (1) 点击精灵列表中的缩略图,或 (2) 双击舞台上的精灵本身来选择精灵。当前选中的精灵缩略图会被高亮显示,并且用蓝色边框框住。当你选择一个精灵时,你可以通过点击脚本区域上方的三个标签之一来访问其脚本、服装和声音。我们稍后会查看这些标签的内容。目前,右键点击 (或在 Mac 上按住 CTRL 键点击) Cat 精灵的缩略图,查看弹出的菜单,如 图 1-6 所示。
复制选项 ① 会复制精灵并为副本指定一个不同的名称。你可以使用删除 ② 删除项目中的精灵,也可以使用保存到本地文件选项 ③ 将精灵导出为 .sprite2 文件到你的计算机。(要将导出的精灵导入到另一个项目中,只需点击 图 1-5 中显示的“从文件上传精灵”按钮。)隐藏/显示选项 ④ 允许你更改舞台上精灵是否可见。
图 1-6. 右键点击角色的缩略图显示此快捷菜单。
在你的角色缩略图旁边,角色列表还显示舞台的缩略图(参见图 1-6)。舞台有自己的脚本、图像和声音。你在舞台上看到的背景图像叫做背景。当你开始一个新项目时,舞台的背景默认为简单的白色背景,但你可以通过舞台缩略图下方的四个按钮添加新的背景图像。点击角色列表中的舞台图标可以查看并编辑其相关的脚本、背景和声音。
积木标签
Scratch 中的积木分为 10 个类别(调色板):运动、外观、声音、画笔、数据、事件、控制、感知、运算符和更多积木。为了帮助你轻松找到相关的积木,积木通过颜色编码。Scratch 2 有超过 100 个积木,尽管有些积木仅在特定条件下出现。例如,数据调色板中的积木(在第五章和第九章中讨论)只有在创建了变量或列表之后才会出现。让我们查看图 1-7 中的积木标签的各个组成部分。
图 1-7. 积木标签的放大视图
试着点击一个积木,看看它会做什么。例如,如果你点击运动调色板中的移动 10 步,角色将在舞台上移动 10 步。再点击一次,角色再移动 10 步。点击说“Hello!”2 秒积木(在外观调色板中),让角色在气泡框中显示“Hello!”持续两秒。你还可以通过从工具栏中选择积木帮助(问号图标)并点击你不理解的积木来访问该积木的帮助界面。
有些积木需要一个或多个输入(也叫做参数),这些输入告诉积木应该做什么。比如,移动 10 步积木中的数字 10 就是一个参数。查看图 1-8,了解积木如何让你改变它们的输入。
图 1-8. 更改不同类型积木的输入
你可以通过点击move 10 steps中的白色区域(看到数字 10 的地方)并输入一个新的数字①来更改步数,可能是 30,就像你在图 1-8 中看到的那样。一些积木,如point in direction 90,也有下拉菜单供你选择其输入②。你可以点击向下箭头查看可用选项列表并选择一个。这个特定命令有一个白色可编辑区域,所以你也可以直接在白色框内输入一个值。其他积木,如point towards③,会强制你从下拉菜单中选择一个值。
练习 1-4
进入外观面板,改变积木输入的值,然后点击积木看看它们的作用。例如,尝试set color effect to积木。试试数字 10、20、30 等等,直到猫恢复原本的颜色。使用下拉菜单中的选项并尝试不同的数字。你可以点击clear graphic effects积木(也在外观面板中)来清除你所做的更改。
脚本区域
要让一个精灵做有趣的事情,你需要通过从“积木”标签页拖动积木到“脚本区域”并将它们拼接在一起进行编程。当你在“脚本区域”内拖动积木时,白色高亮会指示你可以将该积木放置的位置,以便与另一个积木形成有效连接(图 1-9)。Scratch 积木只有在某些特定的方式下才能拼接在一起,这样避免了使用基于文本的编程语言时常见的输入错误。
图 1-9. 将积木拖入脚本区域并将它们拼接在一起创建脚本。
你不需要完成脚本就能运行它,这意味着你可以在构建脚本时测试它。点击脚本中的任何地方,无论是完整的还是部分的,都会运行整个脚本,从上到下。
练习 1-5
启动一个新的 Scratch 项目,并为Cat精灵创建以下脚本。(forever积木在控制面板中,其他积木则在运动面板中。)

你将在第二章中了解这些积木的大部分。现在,点击你的新脚本运行它。(Scratch 应该会用一个发光的黄色边框高亮显示正在运行的脚本,正如图像右侧所示。)你甚至可以在脚本运行时更改积木的输入并添加新的积木!例如,更改move积木中的数字,看看猫的运动如何变化。再点击一次脚本来停止它。
你也可以轻松地拆解一堆积木并分别测试每个积木。当你尝试理解长脚本时,这将是一个非常宝贵的策略。要移动整堆积木,抓住堆顶的积木。要从堆中间分离出一个积木以及它下面的所有积木,抓住它并拖动。试试看吧。
这个功能还允许你逐步构建项目。你可以连接一小块积木,测试它们是否按预期工作,然后将它们组合成更大的脚本。
你甚至可以将一堆积木从一个精灵复制到另一个精灵。只需将堆积木从源精灵的脚本区拖动到目标精灵的缩略图上。
尝试一下 1-6
向你的项目中添加另一个精灵。将Cat精灵的脚本从脚本区拖动并放到新精灵的缩略图上。你的鼠标箭头必须停留在新精灵的缩略图上,才能成功放置。检查新精灵的脚本标签,确保它有一个与原脚本完全相同的副本。
服装标签
你可以通过更换精灵的服装来改变它的外观,而服装本质上只是一个图像。服装标签包含了你组织精灵服装所需的一切;你可以把它当作一个衣柜。衣柜里可以有许多服装,但精灵一次只能穿一个。
现在让我们试试更换Cat精灵的服装。点击Cat精灵的缩略图并选择服装标签。如图 1-10 所示,Cat有两个服装:costume1和costume2。高亮显示的服装(此例中为costume1)表示精灵当前的服装。
图 1-10。你可以通过服装标签组织精灵的所有服装。
如果你右键点击一个服装的缩略图,你会看到一个弹出菜单,包含三个选项:(1)复制,(2)删除,以及(3)保存到本地文件。第一个选项会添加一个与您复制的服装图像完全相同的新服装。删除选项会删除选中的服装。最后一个选项允许你将服装保存到文件中。你可以通过“从文件上传服装”按钮将该服装导入并在其他项目中使用(这是图 1-10 中的第三个按钮)。试试看这些选项吧。
尝试一下 1-7
点击图 1-10 中猫咪图片上方的第一个按钮,从 Scratch 的库中选择一个新服装。然后从出现的窗口中选择你喜欢的任何图片。应用图 1-10 中的一些提示,来更熟悉服装选项。
声音标签
精灵也可以播放声音,这能为你的程序增添生气。例如,你可以为一个精灵设置不同的声音,当它高兴或伤心时播放。如果你的游戏中有一个像导弹一样的精灵,当它击中或未击中目标时,可以让导弹播放不同的声音。
声音标签中的按钮将帮助你组织精灵可以播放的不同声音。如图 1-11 所示,Scratch 甚至提供了一个工具,可以用来编辑声音文件。我在本书中不会讨论这个工具的细节,但我鼓励你自己尝试使用它。
图 1-11。声音标签可以让你组织精灵的声音。
大多数时候,你只需要使用声音标签顶部的三个按钮。它们允许你从 Scratch 的声音库中选择一个声音①,录制一个新声音②(如果你有麦克风的话),或者从你的计算机中导入一个现有的声音文件③。Scratch 只支持 MP3 和 WAV 格式的声音文件。
试试看 1-8
选择声音标签并点击从库中选择声音按钮。试听 Scratch 中提供的各种声音,以便为你未来的项目获得一些灵感。
背景标签
当你在精灵列表中选择舞台的缩略图时,中间标签的名称将从服装更改为背景。使用这个标签可以组织舞台的背景图片,你可以通过脚本来更改这些背景。例如,如果你正在创建一个游戏,你可能会在游戏开始时显示一个带有说明的背景,然后在用户开始游戏时切换到另一个背景。背景标签与服装标签是一样的。
试试看 1-9
点击精灵列表中舞台缩略图下方的从库中选择背景按钮。从出现的窗口中选择 xy 网格背景,然后点击确定。Scratch 将把 xy 网格添加到你的项目中,并设为默认背景。(xy 网格显示的是一个二维的笛卡尔平面,当你使用运动命令块时非常有用。)重复这些步骤,选择你喜欢的其他背景。
精灵信息
你可以通过点击精灵缩略图左上角的小
图标来查看精灵信息区域,如图 1-12 所示。这个区域显示了精灵的名称、当前的 (x, y) 位置和方向、旋转样式和可见状态,以及它是否可以在演示模式下被拖动。让我们简要了解一下这些选项。
图 1-12. 精灵信息区域
该区域顶部的编辑框①允许你更改精灵的名称。你将在本书中多次使用这个框。
x 和 y 值②显示精灵在舞台上的当前位置。将精灵拖到舞台上,观察这些数字发生了什么变化。
精灵的方向③表示精灵在接收到移动块时将朝哪个方向移动。拖动从圆形图标中心发出的蓝色线条来旋转精灵。
三个旋转样式按钮④(分别命名为旋转、左右翻转和不旋转)控制精灵在改变方向时服装的显示方式。为了理解这些按钮的效果,请创建图 1-13 中所示的脚本,然后在脚本运行时点击每个按钮。你可以在控制调色板中找到等待块。
“在播放器中可拖动”复选框⑤表示精灵是否可以在演示模式下被拖动(使用鼠标)。切换到演示模式并勾选/取消勾选此框,尝试将精灵拖动到舞台上,了解这个复选框的效果。
图 1-13. 用于演示旋转样式的脚本
“显示”复选框⑥允许你在程序设计时显示/隐藏精灵。试试看,观察会发生什么。你将在本书的许多例子中看到一些隐藏的精灵,它们在幕后做着有用的工作。
工具栏
让我们快速看一下图 1-14 中 Scratch 的工具栏,首先介绍一些按钮。(如果你已登录,工具栏会略有不同,具体内容请见附录 A。)使用“复制”和“删除”按钮来复制和移除精灵、服装、声音、积木或脚本。“放大”按钮可以让精灵变大,而“缩小”按钮则可以让它们变小。只需点击你想使用的按钮,然后点击精灵(或脚本)来应用该操作。要返回到箭头光标,点击屏幕上的任何空白区域。你可以使用语言菜单来更改界面的语言。
图 1-14. Scratch 的工具栏
在文件菜单中,你可以创建新的项目,上传(打开)计算机中的现有项目,下载(保存)当前项目到计算机,或者还原(撤销)当前项目的所有更改。Scratch 2 项目的文件扩展名为 .sb2,以便与在 Scratch 先前版本中创建的项目(.sb)区分开来。
在编辑菜单中,“撤销删除”会恢复你最后删除的块、脚本、精灵、服装或音效。小舞台布局选项会缩小舞台,并为脚本区域提供更多空间。选择“加速模式”可以提高某些块的执行速度。例如,在普通模式下执行 move 块 1,000 次可能需要大约 70 秒,而在加速模式下只需大约 0.2 秒。
现在你已经了解了 Scratch 工具栏的基本功能,我们将简要介绍一下 Scratch 内置的画图编辑器。
画图编辑器
你可以使用画图编辑器(图 1-15)来创建或编辑服装和背景。(当然,你也可以自由使用你喜欢的图像编辑程序。)如果你想了解更多关于 Scratch 画图编辑器的信息,可以查看 ScratchPaintEditor.pdf(位于在线资源中,可以从 nostarch.com/learnscratch/ 下载)。
图 1-15. Scratch 的画图编辑器
目前,你需要了解的两个重要功能是:设置图像的中心和设置透明颜色。我将在接下来的章节中解释这些功能。
设置图像的中心
当你指令一个精灵转动(左转或右转)时,它会围绕参考点——即其服装的中心——进行旋转。点击画图编辑器右上角的“设置服装中心”按钮可以让你选择该中心。点击此按钮后,你将在绘图区域看到十字准线,如图 1-16 所示。中心点由这两条轴的交点决定,因此,要移动服装的中心,只需将它们拖动到新的位置。要隐藏这些轴线,再次点击相同的按钮。
图 1-16. 点击“设置服装中心”按钮后更改服装中心,试一试 1-10
RotationCenter.sb2
打开 RotationCenter.sb2 并运行它。该应用包含一个精灵,其服装和脚本如下所示。服装中心设定在正方形的中心。运行脚本并注意其模式。然后编辑服装,将其中心设定在圆形的中心,再次运行脚本,看看图像如何变化。

设置透明颜色
当两幅图像重叠时,顶部的图像会覆盖底部图像的一部分。同样,精灵会覆盖舞台的一部分。如果你想看到图像背后的舞台样子,你需要使用绘图编辑器将图像的至少一部分设为透明,就像右侧的企鹅图像所示,见图 1-17。
在颜色调色板中,点击带有对角红线的方块,用“透明”颜色涂抹以使某部分变得不可见。你可以将此图标视为“无颜色”标志,类似于“禁止吸烟”标志上有一条红线穿过香烟。
图 1-17。你可以通过用“透明”颜色填充图像的某部分,使其变得透明。
现在你已经熟悉了 Scratch 界面,我们将好好利用这些知识,制作一些有趣的东西。卷起袖子,准备好:我们要做一个游戏了!
你的第一个 Scratch 游戏
Pong.sb2
Pong_NoCode.sb2
在这一节中,你将创建一个单人游戏,玩家需要移动挡板,以防止弹跳的网球撞击地面,灵感来自经典的街机游戏 Pong。我们的游戏界面如图 1-18 所示。
图 1-18。我们的游戏界面
如图所示,球从舞台顶部开始,以某个随机角度向下移动,并在舞台边缘弹跳。玩家水平移动挡板(使用鼠标)将球送回上方。如果球触及舞台底部,则游戏结束。
我们将一步步构建这个游戏,但首先我们需要打开一个新的项目。选择文件▸新建以开始一个新的 Scratch 项目。然后通过右键点击 Cat 精灵并选择弹出菜单中的删除来删除该精灵。
第一步:准备背景
为了检测球是否错过了挡板,我们将在舞台底部标记某种颜色,并使用接触颜色?积木(来自传感调色板)来判断球是否触碰到该颜色。我们当前的背景是白色,所以我们只需要在底部绘制一个细长的彩色矩形,如图 1-19 所示。
图 1-19。绘制背景图像底部矩形的步骤
点击舞台缩略图选择它,然后转到背景标签页。按照图 1-19 中的步骤,在舞台背景的底部绘制一个细长的矩形。
第 2 步:添加挡板和球
点击精灵列表上方的绘制新精灵按钮,将Paddle精灵添加到你的项目中。由于挡板只是一个细长的短矩形,重复步骤 1 中的操作,绘制一个像图 1-18 中的挡板。随意给挡板上色,并将其中心大致设置在矩形的中间。
接下来,将精灵命名为能够解释它是什么的名称;我叫它Paddle。同时,点击舞台上的挡板图像并移动它,使其y坐标大约为-120。
我们的游戏现在有了一个挡板,但我们还需要一个球来弹跳,所以点击从库中选择精灵,从精灵列表上方导入一个。在弹出的对话框中,点击物品类别,选择网球图像,将该精灵添加到项目中。将精灵重命名为Ball。
在开始为游戏编写脚本之前,选择文件 ▸ 下载到你的电脑,将目前为止的工作保存到电脑上。在弹出的对话框中,选择你想要保存工作的文件夹,命名文件为Pong.sb2,然后点击保存。如果你当前已登录,还可以将工作保存到云端(即,Scratch 服务器上)。无论你选择将文件保存在本地(电脑上)还是云端,都要记得经常保存工作。
现在,拥有Paddle和Ball精灵后,舞台应类似于图 1-18。如果此时遇到任何困难,你可以打开文件Pong_NoCode.sb2,它包含了我们刚才创建的所有内容。接下来你将为游戏添加脚本,但不用太担心区块的细节。我们稍后会在书中深入探讨这些区块,所以现在让我们专注于学习如何将一个完整的项目组合在一起。
第 3 步:开始游戏并让你的精灵动起来
作为这个游戏的设计师,你将决定玩家如何开始新的一局游戏。例如,游戏可以在按下某个键、点击舞台上的一个精灵,或者甚至拍手或挥手(如果你有摄像头)时开始。绿色旗帜图标(位于舞台上方)是另一个流行的选择,我们将在这里使用它。
这个想法很简单。任何以当点击绿旗时触发的脚本都会在你按下该按钮时开始运行。旗帜变为亮绿色,并保持这样,直到脚本完成。要看到这一过程,请为Paddle精灵创建图 1-20 中显示的脚本。
图 1-20. Paddle 精灵的脚本
当点击绿色旗帜时①,go to x: y:块②将挡板的垂直位置设置为–120,以防你之前用鼠标移动过它。挡板应该悬浮在舞台底部粉色矩形的上方,如果你的矩形较厚,可以调整其位置数字,使其适应你的设计。
脚本接着使用forever块③来不断检查鼠标位置。我们将通过将挡板的* x *位置与鼠标位置匹配,来使挡板来回移动④。运行脚本(点击绿色旗帜图标),然后尝试水平移动鼠标;挡板应该会跟随鼠标移动。点击绿色旗帜旁边的停止图标来停止脚本。
Ball精灵的脚本比之前的长一些,因此我将其分解成简单的部分。当点击绿色旗帜时,球应该开始移动,所以首先,将图 1-21 中的脚本添加到Ball精灵中。
图 1-21. Ball 精灵脚本的第一部分
首先,我们将球移到舞台顶部①,并使用pick random块②(来自Operators调色板)让它朝一个随机角度指向下方。然后,脚本使用forever块③来使球④在舞台上移动,并从边缘反弹⑤。点击绿色旗帜测试到目前为止写的部分。球应该呈之字形移动,挡板应该仍然跟随你的鼠标。
试试看 1-11
将move块中的 12 替换为不同的值,运行脚本并观察发生了什么。这应该能让你了解如何让游戏变得更容易或更难。完成后,点击停止图标。
现在是添加有趣部分的时候了——让球从挡板反弹的块。我们可以修改刚刚创建的forever块,使得球在击中挡板后朝上移动,如图 1-22 所示。
图 1-22. 添加代码以将球踢起
当球和挡板碰撞时,我们指示球朝一个–30 到 30 之间的随机方向移动。当forever块进入下一轮时,它将执行move块,这样就会使球向上移动。再次点击绿色旗帜测试游戏的这一部分。当你确认球按照预期从挡板反弹时,点击停止图标。
我们现在唯一缺少的部分是一些代码,用于在小球触碰到舞台底部时停止游戏。将图 1-23 中显示的脚本添加到Ball角色中,位置可以在图 1-22 的if代码块前面或后面。你会在感应面板中找到touching color ?代码块,并在控制面板中找到stop代码块。
图 1-23. 结束游戏的代码块
当你在touching color?代码块内点击彩色方块时,光标会变成手型光标。将光标移动并点击舞台底部的浅粉色矩形时,代码块内的彩色方块应该与矩形的颜色匹配。stop all代码块正如其名称所示:它会停止所有角色中运行的脚本,Paddle和Ball角色也不例外。
这个基本的乒乓球游戏现在已经完全功能化了。点击绿色旗帜并测试几次游戏,看看你能用这么少的代码创建一个完整的游戏。我希望你能和我一样认为 Scratch 真的很棒!
第四步:用声音增加趣味
当然,游戏有声音会更有趣,因此让我们再添加最后一个触动,每当击中小球时播放一个声音。
双击舞台上的小球以选择它,然后选择“声音”选项卡。点击从库中选择声音按钮,为Ball角色添加一个声音。在弹出的对话框中,选择效果类别,选择pop声音,点击确定将其添加到声音选项卡。之后,回到脚本选项卡,并插入一个播放声音代码块(来自声音面板),如图 1-24 所示。
图 1-24. 当小球触碰到挡板时播放声音
再次测试游戏,这一次,每当小球触碰到挡板时,你应该能听到一个短促的“啪”的声音。
恭喜!你的游戏现在已经完成(当然,如果你想添加更多功能,还是可以继续改进的),你刚刚编写了你的第一个 Scratch 程序。如果你想继续实验,可以尝试复制Ball角色,让游戏中有两个(或更多)小球,看看这会如何改变游戏玩法!
在下一节中,我将介绍 Scratch 中可用的不同类型的代码块。随着你继续阅读本书,你将深入了解这些代码块的工作原理,但现在我们只简要介绍一下它们。
Scratch 代码块:概述
在本节中,你将了解 Scratch 中不同积木的名称及其预期用途。目标是定义你将在接下来几章中阅读的一些术语。如果需要刷新记忆,你可以在进度过程中随时返回本节。
如图 1-25 所示,Scratch 有四种类型的积木:命令积木、功能积木、触发积木和控制积木。命令积木和控制积木(也叫堆叠积木)底部有凸起或顶部有凹口。你可以将这些积木拼接在一起,形成堆叠。触发积木,也叫帽子,顶部是圆形的,因为它们位于堆叠的顶部。触发积木将事件与脚本连接。当某个事件(如按下键或点击鼠标)发生时,它们会运行下方的积木。例如,所有以点击绿色旗帜时积木开始的脚本都会在用户点击绿色旗帜图标时运行。
图 1-25. Scratch 中的四种类型的积木
功能积木(也叫报告者)没有底部的凸起或顶部的凹口。它们不能单独构成脚本的一层;相反,它们作为输入提供给其他积木。这些积木的形状表示它们返回的数据类型。例如,带有圆形末端的积木报告数字或字符串,而带有尖形末端的积木则报告某事是否为真或假。如图 1-26 所示。
图 1-26. 功能积木的形状表示它返回的数据类型
一些功能积木旁边有一个复选框。如果你勾选该框,舞台上会出现一个监视器,显示当前报告者的值。选择一个精灵并勾选x 位置积木(在运动面板中)。然后将精灵拖动到舞台上,观察那个监视器。当你来回移动精灵时,监视器的值应该会发生变化。
算术运算符和函数
现在,让我们快速了解 Scratch 支持的算术运算符和函数。如果你丢了计算器,那就不必担心了!你可以使用运算符面板中的积木在 Scratch 中制作一个自己的计算器,本节将带你探索这些积木。
算术运算符
Scratch 支持四种基本的算术运算:加法(+)、减法(-)、乘法(*)和除法(/)。用于执行这些运算的块,称为 运算符,如 图 1-27 所示。由于这些块生成一个数字,你可以将它们作为输入传递给任何接受数字的块,如图中所示。
图 1-27. Scratch 中的算术运算符
Scratch 还支持取模(mod)运算符,该运算符返回两个数字相除的余数。例如,10 mod 3 返回 1,因为 10 除以 3 的余数是 1。取模运算符的常见用途是测试一个 整数(整数)是否能被另一个(较小的)整数整除。取模为 0 表示较大的数字能被较小的数字整除。这个概念是否能帮助你理解如何判断一个数字是偶数还是奇数?
Scratch 还支持另一个有用的运算符 round,它将小数数字四舍五入到最接近的整数。例如,round(3.1) = 3,round(3.5) = 4,round(3.6) = 4。
随机数
随着你编程经验的增加,你可能会需要在某些时候生成随机数,尤其是在创建游戏和模拟时。Scratch 提供了专门用于此目的的 pick random 块。
每次使用该块时,它都会输出一个随机数。它的两个可编辑的白色框允许你输入该数字的范围,Scratch 只会选择两个限制之间的值(包括两个端点)。表 1-1 显示了使用该块的一些示例。
表 1-1. 使用 pick random 块的示例
| 示例 | 可能的结果 |
|---|---|
![]() |
|
![]() |
|
![]() |
|
![]() |
|
![]() |
|
![]() |
注意
“pick random 0 到 1” 和 “pick random 0 到 1.0” 的输出是不同的。第一个情况会给你 1 或 0,而第二个则会给你一个介于 0 和 1 之间的小数。如果 pick random 块的任何输入包含小数点,输出也将是一个小数值。
数学函数
Scratch 还支持大量的数学函数。sqrt of 块将 14 个数学函数组合在一起,可以从下拉菜单中选择,包括平方根、三角函数、对数函数和指数函数。有关这些函数的详细介绍,请参考MathematicalFunctions.pdf。
总结
本章提供了 Scratch 及其编程环境的高层次概述。你了解了用户界面的各种元素,甚至创建了一个游戏!我们还探讨了 Scratch 的数学运算符和函数。
到目前为止,你已经掌握了创建强大 Scratch 脚本所需的最基本信息,但这仅仅是编写精彩程序的第一步。在接下来的章节中,你将深入挖掘如何使用 Scratch 来提升你的编程技能。
问题
| 问: | 1. 写下下面脚本中每个块的结果。这些乘积中有规律吗?!![]() |
|---|---|
| 问: | 2. 在乘积 9 × 9,99 × 99,999 × 999 等中有规律吗?使用say命令找出这些乘积的结果并检查你的答案。 |
| 问: | 3. 完成下表,写出每个表达式的值。
| 表达式 | 值 |
| --- | --- |
| 3 + (2 × 5) | |
| (10 / 2) – 3 | |
| 7 + (8 × 2) – 4 | |
| (2 + 3) × 4 | |
| 5 + (2 × (7 – 4)) | |
| (11 – 5) × (2 + 1) / 2 | |
| 5 × (5 + 4) – 2 × (1 + 3) | |
| (6 + 12) mod 4 | |
| 3 × (13 mod 3 ) | |
| 5 + (17 mod 5) – 3 | |
现在,使用say命令和适当的运算符块来检查你的答案。 |
| 问: | 4. 使用铅笔和纸评估以下 Scratch 表达式。令x = 2,y = 4。
|
| 问: | 5. 使用say命令和Operators面板中的适当块来计算以下内容:
-
32 的平方根
-
30°的正弦值
-
60°的余弦值
-
将 99.459 四舍五入的结果
|
| 问: | 6. 创建一个函数块,计算以下三个数字的平均值:90、95 和 98。使用say块显示结果。 |
|---|---|
| 问: | 7. 创建一个函数块,将 60°F 转换为摄氏度。(提示:C = (5/9) × (F – 32)。) |
| 问: | 8. 创建一个函数模块,计算一个梯形的面积,梯形的高度为 4/6 英尺,底边长度分别为 5/9 英尺和 22/9 英尺。(提示:A = 0.5 × (b[1] + b[2]) × h,其中h为高度,b[1]和b[2]为两条底边的长度。) |
| 问: | 9. 创建一个函数模块,计算加速一个质量为 2000 千克的汽车到 3 米/秒²所需的力。(提示:力 = 质量 × 加速度。) |
| 问: | 10. 电力费用为每千瓦时 0.06 美元。创建一个函数模块,计算使用一个 1500 瓦空调运作 2 小时的费用。(提示:能量 = 功率 × 时间。) |
| 问: | 11. 使用一个简单的数学技巧,你可以使用round运算符将数字四舍五入到特定的小数位。例如,你可以通过以下三步将数字 5.3567 四舍五入到最接近的十分位(即小数点右侧的第一位):
| a. | 5.3567 × 10 = 53.567 | (将数字乘以 10。)|
| b. | round(53.567) = 54 | (将步骤 a 的结果四舍五入。)|
| c. | 54/10 = 5.4 | (将步骤 b 的结果除以 10。)|
你需要对上述步骤做出哪些修改,才能将结果四舍五入到最接近的百分位(即小数点右侧第二位)?创建一个函数模块,将 5.3567 四舍五入到最接近的十分位(或百分位),并使用say模块显示其输出。|
第二章 运动与绘图
现在你已经熟悉了界面,你准备好使用更多 Scratch 的编程工具了。在本章中,你将进行以下操作:
-
探索 Scratch 的运动与画笔命令
-
动画精灵并让它们在舞台上移动
-
绘制艺术性、几何图案并创建游戏
-
学习为什么精灵克隆是一个有价值的工具
现在是时候戴上你的创意帽,跳入计算机图形的世界了!
使用运动命令
如果你想制作游戏或其他动画程序,你需要使用 运动 调色板中的积木来移动精灵。此外,你还需要命令精灵移动到舞台上的特定位置或朝某个方向转动。在本节中,你将学到如何做到这一点。
绝对运动
记住,正如你在图 1-4 为中心的坐标平面")中看到的那样,舞台就像一个 480 × 360 的矩形网格,中心是点 (0,0)。Scratch 有四个绝对运动命令(go to、glide to、set x to 和 set y to),让你可以告诉精灵在这个网格上的确切位置。
注意
如果你想了解这些和其他积木的更多细节,可以使用脚本面板右侧的 Scratch 提示窗口。如果你看不到提示窗口,只需点击 Scratch 项目编辑器右上角的问号。
为了演示这些命令,假设你想让图 2-1 中的 Rocket 精灵撞击位于 (200,150) 位置的星形 Target 精灵。最明显的方法是使用 go to 块,正如图的右侧所示。x 坐标告诉精灵在舞台上水平移动的距离,而 y 坐标则告诉它垂直移动的距离。
图 2-1. 你可以使用 go to 块将精灵移动到舞台上的任何位置。
Rocket 不会转向目标,但它会沿着一条看不见的直线从当前位置(点 (0,0))移动到点 (200,150)。你可以使用 glide to 命令让 Rocket 放慢速度。它与 go to 命令几乎相同,但它允许你设置 Rocket 到达目标所需的时间。
另一种击中目标的方法是通过 set x to 和 set y to 块独立改变 Rocket 精灵的 x 和 y 位置,如图 2-2 所示。你还记得在第一章的 Pong 游戏中如何使用 set x to 块吗?(请参阅图 1-20 复习一下。)
图 2-2。你可以独立设置精灵的* x 和 y *坐标。
你可以随时在脚本区域的右上角看到精灵当前的 x 和 y 位置。如果你想在舞台上显示这些信息,可以使用 x position 和 y position 变量块。点击这些块旁边的复选框,就可以在舞台上看到它们的值。
注意
运动命令是以精灵的中心为参考点的,你可以在绘画编辑器中设置该中心。例如,发送精灵到点 (100,100) 时,会将精灵移动到其中心位于 (100,100) 的位置,如图 2-3 所示。因此,当你为将要移动的精灵绘制或导入服装时,要特别注意它的中心位置!
图 2-3。运动命令参考精灵的中心。试试看 2-1
在执行下方脚本中的每个命令后,列出 Rocket 精灵的坐标。

相对运动
现在考虑图 2-4 中所示的网格,这里展示了另一个 Rocket 精灵和目标。此时你看不见坐标,所以不知道精灵的确切位置。如果你需要告诉 Rocket 如何击中目标,你可能会说:“移动三步,然后向右转,再移动两步。”
图 2-4。你可以使用相对运动命令在舞台上移动精灵。
像move和turn这样的命令是相对运动命令。例如,上面的第一个“move”命令使得Rocket向上移动,而第二个“move”命令则使其向右移动。运动依赖于(或相对于)精灵当前的方向。Scratch 中使用的方向约定在图 2-5 中说明。
图 2-5. 在 Scratch 中,0 是上,90 是右,180 是下,–90 是左。
你可以使用point in direction命令将精灵朝特定方向(或朝向)旋转。要选择上、右、下或左,只需点击下箭头,从下拉菜单中选择其中一个选项。对于其他方向,可以在白色编辑框中输入你想要的值。你甚至可以使用负值!(例如,输入 45 或–315 都会将精灵指向东北。)
注意
你可以在精灵信息区域找到精灵当前的方向。你也可以点击方向块旁边的复选框(在运动调色板中)来查看舞台上的方向。
现在你了解了 Scratch 中的方向是如何工作的,让我们看看相对运动命令(move、change x by、change y by和turn)是如何工作的。我们将从move和turn命令开始,它们相对于精灵当前的方向进行操作,如图 2-6 所示。
图 2-6. 一个简单的脚本,演示如何使用move和turn命令
首先,go to块①将Rocket移动到舞台中心。第二个命令块②使精灵朝上,第三个③将其顺时针旋转 45°。然后,精灵按照当前方向移动 100 步④,之后再逆时针旋转 45°⑤,停在上方的位置。
方向和服装
point in direction命令并不关心精灵的服装。例如,考虑下面显示的两个精灵。

使用绘图编辑器时,我们将小鸟的服装画成面朝右,而将昆虫的服装画成面朝上。你认为如果对每个精灵使用point in direction 90命令(即朝右),会发生什么?
你可能会猜测昆虫会转向右边,但实际上,两个角色都不会转动。尽管 90°标记为“右”,但这个方向实际上是指在绘图编辑器中的服装的原始方向。因此,因为昆虫在绘图编辑器中看起来是朝上的,所以当你让它指向 90°时,它仍然会朝上。如果你希望你的角色响应 指向方向 命令,如 图 2-5 所示,你需要在绘图编辑器中绘制角色的服装,使其面向右(如上图中的鸟的服装所示)。
有时你可能只想从当前位置水平或垂直移动角色,这时 change x by 和 change y by 块就派上用场了。图 2-7 中的脚本演示了这些块的使用。
图 2-7. 通过 change x by 和 change y by 导航一条曲折的路径。
在 Rocket 角色移动到舞台中心后,第一个 change x by 50 命令 ① 将 50 添加到其 x 坐标,使其向右移动 50 步。接下来的命令 ②,change y by 50,使 y 坐标为 50,导致角色向上移动 50 步。其他命令的工作方式也类似。尝试追踪角色的运动,见 图 2-7,找到角色的最终位置。
尝试 2-2
找出火箭执行下列两个脚本时的最终 (x, y) 位置。你可以使用什么数学定理来证明这两个脚本是等效的?

其他运动命令
还有四个运动命令需要探索:朝向;第二种类型的 go to 块;如果在边缘,反弹;以及 设置旋转样式。
TennisBallChaser.sb2
你已经了解了旋转样式,并且在 第一章 中看到了 如果在边缘,反弹 命令的实际效果(参见 图 1-13)。为了查看另外两个命令的实际效果,让我们创建一个简单的应用程序,让猫追逐网球,如 图 2-8 中所示。
图 2-8. 编程让猫追逐网球
如图所示,该应用包含两个精灵,分别名为Cat和Ball,以及两个脚本。当您点击绿色旗帜图标时,Ball精灵会跟随鼠标指针。Cat精灵不断朝向Ball并使用滑行命令向其移动。请构建这个应用并观察其工作原理。您可以在Control调色板中找到forever积木,在Sensing调色板中找到mouse x和mouse y积木。完整的应用文件可以在TennisBallChaser.sb2中找到。
在接下来的部分,我们将查看Pen调色板,并学习如何让精灵留下其运动的视觉轨迹。
画笔命令与 Easy Draw
EasyDraw.sb2
您在上一部分中使用的运动命令可以将精灵移动到舞台上的任何位置。现在,是时候看到精灵实际走过的路径了吧?Scratch 的画笔可以帮忙。
每个精灵都有一支隐形的画笔,可以设置为上或下。如果画笔在下,精灵移动时会留下轨迹。否则,精灵会移动但不留下任何痕迹。Pen调色板中的命令允许您控制画笔的大小、颜色和阴影。
尝试 2-3
打开 Scratch 中的提示窗口,点击房子图标,然后点击Pen以简要查看每个画笔命令的描述。下面的脚本演示了大多数这些命令。重新创建这些脚本,运行它们,并描述每个脚本的输出。在运行这些脚本之前,别忘了将精灵的画笔设置为下。(您可以在Control调色板中找到repeat积木。)

让我们详细探索一些画笔命令,并通过按箭头键在舞台上移动和旋转精灵,创建一个简单的绘图程序。按一下上箭头(↑)会让精灵向前移动 10 步。按下箭头(↓)会让精灵向后移动 10 步。每按一次右箭头(→),精灵会向右转动 10°,每按一次左箭头(←),精灵会向左转动 10°。例如,若要让精灵转动 90°,如图 2-9 所示,您需要按左箭头或右箭头九次。
首先,启动一个新的 Scratch 项目。将Cat的服装更换为能清晰显示精灵指向左、右、上或下的图案。beetle或cat2(来自动物文件夹)是不错的选择,但也可以选择其他任何您喜欢的服装。在Costumes标签页中,点击从库中选择服装按钮,选择合适的服装。
图 2-9. Easy Draw 应用实例
现在,将图 2-10 中显示的脚本添加到你的角色中。你可以从事件调色板中的按键按下块创建四个当按下空格键的块。只需点击向下箭头,选择所需的箭头键。
图 2-10。Easy Draw 应用程序的脚本
当你点击绿色旗帜时,角色将移动到舞台的中心①并指向上方②。然后,笔的颜色③和大小④将被设置,脚本将把笔放下⑤为绘图做好准备。之后,程序将清除舞台上所有先前的绘图⑥。
要清除舞台并开始新的绘图,你只需点击绿色旗帜。使用键盘箭头键绘制任何你喜欢的形状。你认为顺序↑→↑→ ↑→...会绘制出什么形状?
尝试一下 2-4
添加一个选项,当按下字母W时,使绘图笔变宽;当按下字母N时,使绘图笔变窄。想一想其他可能改进应用程序的方式,并尝试实现它们。
重复的力量
到目前为止,我们的程序相对简单,但随着你开始编写更长的脚本,你会经常需要多次连续复制同一组积木。重复脚本会使你的程序变得更长、更难理解,并且更难进行实验。例如,如果你需要更改一个数字,你将不得不在每个脚本副本中进行相同的更改。控制调色板中的重复命令可以帮助你避免这个问题。
DrawSquare.sb2
例如,假设你想绘制图 2-11(左)所示的方形。你可以指示角色按照这些重复的指令执行:
-
移动一定距离并逆时针旋转 90°。
-
移动相同的距离并逆时针旋转 90°。
-
移动相同的距离并逆时针旋转 90°。
-
移动相同的距离并逆时针旋转 90°。
图 2-11。一个方形(左)和一个用移动和旋转命令绘制它的脚本(右)
图 2-11 还展示了一个实现这些指令的脚本。注意,它重复了命令 移动 100 步 和 转向 90 度 四次。相比之下,我们可以通过 repeat 块来避免重复使用相同的两个块,repeat 块会按你指定的次数执行其中的命令,如 图 2-12 所示。使用 repeat 块还可以让指令变得更容易理解。
图 2-12. 使用 repeat 块绘制正方形
你用 图 2-11 中的脚本绘制的正方形,取决于精灵开始时面对的方向。这个概念在 图 2-13 中有所说明。注意,在绘制完正方形后,精灵会回到起点并面朝它开始移动时的方向。
图 2-13. 精灵的初始方向改变了正方形的位置。尝试 2-5
Polygon.sb2
你可以轻松修改 图 2-12 中的正方形绘制脚本来绘制其他规则的多边形。修改后的脚本如下所示。你可以为“边数”替换任何整数来指定所需的多边形,并为“边长”指定任何值来控制多边形的大小。图中还展示了使用此脚本绘制的六个相同边长的多边形。精灵从图中绿色箭头所示的位置和方向开始。打开 Polygon.sb2 文件,并使用不同的“边数”值运行它。当这个数字变得很大时会发生什么?这应该能给你一个绘制圆形的思路。

旋转的正方形
RotatedSquares.sb2
你可以通过按一定的顺序重复图案来创造惊人的艺术。例如,图 2-14 中展示的脚本通过旋转并绘制正方形 12 次,创造了一个吸引人的图案。(为了简洁,初始化笔并放下笔的块未展示。)
图 2-14. 绘制旋转的正方形
外部repeat积木①执行 12 次。每次在循环中,它绘制一个正方形②,然后左转 30°③,为绘制下一个正方形做准备。
试一试 2-6
请注意,(12 次重复)×(每次重复 30°)= 360°。如果你将程序中的数字改为 4 次重复和 90°,你觉得会发生什么?如果是 5 次和 72°呢?试着改变repeat的重复次数和turn的角度,看看会发生什么。
使用印章探索
Windmill.sb2
在上一节中,你学会了使用turn和repeat积木将简单形状转化为复杂图案。但是如果你想旋转更具挑战性的形状怎么办?你可以不使用move和turn命令绘制基本形状,而是在画图编辑器中创建一个新服装,并使用stamp积木在舞台上绘制多个副本。为了说明这种技术,让我们编写一个程序来绘制图 2-15 所示的风车。
图 2-15. 印章命令让你轻松创建复杂的几何图案。
我们使用画图编辑器绘制了旗帜形状(见图 2-15,左侧),并将其作为我们角色的服装。我们将服装的中心设置在旗帜的下尖端,这样可以围绕这个点旋转旗帜。
绘制风车的脚本如图 2-15(中间)所示。repeat积木执行八次;每次,它会在舞台上印刷一个服装副本,然后将角色旋转 45°向左。请注意,为了使此脚本正常工作,必须使用设置旋转样式积木,并将角色的旋转样式设置为all around,以便旗帜在旋转时能够翻转。
注意
《DrawingGeometricShapes.pdf》在额外资源包中(你可以从 nostarch.com/learnscratch/ 下载)详细介绍了绘制几何图形,如矩形、平行四边形、菱形、梯形、风筝形和多边形,并教你如何创建吸引人的多边形艺术。
试一试 2-7
改变颜色效果块(来自外观面板)允许你应用图形效果,如颜色、旋转和鱼眼效果。打开文件Windmill.sb2,并将此命令添加到重复块中。尝试其他图形效果,制作更多酷炫的图案。请注意,为了让改变颜色效果块生效,绘图编辑器中的旗帜颜色不能是黑色。
Scratch 项目
在本节中,我们将开发两个简短的程序,进一步帮助你理解到目前为止学到的运动和笔迹块。你可以在本章的项目文件中找到背景和角色,因此我们将专注于编写使这些应用程序正常工作的脚本。一个名为“生存跳跃”的额外奖励游戏的解释可以在额外资源中找到。详细信息请参阅BonusApplications.pdf(nostarch.com/learnscratch/)。
其中一些脚本将使用你尚未见过的命令块,但如果你不完全理解其中的某些内容,也不必担心。你将在接下来的章节中学习到所有相关内容。
获取金币
Money_NoCode.sb2
我们的第一个应用是一个简单的游戏,玩家需要使用键盘箭头移动角色,尽可能多地收集金币袋。如图 2-16 所示,金币袋会在网格上的随机位置出现。如果玩家在三秒钟内没有抓到金币袋,它会移动到其他地方。
图 2-16. 帮助猫抓取尽可能多的金币袋!
打开文件Money_NoCode.sb2。该文件缺少脚本,但你现在将创建它们,文件中包含你所需的其他一切。
注意
图 2-16 中显示的坐标轴是为了帮助你理解这些脚本中使用的数字。如有需要,可以返回此图,刷新你对角色如何移动的理解。
我们首先编写Player角色的脚本,如图 2-17 所示。
图 2-17. Player角色的脚本
当玩家点击绿色旗帜时,这个精灵会移动到 (–30,–30) ① 并朝右指向 ②。其他四个脚本响应方向键。当按下一个方向键时,相应的脚本会改变精灵的方向 ③,播放一个短音效(使用 声音 面板中的 播放声音 块 ④),并移动精灵 60 步 ⑤。如果需要,精灵会从舞台边缘反弹 ⑥。由于 60 步对应于图 2-16 中网格上的 1 个方格,因此每次按下方向键时,Player 精灵会移动 1 个方格。
注释
你是否注意到在图 2-17 中,四个处理箭头的脚本几乎是完全相同的?在第四章中,你将学习如何避免这样重复代码。
继续测试游戏的这一部分。你应该能够通过键盘上的方向键移动 Player 精灵在舞台上。当你完成这部分功能后,我们将继续处理 Gold 精灵,其脚本见图 2-18。
图 2-18. Gold 精灵的脚本
与 Player 精灵的脚本类似,这个脚本也会在点击绿色旗帜时启动。它会移动金币袋。它还会通过名为 score 的变量来跟踪已经收集到的金币袋数量,我已经在 数据 面板中为你创建了这个变量。
注释
像 score 这样的标签被称为 变量。它们让我们能够保存信息,以便在程序中稍后使用。你将在第五章中学习所有关于变量的知识。
由于游戏刚开始,我们还没有收集到任何金币袋,所以我们将 score 设置为 0 ①。接下来,我们启动一个循环,该循环会重复 20 次 ②,向玩家展示总共 20 个金币袋。(如果你不想要 20 个金币袋,可以随意选择你喜欢的数字。)每次循环运行时,金币袋将出现在某个随机位置 ③,给玩家一些时间来抓取它 ④,如果玩家成功抓取,则增加 score ⑤。
我们需要让金币袋随机出现在舞台上的 48 个方格之一。正如你在图 2-16 中看到的,金币袋的 x 位置可以是以下任意值:–210、–150、–90、...、210。这些数字间隔为 60 步,因此你可以通过计算从 –210 开始找到每个 x 位置:
| x = –210 + (0 × 60) |
|---|
| x = –210 + (1 × 60) |
| x = –210 + (2 × 60) |
| x = –210 + (3 × 60) |
依此类推。类似的表达式适用于 y 位置。
我们可以通过生成一个 0 到 7 之间的随机数,将其乘以 60,并将结果加上-210 来设置袋子的x位置。图 2-19 展示了在我们的脚本中创建set x to块的详细步骤;set y to块的构建方式也类似。
图 2-19. 从图 2-18 构建set x to块
在随机位置出现后,金袋子会给玩家三秒钟的时间来抓住它。(你可以更改这个时长,让游戏变得更难或更容易。)为了跟踪时间,脚本首先将 Scratch 内置的计时器重置为 0。然后它会等待,直到玩家通过触摸袋子抓住它,或者计时器超过三秒。当任一条件发生时,wait until块将让脚本继续执行if/then块。图 2-20 展示了创建wait until块的细节。
图 2-20. 在图 2-18 的脚本中构建wait until块
注意
if/then *块中的代码只有在你在if/then 块的头部指定的条件为真时才会运行。第六章详细解释了这个块,但现在,你已经足够了解如何使用它来为程序增添个人特色。
如果玩家触摸到袋子,if/then块中的命令将会执行。在这种情况下,play sound块将播放WaterDrop声音,而change score by 1块(在数据调色板中)将分数加 1。
游戏现在完成了。点击绿旗测试你的创作!
Scratch 的计时器
Scratch 保持一个计时器,记录自 Scratch 启动以来已经过去的时间。当你在 Web 浏览器中启动 Scratch 时,计时器将被设置为 0,并且只要你保持 Scratch 打开,它会按十分之一秒为单位递增。timer块(在感应调色板中)保存计时器的当前值。块旁边的复选框允许你在舞台上显示/隐藏该块的监视器。reset timer块将计时器重置为 0,并且时间会立即重新开始计数。即使项目停止运行,计时器仍会继续运行。
捕捉苹果
CatchApples_NoCode.sb2
请参考图 2-21 中的抓苹果游戏。在这个游戏中,苹果会在舞台顶部的随机水平位置、随机时间出现并掉落到地面。玩家必须移动购物车来抓住苹果,防止它们掉到地面,每个苹果值 1 分。
图 2-21。抓苹果游戏
一开始,你可能会认为这样的游戏需要许多精灵,且每个精灵的脚本几乎一样。毕竟苹果很多。然而,自 Scratch 2 版本起,情况发生了变化。借助克隆功能,你可以轻松创建多个精灵的副本。在我们的抓苹果游戏中,我们只使用一个苹果精灵,并可以创建任意数量的克隆。
打开文件CatchApples_NoCode.sb2,该文件包含了没有脚本的游戏设置。为了让游戏更有趣,设置中还包含了一个名为score的变量(在数据调色板中为你创建),我们将用它来跟踪抓到的苹果数量。但首先,你将为购物车精灵创建如图 2-22 所示的脚本。
图 2-22。购物车精灵的脚本
当点击绿旗时,我们将购物车移到舞台底部中央。接下来,脚本会持续检查左右箭头的状态,并相应地移动购物车。我选择了数字 30 是通过试验得出的,所以你可以根据自己的实验结果自由更改。
接下来是克隆的部分。首先将图 2-23 中的脚本添加到苹果精灵中。这个脚本也会在点击绿旗时开始运行。
图 2-23。苹果精灵的第一个脚本
由于我们还没有抓到任何苹果,脚本将score变量设置为 0 ①。接着,它使用外观调色板中的显示块②使精灵可见。然后,它开始一个重复块,该块会循环 30 次③,让 30 个苹果掉落。
在每次循环过程中,苹果精灵将移到舞台上方的随机水平位置④。接着,它会调用控制调色板中的创建克隆块⑤,克隆自身,等待一段短暂的随机时间⑥,并开始下一轮重复块。完成 30 轮重复块后,脚本通过外观调色板中的隐藏块⑦将苹果精灵隐藏。
如果你现在点击绿色旗帜运行游戏,30 个苹果会随机出现在舞台的顶部,并停留在那里——因为我们还没有告诉克隆的苹果该做什么。这时,Apple 精灵的下一个脚本(图 2-24)就派上用场了。
图 2-24. 第二个Apple精灵的脚本
由于当我作为克隆启动时模块①(来自控制面板),每个克隆都会执行图中显示的脚本。每个Apple精灵向下移动 10 步②,并检查它是否被小车抓住或掉落。如果克隆检测到它碰到了小车③,这意味着它被抓住了。因此,它会增加分数,播放音效,并删除自己(因为它不再有任务)。如果克隆掉落到小车下方④,则玩家错过了;在这种情况下,克隆会播放另一种音效,然后删除自己。如果克隆既没有被抓住也没有错过,它仍在下落中,永远模块再次循环执行。
现在我们的苹果已经知道如何掉落了,游戏完成了!点击绿色旗帜测试一下吧。如果你想进行实验,可以尝试改变克隆不同苹果之间的等待时间以及移动小车的速度。这样做会给你一些调整游戏难度的灵感吗?
关于克隆精灵的更多信息
任何精灵都可以使用创建克隆模块复制自己或其他精灵。(舞台也可以使用相同的模块克隆精灵。)克隆的精灵会继承原始精灵在被克隆时的状态——即原始精灵的当前位置和方向、服装、可见状态、画笔颜色、画笔大小、图形效果等。这个概念在图 2-25 中得到了说明。
图 2-25. 克隆继承了主精灵的属性。
克隆也会继承主精灵的脚本,如图 2-26 所示。这里,主精灵创建了两个克隆。当你按下空格键时,所有三个精灵(主精灵和两个克隆)都会向右转 15°,因为它们都会执行当空格键按下时的脚本。
图 2-26. 克隆继承了主精灵的脚本。
在使用 创建克隆 块时,特别需要注意,如果脚本没有以绿色旗帜触发器开始,你可能会得到比预期更多的精灵。考虑下图中显示的程序 图 2-27。第一次按下空格键时,会创建一个克隆,应用程序中会有两个精灵(主精灵和克隆精灵)。
图 2-27. 按键响应创建克隆
现在,如果你第二次按下空格键,你的应用程序中将有四个精灵。为什么?主精灵会响应按键并创建一个克隆,但第一个克隆也会响应并创建另一个克隆(即克隆的克隆)。第三次按下空格键时,你的应用程序中将有八个精灵。克隆的数量将呈指数增长!
你可以通过仅在以 当绿色旗帜点击时 块开始的脚本中克隆精灵来解决这个问题。这些脚本仅由主精灵执行。
总结
在本章中,你学习了如何使用绝对运动命令将精灵移动到舞台上的特定位置。然后,你使用相对运动命令参考精灵自己的位置和方向移动精灵。之后,你使用画笔命令创建了一些精美的图形。
当你绘制不同的图形时,你发现了 重复 块的强大功能,它让你能够创建更简短、更高效的脚本。你还学习了 印章 命令,并与 重复 块结合使用,轻松设计复杂的图案。
在本章的最后,你创建了两个游戏,并了解了 Scratch 的克隆功能。在下一章,你将使用 外观 和 声音 调色板创建更具吸引力的程序。
问题
| 问: | 1. 解释以下脚本是如何工作的。写出图形所有角落的 (x,y) 坐标。![]() |
|---|
| 问: | 2. 编写一个脚本,按顺序连接下列各组点,并展示最终形状:
-
(30,20), (80,20), (80,30), (90,30), (90,80), (80,80), (80,90), (30,90), (30,80), (20,80), (20,30), (30,30), (30,20)
-
(–10,10), (–30,10), (–30,70), (–70,70), (–70,30), (–60,30), (–60,60), (–40,60), (–40,10), (–90,10), (–90,90), (–10,90), (–10,10)
|
| 问: | 3. 编写一个脚本,绘制下列图案。![]() |
|---|---|
| 问: | 4. 请考虑以下脚本及其输出。重新创建该脚本,添加必要的画笔设置命令,运行并解释其工作原理。![]() |
| 问: | 5. 请考虑以下脚本及其输出。重新创建该脚本,添加必要的笔设置命令,运行并解释它是如何工作的。![]() |
| 问: | 6. 请考虑以下脚本及其输出。重新创建该脚本,添加必要的笔命令,运行并解释它是如何工作的。![]() |
| 问: | 7. 创建如下所示的脚本,添加必要的笔命令并运行它。解释这个脚本是如何工作的。![]() |
| 问: | 8. 编写一个程序,生成如下所示的输出。![]() |
| 问: | 9. 在这个问题中,你将编写完成如下“气球爆破”游戏所需的脚本。BalloonBlast_NoCode.sb2 这个游戏包含两个精灵,分别名为Balloon和Rocket。当你点击绿色旗帜时,Balloon精灵会在上面的界面中创建五个克隆。Rocket精灵会自动左右移动,碰到舞台边缘时会反弹。你需要在合适的时机按下空格键发射火箭并爆破气球。打开文件BalloonBlast_NoCode.sb2。该文件包含在游戏开始时创建五个克隆的代码。你的任务是通过添加以下两个脚本来完成游戏。![]() |
第三章 外观与声音
在上一章,你学会了如何使用运动命令让精灵在舞台上移动,以及如何使用画笔命令绘制图案。在本章,你将学习外观和声音面板中的各种命令。在这个过程中,你将完成以下内容:
-
创建动画和图像效果
-
学习 Scratch 中的图层如何工作
-
播放音频文件并编排音乐
-
自己制作完整的动画场景
外观面板中的命令将帮助你创建动画,并为造型和背景应用图形效果,如旋转、鱼眼、鬼影等。声音面板中的命令在你需要为应用添加声音、配音或音乐时非常有用。让我们从一些动画开始吧!
外观面板
你可以使用画笔命令直接在舞台上绘制图像,但造型提供了另一种强大且有时更简单的方法,来为程序添加图形。外观面板中的命令将帮助你操控造型来创建动画、添加思维气泡、应用图形效果,并改变精灵的可见性。我们将在本节中探讨这些命令块。
改变造型以实现动画效果
Animation.sb2
你知道如何让精灵从舞台上的一个点移动到另一个点,但静态的精灵跳动时并不像活生生的样子。如果你使用不同的造型并快速切换它们,你可以让精灵看起来像是真的在移动!打开文件Animation.sb2,在图 3-1 中试试这个动画效果。
图 3-1. 你可以通过在不同造型之间切换,创造动画的错觉。
这个应用包含一个精灵和七个造型,还有一个脚本。你可以在造型标签中看到七个造型,在脚本标签中看到精灵的脚本。当你点击绿色旗帜运行应用时,纸人会看起来在舞台上行走。它运动的关键是下一个造型命令,它告诉精灵切换到列表中的下一个造型。如果精灵穿着列表中的最后一个造型,它会回到第一个造型。
当点击绿色旗帜时,脚本会启动一个永久循环,并在末尾添加一个等待块,用来在每次更换造型后创建 0.1 秒的延迟。如果你从脚本中移除这个延迟,纸人看起来就会跑步而不是走路。尝试调整移动和等待块的不同数值,看看它们如何影响动画效果。
尽管你可以使用画笔命令绘制这个走路的小人,但你需要编写一个长脚本。另一方面,一旦绘制了这些服装,编程动画就变得轻松了。记住,你可以使用你喜欢的绘画程序,或使用 Scratch 的画图编辑器创建图像。
ClickOnFace.sb2
如果你希望用户与精灵互动,可以在鼠标点击时更改其服装,如“点击面孔”应用所示。此应用包含一个名为 Face 的精灵,具有图 3-2 中显示的五种服装。它使用 when this sprite clicked 块(来自 事件 调色板)来告诉精灵何时切换服装。
图 3-2. 每当精灵被点击时,笑脸和背景都会发生变化。
当你运行此应用时,每次点击面部图像时,图像会变为列表中的下一个。该脚本还使用 switch backdrop to 块来命令舞台随机切换到其中一个四种背景图像。当舞台切换到 Stage4 图像时,Face 精灵检测到此事件(使用 when backdrop switches to 触发块,来自 事件 调色板)。在这种情况下,面部图像会移动到舞台的右上角,然后返回中心。
试试看 3-1
TrafficLight.sb2
文件 TrafficLight.sb2 包含一个精灵,具有三种服装(分别命名为 red、orange 和 green)以及一个不完整的脚本,如下所示。通过添加必要的 wait 块来完成该应用,从而创建一个逼真的交通灯动画。

注意
你可以使用 switch backdrop to 命令来更改故事中的场景、游戏中的关卡等。你项目中的任何精灵都可以使用 when backdrop switches to 块来检测舞台何时切换到某个背景,并相应地做出反应。有关更多详细信息,请参见 Scratch 界面中的提示窗口。
说话和思考的精灵
你可以使用 say 和 think 命令,让你的精灵像漫画中的角色一样说话或思考,如图 3-3(左侧)所示。
图 3-3. 使用 say 或 think 命令在对话框或思考框中显示消息。
你在这些命令中输入的任何短语都会出现在精灵上方,且消息会永久显示。如果你想清除消息,使用没有文本的 说 或 思考 块。你也可以使用 说(秒)(或 思考(秒))命令来在固定时间内显示消息,如 图 3-3(右图)所示。
尝试 3-2
Argue.sb2
要查看说话和思考命令的效果,打开文件 Argue.sb2 并运行它。这个应用模拟了两个角色之间的无休止争论,如下所示。研究脚本,了解它们如何使用精确的时机来同步两个角色的动作。

图像效果
GraphicEffects.sb2
设置效果为 命令允许你对服装和背景应用不同的图形效果。Scratch 给这些效果命名,如鱼眼、旋涡、马赛克等。图 3-4 精确展示了它们的效果。
图 3-4。此图展示了当你应用 Scratch 的图形效果时猫咪会发生什么。
点击 设置效果为 块中的下箭头,从下拉菜单中选择你想要的效果。你也可以使用 通过改变效果 命令来调整效果,而不是直接设置它。例如,如果当前的幽灵效果设置为 40,通过改变 60 会将幽灵效果设置为 100,导致精灵消失(像幽灵一样)。当你想让图像恢复到原始状态时,使用 清除图形效果 块。
注意
你可以通过按顺序使用多个图形效果命令,同时对精灵应用多个效果。
大小和可见性
SneezingCat.sb2
有时你可能需要改变精灵的大小或控制它在程序中何时出现。例如,你可能希望场景中的近物看起来更大,或者你可能只希望在游戏开始时显示一个“指令”精灵。
如果你需要缩小或放大精灵,设置大小为 % 和 改变大小为 命令可以帮忙。第一个命令将精灵的大小设置为原始大小的百分比,第二个命令相对于当前大小修改精灵的大小。当你需要让精灵出现或消失时,分别使用 显示 块或 隐藏 块。
要查看这些命令的实际效果,请打开 SneezingCat.sb2。在这个应用中,我们将通过改变猫的大小,让它像卡通人物一样打喷嚏,如 图 3-5 所示。
图 3-5. 这个脚本让 Cat 精灵打喷嚏。
当精灵准备打喷嚏时,大小会增大,喷嚏打完后,它会慢慢恢复到原来的大小。运行程序并观察发生了什么,体验这些命令的效果。
试试 3-3
在 图 3-5 中,将一个积木添加到脚本的末尾,使 Cat 精灵在打完喷嚏后消失。再添加一个积木,在脚本开始时显示这个精灵。
层
Looks 调色板中的最后两个命令影响精灵在舞台上绘制的顺序。这个顺序决定了当精灵重叠时哪些精灵是可见的。例如,假设你想创建一个女孩站在大岩石后面的场景。有两种层叠的可能性,如 图 3-6(左图)所示。
图 3-6. 前层的精灵完全可见,并且可以覆盖部分重叠的精灵。
如果你想让女孩站在岩石后面,你必须把岩石移到前面的绘图层,或者把女孩发送到后面的绘图层。Scratch 提供了两个命令,可以让你重新排列绘图层,go to front 和 go back layers(如图所示)。第一个命令让 Scratch 始终将一个精灵绘制在最上面,而第二个命令则将精灵按你指定的层数向后移动。
试试 3-4
Layers.sb2
Layers.sb2 应用中有四个在舞台上移动的物体。你可以通过按下物体颜色的首字母将其移动到最上面。运行应用程序,查看 go to front 命令的效果。
我们已经介绍了使用 Looks 调色板进行动画,但还有另一种可以让我们的应用程序更加生动的方式。在下一节中,我们将探索 Sound 调色板及其丰富的命令集。
声音调色板
游戏和其他应用通过音效和背景音乐来增加兴奋感。在本节中,你将学习如何使用 Scratch 的与声音相关的积木,首先是如何加入音频文件并控制它们的播放。接着,你将学习演奏鼓和其他乐器的命令积木。之后,你会学习如何控制音量并改变音乐音符和鼓点的播放速度(或节奏)。
播放音频文件
你可以将音频文件以多种格式保存在计算机上,但 Scratch 只识别两种格式:WAV 和 MP3。有三个命令积木可以让你在应用中使用这些音频文件:播放声音、播放声音直到完成和停止所有声音。前两个积木都用于播放指定的声音。播放声音命令允许下一个命令在声音未播放完时就开始,而播放声音直到完成命令则会等待声音播放结束后才会继续执行下一个命令。停止所有声音命令会立即关闭正在播放的所有声音。
你可以通过反复播放音频文件来为你的应用添加背景音乐。最简单的方式是使用播放声音直到完成,让文件完全播放完毕,然后重新开始,如图 3-7(左)所示。
图 3-7. 创建背景音乐的两种方式:播放完后重复声音(左)或在播放一定时间后重新开始声音(右)。
根据音频文件的不同,这种方法可能会在连续重启之间产生一个非常短暂但有时会注意到的暂停。你也可以使用播放声音命令配合等待命令,这样可以更好地控制播放时长,如图 3-7(右)所示。通过调整等待时间,你或许能缩短暂停时间,从而使当前播放结束和下一次播放开始之间的过渡更加平滑。
演奏鼓声和其他声音
BeatsDemo.sb2
如果你在开发游戏,可能希望在玩家击中目标、完成关卡等时播放一个短暂的音效。你可以使用play drum for beats命令轻松创建这些音效,它可以根据你选择的 18 种鼓声之一播放指定数量的拍子。你还可以使用rest for beats命令添加暂停。图 Figure 3-8 中的BeatsDemo.sb2应用演示了 beats 参数的效果。
图 3-8. Scratch 中的 beats 示意图
该脚本包含三个repeat块,重复次数分别为二、四和八。每个repeat块使用不同数量的拍子演奏相同的鼓声。如果把时间轴看作被划分为 0.2 单位的时间间隔,第一个循环将播放两个鼓声,它们之间相隔 0.8 单位的时间。第二个循环将播放四个鼓声,它们之间相隔 0.4 单位的时间,而第三个循环则播放八个鼓声,它们之间相隔 0.2 单位的时间。每个循环完成的时间是相同的;我们只是在同样的时间间隔内敲打鼓的次数不同。
我说“时间单位”而不是秒,因为完成每个循环的实际时间取决于tempo,这个值可以通过set tempo to命令来设置。使用默认的 60 拍每分钟(bpm)速度,上述示例中的每个循环将花费 1.6 秒完成。如果将速度设置为 120 bpm,每个循环将花费 0.8 秒完成;如果设置为 30 bpm,则每个循环花费 3.2 秒,以此类推。
作曲
FrereJacques.sb2
Scratch 还包含两个命令,可以让你演奏音符并创作自己的音乐。play note for beats命令会播放你选择的音符,范围从 0 到 127,并持续你指定的拍子数。set instrument to块告诉 Scratch 音符应该像什么样的乐器。我们可以使用这些命令创作一首完整的歌曲。图 Figure 3-9 中的脚本演奏了法国儿童歌曲“Frère Jacques”。
图 3-9. 演奏“Frère Jacques”的脚本
打开这个名为FrereJacques.sb2的应用程序,并尝试不同的set instrument to命令值,以更改演奏这首歌曲的乐器。
控制音量
假设你希望在应用程序中的某些事件发生时让声音逐渐减弱。例如,如果你正在发射一枚火箭进入太空,你可能希望火箭起飞时声音很响,然后随着它越来越远,声音变得越来越小。
Scratch 包含了一组命令来控制音频文件、鼓声和音符的音量或响度。设置音量为%命令将精灵的响度设置为扬声器音量的百分比。然而,它只影响使用它的精灵(或舞台),所以如果你想让不同音量的声音同时播放,你需要使用多个精灵。更改音量为块会根据你输入的数字增加或减少音量。负数会让声音更轻,正数则会让声音更响。你甚至可以通过勾选音量块旁边的框来显示精灵的音量。这些块非常方便,如果你想根据精灵与目标的距离来改变音量(例如在寻宝游戏中),或者让歌曲的某些部分比其他部分更响亮。你还可以使用这些块通过同时播放不同音量的不同乐器来模拟一个交响乐团。
试一试 3-5
VolumeDemo.sb2
文件VolumeDemo.sb2模拟了一只猫走进森林的情景。这个应用程序使用更改音量为命令来让猫的声音在它走得更深进入树林时逐渐变小。想出一些方法让这个模拟更加真实,并尝试实现它们。
设置节奏
声音面板中的最后三个块与节奏或鼓和音符的演奏速度相关。节奏以每分钟拍数(bpm)来衡量。节奏越高,音符和鼓声的播放速度越快。
Scratch 允许你使用设置节奏为 bpm命令选择特定的节奏。你也可以使用更改节奏为命令来让精灵以某个特定的速率加速或减速。如果你想在舞台上看到精灵的节奏,可以勾选节奏块旁边的框。
试一试 3-6
TempoDemo.sb2
打开文件TempoDemo.sb2并运行它,观察设置节奏为 bpm和更改节奏为命令的效果。
Scratch 项目
外观和声音面板中的命令将帮助你为应用程序添加许多有趣的效果。在这一部分,我们将把本章到目前为止学到的所有内容结合起来,创建一个包含跳舞人物和烟花的动画场景。这将帮助你复习一些新的命令块,并让你有更多的机会练习创建一个完整的 Scratch 项目。
舞台上的舞蹈
DanceOnStage.sb2
在这一部分,你将为舞台上的舞者精灵制作动画。这个应用程序在图 3-10 中进行了说明,完整的脚本保存在DanceOnStage.sb2中。我们将在这里一起构建整个场景——跟着一起做,看看它是如何工作的!
图 3-10。舞会应用程序演示。
首先,启动一个新项目。如果 Scratch 尚未运行,只需启动它——这会自动为你创建一个新项目。否则,从文件菜单中选择新建。无论哪种情况,你都会有一个包含默认Cat精灵的新项目。
你将在此应用程序中使用的背景是来自室内类别的party room。导入这个背景并删除默认的白色背景,因为你不需要它。现在舞台应该看起来像图 3-11。
图 3-11。我们稍后会将派对房间背景的一些部分转换成精灵。
仔细查看图 3-10 和图 3-11,注意Ball和Board精灵看起来像是背景的一部分。正如你接下来会看到的,这两个精灵实际上是从那张图像中创建出来的,并被放置在舞台上以覆盖它们所来自的区域。通过这种方式创建这两个精灵,让我们可以改变它们的颜色,使舞台看起来更真实。
现在我们需要一些背景音乐。让我们使用Music Loops类别中的medieval1文件。将该文件导入舞台,然后删除默认的“pop”音效。接着,将图 3-12 中的脚本添加到舞台。它使用播放声音命令并加上一个等待时间,使音频文件可以平滑地重新启动。9.5 秒的等待时间是通过实验得出的。
图 3-12。舞台播放我们的背景音乐。
点击绿色旗帜测试你目前创建的内容。你应该听到一个音频片段不断重复播放。准备好后停止脚本,我们将添加我们的舞者。
将Cat精灵的服装替换为Dancer的服装。从People类别中导入dan-a和dan-b服装,删除两个Cat服装,并将Cat精灵的名称改为Dancer。Dancer的脚本如图 3-13 所示。
图 3-13。这个脚本告诉Dancer精灵如何跳舞。
Dancer精灵向右移动 20 步,改变服装,向左移动 20 步,再次改变服装。这些步骤将永远重复,模拟他在跳舞。该脚本还会在每一步改变一些鱼眼效果,增加一些变化。点击绿色旗帜测试程序的新功能。你应该能听到背景音乐,并看到Dancer精灵在舞台上左右移动。
现在你已经为派对准备好了一个舞者,接下来让我们通过Ball、Board和SpotLight精灵加入一些彩色灯光。为了创建Ball精灵,点击舞台缩略图选择它,然后选择背景标签。右键点击party room背景的缩略图,并从弹出菜单中选择保存到本地文件。这会弹出一个对话框,允许你将背景图像保存到本地。记住你保存图像的位置,因为稍后你需要导入它。
点击从文件上传精灵按钮(位于精灵列表上方),然后选择你刚刚保存的图像。这会创建一个新精灵,服装与背景图像相同。将该精灵命名为Ball,并在绘图编辑器中编辑其服装,去除除图 3-14(左侧)所描绘的五彩球以外的所有内容。确保将球周围的空间涂上透明颜色。接下来,将Ball精灵放置在舞台上,准确地覆盖背景中你取图的地方,这样它看起来就像是图像的一部分(参见图 3-11)。
图 3-14。Ball精灵在绘图编辑器中的服装及其脚本
图 3-14 还展示了你应该为Ball精灵添加的脚本。该脚本持续改变精灵的颜色效果,给人一种小圆圈不断变色的错觉。
创建Board精灵的方式与创建Ball精灵相同。图 3-15 展示了该精灵在绘图编辑器中的样子(左侧)以及你需要的动画脚本(右侧)。我为服装添加了一些颜色(与图 3-11)进行对比),以使改变颜色效果命令生效。
图 3-15。Board 精灵及其脚本
因为Board精灵与Dancer精灵重叠,脚本将Board精灵移至后两层,使得Dancer始终位于前方。你也可以通过选择Dancer精灵并点击外观面板中的移至前面积木来实现同样的效果。
本应用中的最后一个精灵是SpotLight精灵。图 3-16 展示了该精灵在绘图编辑器中的外观,以及你需要创建的脚本。图像的中心位于锥形的尖端,代表一束光。
图 3-16。SpotLight 精灵及其脚本
脚本首先将精灵的鬼影效果设置为 30,使其透明,以免遮挡背景。接着,脚本将该精灵移后一层,使光束位于舞者后面。然后,精灵的位置调整,使光束看起来是从聚光灯发出的(见图 3-10)。你需要根据你的图形选择* x 和 y *坐标。之后,脚本命令光束跟随舞者(使用指向命令)并永久改变其颜色。
完成聚光灯添加后,应用程序应该完成了。点击绿旗观看你的舞会效果!除了音乐和舞蹈外,你还应该看到Ball、Board和SpotLight精灵随着灯光变化,仿佛真实的迪斯科灯光正在作用。
在下一节中,我们将讨论一个不同的应用,突出展示我们在本章中学习的许多图形效果。
烟花
Fireworks_NoCode.sb2
另一个与本章讨论的图形积木和其他概念自然契合的应用是一个动画烟花场景。在这一节中,你将制作一个简单的烟花动画,带着五光十色的火花充满天空。烟花火箭将随机爆炸,产生如同受重力作用般的掉落火花,并随着时间慢慢消失,如图 3-17 所示。
图 3-17。烟花动画效果
首先打开文件Fireworks_NoCode.sb2,该文件包含了没有任何脚本的应用程序初始设置。如图 3-17 所示,应用程序包含两个精灵:City 精灵和 Rocket 精灵。City 精灵展示了一幅高楼大厦的图像,您可以根据自己的喜好进行动画处理。Rocket 精灵将不断创建克隆,在黑暗的天空中爆炸,产生烟花效果。
Rocket 精灵有八个服装,如图 3-18 所示。第一个服装,C1,只是一个小点,我们将把它发射到空中。当这个小点到达它的目标位置(随机选择)时,它将切换到其他服装之一(也是随机选择)以模拟最初的爆炸。然后我们将使用适当的图形效果让这个爆炸看起来更真实。
图 3-18. Rocket 精灵的八个服装。
牢记这个计划后,将图 3-19 中显示的脚本添加到 Rocket 精灵中。此脚本会在用户点击绿旗时运行。在隐藏 Rocket 精灵后,它开始一个永远循环,在随机时间创建自己的克隆。由于克隆继承了 Rocket 精灵的可见性状态,所有创建的克隆一开始都会是隐藏的。
图 3-19. Rocket 精灵的第一个脚本
现在我们需要告诉克隆的火箭该做什么。这个脚本见于图 3-20。
图 3-20. 克隆精灵的启动脚本
克隆的火箭首先穿上它的第一个服装①(小红点)。然后,它移动到舞台底部的一个随机水平位置②,展示自己③,并滑动到舞台上方的一个随机位置④(某个建筑物上方的位置)。这部分脚本模拟了火箭的发射,运行后,你将看到一个红点从地面飞向天空。当这个点到达天空中的最终位置时,它会因为脚本第二部分的指令而爆炸。首先,克隆会播放一个短促的鼓声⑤(模拟爆炸声)。烟花爆炸开始时较小,然后扩展,所以克隆将其初始大小设置为 20%,并随机选择另一个服装⑥。接着,它开始一个重复循环⑦来增长烟花的大小。每次通过循环时,克隆的大小增加 4。循环结束时,克隆会自我删除⑧。
这就是本次烟花节的全部内容!现在你应该能够运行动画并展示你创建的场景了。通过仅仅几个脚本,我们就制作了一个相对复杂的动画。
总结
在本章中,我们介绍了许多新的编程积木,可以用来为我们的应用程序增添一些亮点。有了这些积木,我们可以添加颜色、动画、图形效果、音乐等等。
我们解释了外观调色板中的积木,并举了几个如何使用它们的例子。你通过切换服装为角色制作了动画,了解了绘图层,并看到了层级如何影响重叠角色的外观。
接下来,我们介绍了声音调色板中的指令,并解释了如何播放音频文件、鼓声和音符。你通过使用外观和声音调色板中的指令,创建了一个完整的舞蹈场景,并通过制作烟花动画应用程序来完美收尾。
在下一章中,你将学习如何使用消息广播和接收来协调不同角色之间的工作。你还将学习如何将一个大型程序拆分成更小、更易管理的部分,这些部分称为过程。这个概念是编写更复杂应用程序的关键。
问题
| 问: | 1. 打开应用程序Zebra.sb2,如下所示。该应用程序包含一个角色(Zebra),它有三套服装。编写一个脚本,使Zebra在舞台上移动,并切换服装,创造奔跑的错觉。Zebra.sb2![]() |
|---|---|
| 问: | 2. 打开应用程序Wolf.sb2,如下所示。当你点击绿色旗帜时,Wolf会播放WolfHowl声音,持续约 4 秒钟。创建一个脚本,使Wolf的服装与声音同步变化。(提示:在每次切换服装后插入一个适当时间延迟的等待积木。)Wolf.sb2![]() |
| 问: | 3. 打开应用程序ChangingHat.sb2,如下所示。这个应用中的帽子是一个精灵,具有五个服装。创建一个脚本,使得点击帽子时切换其服装。然后创建一个游戏,玩家通过点击不同的衣物来为角色穿衣。ChangingHat.sb2![]() |
| 问: | 4. 打开Aquarium.sb2。该应用包含六个精灵,如下图所示。尝试不同的图形效果来动画化水族馆。以下是一些建议:Aquarium.sb2
-
在舞台上使用旋转效果。从较大的数字开始,比如 1,000,使角色呈现波浪状外观。
-
以合适的速率改变
Bubble1和Bubble2精灵的服装。 -
在舞台上移动
Fish并改变其服装。 -
对
Tree精灵应用幽灵效果。 -
对
Coral和Bubble3精灵使用颜色效果。![无标题图片]()
|
| 问: | 5. 打开应用程序Words.sb2(见下一页)并使用大小和旋转来动画化文字。创建图中显示的两个脚本并运行应用查看效果。Words.sb2![]() |
|---|---|
| 问: | 6. 打开应用程序Joke.sb2,如下所示。完成Boy和Girl精灵的脚本,讲述任何你想要的笑话。Joke.sb2![]() |
| 问: | 7. 打开Nature.sb2。该应用包含三个精灵,如下所示。使用运动和声音来动画化这个场景。以下是一些建议:Nature.sb2
-
Bird精灵有两个服装,能够呈现飞行效果。创建一个脚本,让Bird在舞台上飞行,并在随机时间播放Bird的声音。 -
Duck精灵有 12 个服装,展示了Duck从水中抓鱼并吃掉的场景。创建一个脚本,让Duck在舞台上移动,并在随机时间播放Duck的声音。 -
Seal精灵有四个服装,展示了Seal和球一起玩的场景。创建一个脚本,让Seal四处玩耍,并在随机时间发出SeaLion的声音。![无标题图片]()
|
第四章 程序
本章将解释如何通过“分而治之”的方法进行编程。与其将程序作为一个大块来构建,不如编写单独的过程(函数),然后将它们组合在一起。使用过程将使得编写程序更简单,测试和调试也更容易。在本章中,你将学习如何:
-
使用消息广播来协调多个精灵的行为
-
使用消息广播来实现过程
-
使用 Scratch 2 的“自定义模块”功能
-
使用结构化编程技术
到目前为止,我们开发的大多数应用程序只包含一个精灵,但大多数应用程序需要多个精灵协同工作。例如,一个动画故事可能有几个角色以及不同的背景。我们需要一种方法来同步精灵分配的任务。
在本章中,我们将使用 Scratch 的消息广播机制来协调多个精灵之间的工作(这是在 Scratch 的前一个版本中实现过程的唯一方式)。接着我们将讨论如何使用 Scratch 2 的“自定义模块”功能,将大型程序结构化为较小、更易管理的部分,这些部分被称为过程。过程是一系列执行特定功能的命令。例如,我们可以创建过程来让精灵绘制图形、进行复杂的计算、处理用户输入、排列音乐音符、管理游戏等。创建完成后,这些过程可以作为构建各种有用应用程序的积木。
消息广播与接收
那么,Scratch 中的广播系统如何在实践中工作呢?任何精灵都可以使用广播或广播并等待模块(来自事件面板)(见图 4-1)广播一条消息(你可以将这条消息命名为任何你喜欢的内容)。此广播会触发所有精灵(包括广播的精灵本身)中以匹配的当我收到触发模块开始的所有脚本。所有精灵都会听到广播,但只有在它们有对应的当我收到模块时才会响应。
图 4-1. 你可以使用消息广播和接收模块来协调多个精灵的工作。
参考图 4-2。图中显示了四个角色:海星、猫、青蛙和蝙蝠。海星广播了 jump 消息,这个广播会发送到所有角色,包括它自己。响应这个消息时,猫和青蛙都会执行它们的 jump 脚本。注意每个角色以不同的方式跳跃,执行不同的脚本。蝙蝠也接收到了 jump 消息,但由于没有指示它该如何处理该消息,它没有作出反应。图中的猫知道如何 walk 和 jump,青蛙只能 jump,而蝙蝠只被教会了 fly。
广播并等待命令与广播命令类似,但它会等到所有消息接收者执行完它们对应的当我接收到块后才会继续执行。
图 4-2. 广播消息被所有角色接收,包括发送广播的角色。
发送和接收广播
SquareApp.sb2
为了演示消息广播和接收是如何工作的,让我们创建一个简单的应用程序,绘制随机颜色的方块。当用户点击舞台上的左键时,舞台会检测到这个事件(使用其当此角色被点击块),并广播一个消息,你可以将其命名为 Square(如果你想,也可以选择其他名称)。当应用程序中的唯一角色接收到这个消息时,它会移动到当前的鼠标位置并绘制一个方块。按照以下步骤创建该应用程序:
-
启动 Scratch,然后从文件菜单中选择 新建 来创建一个新的应用程序。你可以随意更改猫的服装,选择你喜欢的任何样式。
-
向角色的脚本区添加当我接收到块(来自事件面板)。点击该块中的下拉箭头,并从下拉菜单中选择新消息...。在弹出的对话框中,输入
Square并点击 确定。该块的名称应更改为当我接收到 Square。 -
完成图 4-3 所示的脚本。角色首先抬起笔,移动到当前的鼠标位置,这由鼠标 x和鼠标 y块(来自感知面板)指示。接着它会随机选择一个笔的颜色,放下笔并绘制一个方块。
现在,当接收到 Square 消息时,角色准备好处理该消息。 图 4-3 中的脚本可以称为消息处理器,因为它的工作是处理(或处理)一个广播消息。
图 4-3. Square 消息处理器
现在,让我们进入舞台并添加代码,以便在鼠标点击时广播Square消息。点击精灵列表中的舞台,并添加图 4-4 中显示的两个脚本。第一个脚本在点击绿色旗帜时清除舞台上的任何笔迹。第二个脚本在用户点击舞台上的鼠标时触发,使用广播模块通知精灵是时候绘制了。
图 4-4。方形绘图应用中舞台的两个脚本
该应用程序现在已完成。要测试它,只需点击舞台上的鼠标。每次点击鼠标时,应用程序应通过绘制一个方形来响应。
向多个精灵广播消息进行协调
Flowers.sb2
为了看到多个精灵响应相同的广播消息,我们将创建一个应用程序,在鼠标点击时在舞台上绘制几个花卉。花卉应用包含五个精灵(命名为Flower1到Flower5),它们负责在舞台上绘制五朵花。每个精灵都有自己的服装,如图 4-5 所示。请注意,每个服装的背景是透明的。同时注意每个服装的旋转中心位置(用交叉线标出)。
图 4-5。花卉使用这五个花瓣精灵(如在画图编辑器中所示)。
当一个精灵收到绘制其花卉的消息时,它将在舞台上盖上多个旋转的服装副本,如图 4-6 所示。该图还展示了我们接下来要探索的花卉绘制脚本的示例输出。
图 4-6。花卉应用的绘制过程(左)和一些可能的花卉(右)
当你在舞台上点击鼠标时,舞台会通过当该精灵被点击时模块检测到鼠标点击。作为响应,它清除背景并广播一个名为Draw的消息。所有五个精灵都会响应这个消息,执行一个类似于图 4-7 所示的脚本。
图 4-7. 五个角色使用的基本脚本
脚本首先为颜色效果、亮度效果和大小赋予随机值,以改变绘制的花朵的外观。然后,它会移动到舞台上的一个随机垂直位置,并通过盖印旋转后的服装副本来绘制一朵花。
打开这个应用程序(名为Flowers.sb2),并运行它看看它是如何工作的。尽管它很简单,但它的输出却非常吸引人。我鼓励你设计不同的服装来创建不同类型的花朵。更改服装的中心位置,发现更有趣的花朵设计。
既然你已经了解了消息广播和接收的工作原理,我们将继续介绍结构化编程,作为管理大型程序复杂性的方式。
以小步骤创建大型程序
你到目前为止写的脚本相对较短且简单。最终,你将编写更长、更复杂的脚本,其中包含数百个块,理解和维护它们将成为一个真正的挑战。
一种被称为结构化编程的方法是在 1960 年代中期发展起来的,目的是简化编写、理解和维护计算机程序的过程。这个方法要求将程序分解为较小的部分,每个部分解决整个任务的一部分,而不是让你编写一个单一的大程序。
例如,考虑一下烤蛋糕的过程。你可能在烘烤时不会考虑每一个步骤,但这个过程遵循一个精确的食谱,列出了必要的步骤。食谱可能包括这样的指示:(1)混合 4 个鸡蛋、2 盎司面粉和 1 杯水;(2)将混合物放入平底锅;(3)将平底锅放入烤箱;(4)在 350°F 的温度下烘烤 1 小时;等等。从本质上讲,食谱将烤蛋糕的问题分解为几个明确的逻辑步骤。
同样,当你为你的编程问题设计解决方案时,将问题分解成可管理的、适合思考的小块是有帮助的。这种方法帮助你保持对整个程序以及其各个组成部分之间关系的清晰视图。
考虑图 4-8,它展示了一个在舞台上绘制形状的长脚本。你会看到,可以通过功能将这个脚本分成更小的逻辑块。例如,前六个块用于初始化角色。第一个repeat块绘制一个正方形,第二个绘制一个三角形,依此类推。使用结构化编程方法,我们可以将相关的块组合在一起,给它们起一个代表性的名称,以形成过程。
一旦我们写好了这些过程,我们就可以按照特定的顺序调用它们来解决编程问题。图 4-8 也展示了如何将这些独立的过程组合在一起,实现与原始脚本相同的功能。显然,使用过程的脚本(右边)比原始脚本(左边)更加模块化,且更易于理解。
过程还可以帮助你避免重复编写相同的代码。如果一组命令在程序中的多个地方执行,你可以编写一个过程来执行这些命令,并代替它使用。这种避免重复代码的策略被称为代码重用。例如,注意在图 4-8 中,绘制正方形过程是如何被重用的。
使用过程可以让你应用“分治法”策略来解决复杂问题。你将一个大而复杂的问题分解成多个子问题,然后分别解决这些更简单的问题,逐一测试每个子问题。解决所有子问题后,你将这些部分组合在一起,从而解决原始问题。这类似于我们的蛋糕烘焙策略:我们的食谱将问题分解成了明确定义的步骤,我们按正确的顺序执行这些步骤,最终完成蛋糕。
图 4-8. 将一个大脚本分解成每个完成一个功能的逻辑部分
在这一点上,你可能会问,“我们怎么创建这些过程?”在 Scratch 2 之前,你不能像图 4-8 中显示的那样构建初始化块,并在脚本中调用它。模拟过程并为程序增加一些结构的唯一方法是通过 Scratch 的消息广播机制。在 Scratch 2 中,这个问题得到了改变,新增了强大的“自定义块”功能。
本节将演示旧版的方法,因为这是你在旧版本的 Scratch 中创建的脚本所看到的方式。然而,创建自定义块的功能将在下一节中讲解,并将在本书的其余部分持续使用。
由于精灵可以接收自己的广播消息,我们可以通过让一个精灵向自己广播消息并在当我接收到触发块下执行所需任务来实现过程。我们可以使用广播并等待块来确保我们的过程按照正确的顺序被调用,从而为程序增加结构性和模块化。困惑了吗?让我们来看一下实际操作。
使用消息广播创建过程
Flowers2.sb2
我们将探讨过程是如何工作的,以及它们如何通过重新创建之前的“Flowers”程序来改进你的代码。
打开文件Flowers2.sb2,其中包含程序的新版本。舞台的脚本与之前相同(当它检测到鼠标点击时,舞台会广播Draw消息),但这一次,我们的程序只使用一个角色,而不是五个。这个角色有五个服装,leaf1到leaf5,并将在每个服装上调用一个过程来绘制一朵花。由于我们只有一个角色,因此只需要一份绘图代码(而不是我们第一版中五个重复的脚本)。这使得程序更小,代码也更容易理解。当应用程序中的角色接收到Draw消息时,它会执行图 4-9 中显示的脚本。
图 4-9. 当角色接收到Draw消息时,它会调用DrawFlower五次(在循环中)来绘制五朵花。
该脚本设置了* x 坐标和服装,用于绘制第一朵花,然后进入一个循环来绘制五朵花。在每次循环中,循环设置花朵的 y 坐标,并通过广播消息调用DrawFlower。这个调用会暂停脚本的执行,直到DrawFlower完成。当这种情况发生时,Draw脚本会恢复执行,调整 x *坐标并更改服装,为绘制下一朵花做准备。
注意
你可以随意为过程命名,但我建议选择一个能反映该过程目的的名称。这在你回顾几个月前写的程序时尤其有帮助。例如,如果你想在游戏中展示玩家的分数,你可以创建一个名为ShowScore的过程。将这个过程命名为Mary或Alfred显然不会提醒你(或任何阅读你程序的人)这个过程的作用。
DrawFlower过程如图 4-10 所示。它在绘制一朵花之前设置了颜色效果、亮度和角色大小的随机值,然后将当前服装的旋转版本进行印章操作。
在程序的第一个版本中包含了五个精灵和五个重复的脚本,而第二个版本则使用一个精灵调用一个过程来绘制所有五朵花,达到了相同的结果。打开Flowers.sb2和Flowers2.sb2并在浏览器的两个标签中进行比较。新的版本是不是更简洁易懂?使用过程可以让你编写更小的程序,便于理解和维护。当你编写更复杂任务的程序时,这将变得更加有益。
图 4-10. DrawFlower 过程
创建你自己的块
从 Scratch 2 开始,你也可以创建自己的自定义块。创建自定义块后,它应该出现在更多块调色板中,你可以像使用其他 Scratch 块一样使用它。
为了向你展示如何创建和使用这些块,我们将修改上一节中讨论的 Flowers2 程序,使用一个自定义块来替代DrawFlower过程。以下步骤将指导你创建这个新版本的应用程序。
-
首先,打开你在上一节中查看的Flowers2.sb2文件。从文件菜单中选择文件 ▸ 下载到你的计算机,并将文件保存为Flowers3.sb2。如果你愿意,可以选择不同的名称。
-
点击
Flower精灵的缩略图以选择它。然后选择更多块调色板,并点击创建一个块。你应该会看到如图 4-11 所示的对话框(左侧)。为该块输入DrawFlower作为名称,然后点击确定。一个名为DrawFlower的新功能块应该会出现在更多块调色板中,同时一个定义 DrawFlower块应该会出现在脚本区域,如图中所示(右侧)。
图 4-11. 创建 DrawFlower 自定义块后出现的新块对话框 -
将与当我接收到 DrawFlower块连接的脚本分离,并将其连接到定义 DrawFlower块,如图 4-12 所示。这样会创建一个名为DrawFlower的新过程,它作为自定义块实现。删除当我接收到 DrawFlower块,因为它不再需要。
图 4-12. 作为自定义块实现的DrawFlower过程 -
现在我们已经创建了一个DrawFlower过程,我们只需要在
Draw消息处理器中调用它。按照图 4-13 所示修改Draw消息处理器。注意,我们只是将广播 DrawFlower 并等待积木替换为新的DrawFlower自定义积木。
图 4-13. 从Draw消息处理器调用DrawFlower
程序现在已经完成,你可以进行测试。点击舞台上的鼠标,验证程序是否像之前一样运行。查看运行时不刷新屏幕了解如何加速程序的执行。
现在你已经了解了自定义积木的基本知识,你可以更进一步,制作可以接受输入的积木。
运行时不刷新屏幕
使用自定义积木实现DrawFlower过程引出了另一个可以缩短绘图脚本执行时间的功能。为演示,请执行以下操作:
-
右键点击更多积木调色板下的DrawFlower积木,并从弹出菜单中选择编辑。这将弹出一个对话框,如图 4-11 所示,只是标题将是编辑积木而非新积木。
-
点击选项旁的箭头,勾选运行时不刷新屏幕框并点击确定(见图 4-15)。
-
现在,点击舞台上的鼠标,看看会发生什么。你应该会看到五朵花几乎同时出现在舞台上,而不是看到单独的旋转和印章步骤。这是发生了什么的解释。
DrawFlower过程包含了许多改变精灵外观的积木,包括设置颜色、设置亮度、设置大小和印章。在执行这样的积木后,Scratch 通常会暂停一会儿以刷新(即重新绘制)屏幕。这就是我们在程序运行时能看到绘图进度的原因。
如果选择“运行时不刷新屏幕”选项,积木将在不暂停刷新屏幕的情况下运行,这样可以使过程运行得更快。Scratch 执行完整个过程后,屏幕将刷新。
除了加速过程外,运行时不刷新屏幕选项还帮助防止反复重绘所导致的闪烁。
向自定义积木传递参数
让我们从创建一个名为Square的自定义积木开始,它绘制一个边长为 100 像素的正方形,如图 4-14 所示。
图 4-14. 一个绘制固定大小正方形的Square程序
Square程序的功能是有限的,因为绘制的正方形的大小一成不变。如果你想绘制边长不同的正方形,例如 50、75 或 200,怎么办?你可以定义多个名为Square50、Square75和Square200的自定义积木,但在大多数情况下,创建多个基本相同的积木并不是一个好主意;如果需要做出更改,你还得找到所有副本并修改它们。一个更好的解决方案是使用一个单一的Square积木,允许用户在调用时指定所需的边长。
你其实从第一章开始就已经在应用这个概念了。例如,Scratch 提供了一个单一的move积木,允许你通过在参数框中输入数字来指定精灵移动的步数。通过这种方式,Scratch 不需要为每一个可能的移动距离提供一个新的积木。
因此,我们需要做的是在Square积木中添加一个参数输入框,供用户输入边长。图 4-15 展示了如何修改Square积木。
图 4-15. 向Square积木添加数字输入
首先,右键点击Square积木(或脚本区中的定义 Square积木),从弹出菜单中选择编辑,以打开编辑积木对话框①。点击选项旁边的小箭头,展开对话框并查看可用的选项。
我们希望我们的Square积木接受一个正方形的边长,这是一个数字,因此点击添加数字输入②,向积木中添加一个数字输入框。应该向Square积木添加一个名为number1的数字输入框。
为了表明这个新输入框用于接收正方形的边长,我们将默认名称从number1改为一个有意义的名称③,像是side、length或sideLength。(再次提醒,尽管 Scratch 对你使用的标签不关心,但你自己会!选择一个能反映参数含义的名称。)在这个示例中,我们使用side作为名称。
从技术上讲,这就是我们需要做的,向我们的过程添加一个数字插槽。如果我们点击确定,我们将得到一个正方形块,它接受一个数字作为输入。我们可以将这个块拖到脚本中,并在参数插槽中指定所需的长度,如正方形 50。但用户如何知道传递给正方形的数字代表什么呢?是表示 50 的面积,50 的对角线,50 的边长,还是其他什么?
假设 Scratch 的滑行块是这样设计的:

你如何知道第一个插槽表示时间(以秒为单位),第二个和第三个插槽表示目标滑行点的 x 和 y 坐标呢?Scratch 设计者通过为这些插槽添加标签,使得滑行块更加易于理解和使用,具体如下:

我们可以通过添加文本来描述参数插槽的意义(或用途),对我们的正方形块进行相同的操作。点击添加标签文本④,如图 4-15 所示,在side参数后添加标签。输入steps作为标签文本,然后点击确定。
现在,如果你检查脚本区域中正方形过程的定义,你会看到一个小块(名为side)被添加到其头部,如图 4-16(左)所示。移动块中仍然包含固定数字 100,但现在我们需要做的只是将side块从正方形方法的头部拖动,并将其放置到移动块的参数插槽中,替换数字 100,如图 4-16(右)所示。
图 4-16. 修改正方形过程以使用边长参数
在Square过程的头部出现的标签side被称为参数。你可以把参数看作是一个命名的占位符。我们希望Square过程能够绘制任意大小的正方形,因此我们没有在过程内部硬编码一个固定的数字,而是使用了一个名为side的通用参数。当用户调用Square过程时,会指定side的具体值。让我们通过修改图 4-14 中的脚本来说明这一点,使用我们新版本的Square过程。所需的更改在图 4-17 中有展示。
图 4-17。调用Square过程,并将side设置为 100
在这里,数字 100(称为实参)被传递给Square过程。当执行Square时,它的side参数被设置为 100,并且这个值会替换过程内部所有出现的side块。正如你所看到的,能够为过程指定不同的实参是一个强大的特性,它为我们的程序增加了很多灵活性。
我们可以通过使Square过程接受正方形的颜色作为第二个参数来进一步增强其功能,如图 4-18 所示。在这里,我们添加了一个第二个输入参数,称为clrNum(即颜色编号),它表示正方形的期望颜色。现在,该过程在执行绘制循环之前,会将笔的颜色设置为clrNum指定的值。编辑Square块以实现图中所示的更改。
参数与实参
尽管许多程序员将参数和实参这两个术语交替使用,但实际上它们是不同的。为了解释清楚,考虑下面的Average过程,它计算两个数字的平均值。

如定义所示,这个过程有两个名为num1和num2的参数。参数定义了过程的输入。你将使用左侧显示的块调用这个过程,并在可用的插槽内指定一些值或表达式。在上面的例子中,100 和 50 被称为该过程的实参。
当然,过程调用中的参数数量必须与过程定义中的参数数量匹配。当你调用Average时,参数num1和num2分别接收值 100 和 50,因为参数和实参是通过位置来匹配的。
图 4-18。这个版本的Square将所需颜色作为第二个参数。尝试练习 4-1
那么,正方形边框的粗细如何呢?修改Square过程,增加一个名为penSize的第三个参数,用于指定绘制正方形时使用的笔的大小。
让我们通过一些有用的技巧来结束这一节,帮助你处理自定义积木:
-
自定义积木不能在精灵之间共享。如果你为
Sprite1创建了一个自定义积木,那么只有Sprite1可以使用该积木。同样,为舞台定义的自定义积木只能被属于舞台的脚本调用。 -
给你的参数起一个有意义的名字,表明它们的用途。
-
要删除自定义积木,只需将其define积木(即帽子积木)从脚本区拖动并放到调色板区。只有当你的项目中没有与该积木关联的堆栈积木时,你才能删除define积木,因此在尝试删除之前,先从你的脚本中移除所有自定义积木的使用。
-
要删除自定义积木的参数,在编辑积木对话框中点击参数的名称,然后点击出现在参数槽上方的小X图标。
-
除了数字输入外,你还可以添加字符串和布尔型参数。我们将在下一章讨论变量时深入讲解数据类型。
现在,你可能会问:一个过程能否调用另一个过程?在下一节中,你将了解如何使用嵌套过程调用来扩展现有过程的功能和实用性。
使用嵌套过程
如前所述,一个过程应该设计成执行一个单一的、明确定义的任务。为了执行多个任务,调用另一个过程作为其执行路径的一部分是完全合法的——在许多情况下也是很有用的。通过这种方式嵌套过程,你可以在构建和组织程序时获得极大的灵活性。
RotatedSquares.sb2
为了让你了解其实际应用,我们从上一节中编写的Square过程开始(参见图 4-17)。现在,我们将创建一个新的过程,名为Squares,它绘制四个拉伸的正方形,如图 4-19 所示。它通过调用Square过程四次来实现,每次调用使用不同的参数,最终输出四个共享一个角的正方形。
图 4-19。Squares过程及其输出
我们现在可以使用Squares来创建一些有趣的艺术作品。图 4-20 展示了另一个过程,叫做RotatedSquares,它多次调用Squares过程,并在每次调用后旋转图形一定角度。
图 4-20. RotatedSquares过程和一些可能的输出
在这个过程中,count参数被使用了两次:第一次用于确定重复次数,第二次在调用Squares后计算转动角度。例如,将count设置为5,将会导致重复图 4-20 中的方形模式五次,每次调用后旋转 72°(即 360° / 5)。你可以尝试不同的值以发现新的模式。
Checkers.sb2
让我们来做一个展示嵌套过程强大功能的例子:我们将从图 4-16 中的Square过程开始,最终得到一个棋盘格。
创建一个新的过程(叫做Row),如图 4-21 所示,画出一排方形。请注意,画多少个方形是作为参数指定的。为了简化起见,我们将每个方形的大小固定为 20 步,而不是将大小定义为Row过程的第二个参数。
图 4-21. Row过程
图 4-21 还展示了调用Row并传入4作为参数的结果,这会使过程在循环中调用Square 20 步四次。在每次画完一个方形后,角色的位置会进行调整,以为下一个方形设定初始位置。画完四个方形后,最后一条命令会将角色返回到其初始位置。
要在图 4-21 中显示的方形下方画出另一行方形,我们只需将角色下移 20 步,然后再次调用Row过程。我们可以重复这个操作,画出任意多行。我们的Checkers过程,如图 4-22 所示,就是这么做的。
图 4-22. Checkers 过程及其输出
该过程有两个参数:所需棋盘的行数和列数。每绘制完一行后,过程会将精灵向下移动 20 步,为绘制下一行方格做准备。
本节中的示例展示了过程如何帮助你将程序分解为更小、更易于管理的部分。写完并测试好你的过程后,你可以将它们作为构建更复杂过程的积木,而无需过多担心底层的实现细节。你可以专注于使用这些过程作为构建块来组装整个应用程序这一重要任务。
实践练习 4-2
如果你将初始方向设置为 0°(上)而不是 90°(右),你认为会发生什么?脚本会正常工作吗?如果不行,你怎么修复它?做出这个更改并运行脚本来测试你的答案。
与过程一起工作
现在你已经了解了将程序分解成更小的部分并逐一处理的重要性,让我们讨论如何进行这种分解。每个问题都不同,没有“万能”的解决方案——这也正是让它成为一个有趣难题的原因!
在本节中,我们将首先探讨将大型程序分解为具有清晰逻辑结构的模块化部分的自上而下过程。然后我们将讨论另一种构建复杂程序的方法:通过组合现有过程的自下而上过程。图 4-23 展示了这两种方法的高层次视图。
图 4-23. 说明自上而下(左)和自下而上(右)方法
在这两个图中,我们想要解决的问题在顶部,构建我们解决方案的各个步骤在底部。你可以从任何一个对你有意义的层次开始。
将程序分解为过程
解决任何编程问题的第一步是充分理解问题。在此之后,你可以规划一个通用的解决方案,并将其分解为主要任务。没有一种“正确”或“错误”的方法来分解任何特定的程序,随着经验的积累,你会更好地判断“主要”的含义。从通用解决方案到具体细节的分解,至少能确保程序的整体逻辑是正确的。
House.sb2
为了演示这种解决问题的策略,我们来考虑如何绘制一个类似于图 4-24 中所示的房子。
图 4-24。我们可以通过将任务分解成若干个小块,并逐个处理每个小块,来绘制这座房子。
一方面,处理这个简单问题让我们可以专注于解决策略,而不会被许多细节困扰。另一方面,尽管问题看似简单,但它能够衍生出许多不同的解决方案。以下是一些可能性:
-
我们可以将房子看作由直线组成。在这种情况下,绘制每一条线都是一个主要任务。
-
我们可以将房子看作由六个独立的形状组成:侧面 1、侧面 2、两扇门、一个三角形和一个平行四边形。绘制每个形状构成了一个主要任务。
-
由于两扇门是相同的,我们可以定义一个绘制门的主要任务,并调用该任务两次。
-
我们可以将房子顶部的三角形和平行四边形视为一个单元,即屋顶。在这种情况下,一个主要任务就是绘制屋顶。
-
我们可以将侧面 1 及其门视为一个单元,即前侧。这样,一个主要任务就是绘制前侧。
还有许多其他的可能性,但这些已经足够说明问题了。关键是将任务分解为小的、易于理解的部分,并逐一处理每个部分。如果发现类似的部分,尝试提出一个通用解决方案并将其应用于所有这些部分。
鉴于此,我们绘制房子的计划也在图 4-24 中概述。该计划假设精灵从 A 点朝右开始。我们需要做的就是创建一个脚本,按照计划中概述的步骤进行操作。我们将编写一个过程(名为Side1)来绘制房子的左侧,如步骤 1 所示。我们还将编写三个过程(分别称为Door、Side2和Roof)来绘制两扇门、房子的右侧和屋顶(如步骤 2、3、4 和 6 所示),并将所有这些过程与适当的运动命令连接起来。
我们的House过程如图 4-25 所示,图中还展示了与每个过程调用对应的绘图步骤。该过程接受一个参数(称为scale),用于指定绘制房子的单位长度(即缩放因子)。请注意,Door过程被重复调用了两次。同时请注意,Roof过程负责绘制整个屋顶,它可能包含不同的子过程,用于绘制屋顶的各个组成部分。
图 4-25. House过程。注意主要任务如何与绘图计划对齐。
绘制房屋的各个过程如图 4-26 所示。这些过程使用你在第二章中学到的相同技巧来绘制简单的几何形状。
Side1、Door和Side2过程分别绘制 3×5、1×2 和 9×5 的矩形(根据scale因子进行缩放)。Roof过程有两个子过程(分别命名为Triangle和Parallelogram),用于绘制屋顶的两个部分。请注意,所有这些过程都一致使用了缩放因子scale。这使得我们能够通过传递不同的参数调用House过程,绘制更大或更小的房屋。
动手试一试 4-3
你注意到Side1、Door和Side2过程使用几乎相同的代码吗?创建一个名为Rectangle的新过程,它接受长度、宽度和缩放作为参数,并绘制指定尺寸的矩形。修改Side1、Door和Side2过程,以调用新的Rectangle过程。
图 4-26. 绘制房屋的过程如图 4-24
通过过程构建
FlowerFlake.sb2
解决大问题的另一种方法是先关注较小的细节。如果你先解决了大问题中的小部分(或找到已经存在的解决方案),然后你可以从下到上组合这些结果,最终达到完整的解决方案。
为了演示这个问题解决技巧,让我们从一个简单的过程(称为Leaf)开始,该过程绘制一个如图 4-27 所示的单个叶子。该过程包含一个repeat循环,循环执行两次以绘制叶子的两半。每一半都是通过 15 个短线段绘制而成,线段之间有 6°的转角。这类似于我们在第二章中使用的绘制多边形的方法。
图 4-27. Leaf过程及其输出
以这个过程为起点,我们现在可以绘制一个稍微复杂一些的图形,包含五片叶子。我们新建的过程叫做Leaves,其输出如图 4-28 所示。如你所见,我们只需要在repeat循环中调用Leaf过程,并在每次调用之间使用适当的转角。
图 4-28。Leaves过程在每次调用之间以 72°的转角调用Leaf过程五次。
现在我们可以使用Leaf和Leaves来构建一个更复杂的东西:一个带有叶子的树枝。我们的Branch过程及其输出如图 4-29 所示。精灵向前移动 40 步,画出一片叶子(通过调用Leaf过程),再向前移动 50 步,画出五片叶子(通过调用Leaves过程),最后返回起始位置。
图 4-29。Branch过程及其输出
让我们再进一步。如何使用Branch过程来绘制一朵复杂的花朵图案?我们新建的过程叫做Flower,其输出如图 4-30 所示。这个过程简单地在循环中调用Branch过程六次,每次之间转角 60°。
图 4-30。Flower过程及其输出
我们可以继续下去,但现在应该明白了这个思路。我们从一个简单的过程Leaf开始,并将它用于一个新的过程(叫做Leaves)来创建一个复杂的图案。Branch过程依赖这两个过程来创建更复杂的东西。然后,Flower过程利用Branch绘制了一个更复杂的图案。如果我们想的话,我们还可以创建一个绘制整棵花树的过程,再创建一个绘制满是树的花园的过程。
从这个例子中我们可以得到的要点是,无论我们要解决的问题有多复杂,我们总能通过将许多更小、更易管理的部分拼接在一起来构建解决方案。使用这种问题解决技巧,我们从解决非常简单问题的短小过程开始,然后利用它们创建更复杂的过程。
摘要
在本章中,我们介绍了一些基础概念,这些概念将在本书的剩余部分中得到广泛应用。首先,我们解释了用于交互式通信和同步的消息广播的概念。接着,我们介绍了结构化编程,并讨论了如何使用消息广播来实现程序。然后,我们演示了 Scratch 2.0 的自定义模块功能,并解释了如何向程序传递参数,以使程序更加灵活。我们通过几个示例展示了将大问题拆解成更小、更易管理的部分,并解释了如何使用程序作为创建大型程序的基本构建块。最后,我们考察了一种自下而上的问题解决技巧,在该技巧中,我们将已知的小问题的解决方案拼凑在一起,来解决大问题。
在下一章中,你将学习任何编程语言中最重要的概念:变量。对变量的介绍将是成为一名熟练程序员的重要一步。
问题
| 问: | 1. 编写不同的程序来绘制你名字中的每个字母。为每个字母命名相应的程序。然后编写一个脚本,调用这些程序,让你能在舞台上绘制出你的名字。 |
|---|---|
| 问: | 2. 创建如下所示的程序,运行它,并解释它是如何工作的。![]() |
| 问: | 3. 编写一个程序,将摄氏度转换为华氏度,如下所示。让脚本将答案四舍五入到最接近的整数。测试不同温度下的程序。(提示:°F = (9 / 5) × °C + 32。)![]() |
| 问: | 4. 编写一个程序来创建右侧显示的房子。首先编写绘制房子小部分(例如门、屋顶、窗户等)的程序。然后将这些程序组合起来,创建整个房子。![]() |
| 问: | 5. 编写一个程序来计算圆的面积(A = πr²),给定其半径,如下所示。使用π = 3.14。![]() |
| 问: | 6. 在本练习中,你将模拟鱼在水下所承受的压力。假设鱼所受的压力P(以大气压为单位)与其深度d(距离水面多少米)之间的关系为:P = 0.1d + 1. PressureUnderWater_NoSolution.sb2包含了这个模拟的部分实现。完成脚本,使得鱼在游动时说出它所感受到的压力,如下所示:PressureUnderWater_NoSolution.sb2![]() |
第五章 变量
本章解释了如何创建能够读取和记住值的脚本。当你使用变量时,可以编写与用户交互并响应其输入的应用程序。以下是本章的内容:
-
Scratch 支持的数据类型
-
如何创建变量并操作它们
-
如何获取用户输入并编写交互式程序
尽管你在过去四章中编写的脚本帮助你学习了重要的 Scratch 编程技能,但它们缺少了大型应用程序的许多关键元素。更复杂的程序可以记住值,并根据某些条件决定执行某个动作。本章将解决这两个缺陷中的第一个,决策功能将在下一章介绍。
如你所学,脚本在执行过程中会处理和操作不同类型的数据。这些数据可以输入到命令块中(例如,移动 10 步命令中的数字 10,或者说你好!命令中的“你好!”字符串),也可以是从功能块输出的数据(例如鼠标 x,y 位置和随机选择),或者数据可以是用户响应询问并等待命令时输入的。对于更复杂的程序,你通常需要存储和修改数据以完成某些任务。Scratch 中的数据管理可以通过变量和列表来实现。本章将详细探讨变量,列表将在第九章中讲解。
本章从 Scratch 支持的数据类型概述开始。接着介绍变量,并讨论如何在程序中创建和使用它们。然后将解释变量监视器,并在几个有趣的应用程序中使用它们。在掌握了基本概念后,你将学习如何使用询问并等待命令来获取用户输入。
Scratch 中的数据类型
许多计算机程序操作不同种类的数据,包括数字、文本、图像等,以生成有用的信息。这是一个重要的编程任务,因此你需要了解 Scratch 支持的数据类型和操作。Scratch 内置支持三种可以在积木中使用的数据类型:布尔值、数字和字符串。
布尔值只有两种可能的值:真或假。你可以使用这种数据类型来测试一个或多个条件,并根据结果让程序选择不同的执行路径。我们将在下一章详细讨论布尔值。
数字变量可以存储整数和小数值。Scratch 不区分这两者,它们都被归类为“数字”。你可以使用来自运算符面板的四舍五入块将小数数字四舍五入为最接近的整数。你还可以使用来自运算符面板中的取整(或向上取整)功能,从指定的小数中得到一个整数。例如,取整 3.9是 3,而向上取整 3.1是 4。
字符串是由字符组成的序列,可以包括字母(大写和小写)、数字(0 到 9)以及你键盘上可以输入的其他符号(如 +、–、&、@ 等)。你会使用字符串数据类型来存储名字、地址、书名等。
形状里面有什么?
你是否注意到 Scratch 块和它们的参数插槽各自有特定的几何形状?例如,移动 10 步块中的参数插槽是一个圆角矩形,而说 Hello!块中的参数插槽是一个带尖角的矩形。参数插槽的形状与它接受的数据类型有关。试着在移动 10 步块中输入你的名字(或其他任何文本);你会发现,Scratch 只允许你在圆角矩形插槽中输入数字。
同样,功能块的形状也表示它返回的数据类型。不同形状的含义在图 5-1 中有所说明。
图 5-1. 命令块和功能块形状的含义
参数插槽有三种形状(六边形、矩形和圆角矩形),而功能块只有两种形状(六边形和圆角矩形)。每种形状都与特定的数据类型相关,尽管你需要注意的是,圆角矩形的功能块既可以报告数字,也可以报告字符串。
六边形和圆角矩形插槽只接受相同形状的功能块,而矩形插槽可以接受任何功能块。好消息是,Scratch 会阻止你将类型不匹配的块放入插槽,所以你不需要记住这个规则。试着将一个六边形形状的块拖入圆角矩形插槽,你会发现无法放入,因为它们的类型不兼容。
自动数据类型转换
正如我在上面提到的,一个数字参数插槽只能接受圆角矩形功能块。到目前为止,你处理过的所有圆角矩形功能块——包括x 位置、y 位置、方向、服装编号、大小、音量、节奏等——都报告数字。因此,将它们放入数字插槽(例如移动 10 步块)并不成问题。然而,一些圆角矩形功能块,如Sensing调色板中的回答块或Operators调色板中的连接块,可以包含数字或字符串。这就引出了一个重要问题:如果我们例如将包含字符串的回答块插入数字插槽会发生什么呢?幸运的是,Scratch 会根据需要自动尝试在数据类型之间进行转换,如图 5-2 所示。
在这个例子中,用户在“请输入一个数字”提示下输入了125。用户的输入被保存在回答功能块中。当这个输入传递给说命令时,它会自动转换为字符串。当相同的回答传递给加法运算(需要数字)时,它会转换为数字 125。当加法运算执行时,结果(25 + 125 = 150)会被转换回字符串,“150”会被传递到说块中。Scratch 会自动尝试为你处理这些转换。
图 5-2. Scratch 根据上下文自动进行数据类型转换。
了解 Scratch 中可用的数据类型、对这些类型允许的操作,以及 Scratch 如何在它们之间进行转换,将帮助你理解为什么事物以这种方式运作。在下一部分,你将学习变量,以及如何使用变量来存储和操作程序中的数据。
变量简介
假设我们想创建一个软件版本的“打地鼠”游戏。原版游戏有一个平坦的表面,表面上有几个洞。玩家使用槌子打出洞口的地鼠。在我们的版本中,一个角色精灵会随机出现在舞台上的某个位置,保持可见一段时间后消失。然后它会等一会儿,再出现在另一个位置。玩家需要在精灵出现时尽快点击它。每次点击精灵,玩家就得一分。作为程序员,你面临的问题是,如何追踪玩家的分数?欢迎进入变量的世界!
在本节中,我将介绍变量,这是任何编程语言中最重要的元素之一。你将学习如何在 Scratch 中创建变量,并如何使用它们来记住(或存储)不同类型的数据。你还将探索在程序中设置和更改变量值的可用块。
什么是变量?
变量 是计算机内存中一个命名的区域。你可以把它看作是一个用来存储数据的框,包括数字和文本,供程序根据需要访问。在图 5-3 中,我们展示了一个名为 side 的变量,它的当前值是 50。
图 5-3. 变量就像一个命名的框,里面包含一些值。
当你创建一个变量时,你的程序会分配足够的内存来存储变量的值,并用该变量的名称标记分配的内存。创建变量后,你可以在程序中使用它的名称来引用它所代表的值。例如,如果我们有一个名为 side 的框(即变量),它的值是 50,我们可以构建一个像 移动 (3*side) 步 的命令。当 Scratch 执行这个命令时,它会在计算机内存中找到名为 side 的框,取出其中的内容(在这个例子中是数字 50),并用这个值替换 移动 (3*side) 步 块中的 side 标签。结果,精灵将移动 150 步(即 3 × 50)。
在我们的打地鼠游戏中,我们需要一种方法来记住玩家的得分。为此,我们可以在计算机的内存中预留一些空间(就像一个框)来存储得分。我们还需要给这个框一个唯一的标签,比如 score,这样我们就可以在需要知道或更改其中内容时找到它。
当游戏开始时,我们会告诉 Scratch “将 score 设置为 0”,Scratch 会查找标记为 score 的框,并将 0 这个值放入其中。每当玩家点击精灵时,我们还会告诉 Scratch “将 score 增加 1”。第一次点击时,Scratch 会再次查看 score 框,找到我们的 0,增加 1,并将结果(即 1)放回框中。下次玩家点击精灵时,Scratch 会再次按照我们的“将 score 增加 1”命令来增加 score,并将结果值 2 存入框中。
你稍后会看到这些操作的实际 Scratch 块。现在,请注意 score 的值在程序中是会变化的。这就是为什么我们称其为 变量——它的值是可变的。
变量的一个重要用途是存储评估代数表达式的中间结果。这类似于你做心算的方式。例如,如果让你计算 2 + 4 + 5 + 7,你可能会先将 2 + 4 相加,并记住结果(6)。然后你会将 5 加到之前的结果(保存在你的记忆中),并记住新结果,即 11。最后,你会将 7 加到之前的结果,得到最终结果 18。
为了说明变量如何用于临时存储,假设你想编写一个程序来计算以下表达式:

你可以用一个命令来评估整个表达式,但将所有内容塞进一个语句中会让它变得很难阅读,如下所示:

另一种编写程序的方式是分别评估分子和分母,然后使用say块显示它们相除的结果。我们可以通过创建两个变量来做到这一点,分别命名为num(用于分子)和den(用于分母),并设置它们的值,如图 5-4 所示。
图 5-4。两个变量(num和den)分别保存表达式的分子和分母的值。
看一下我们在计算机内存中如何安排这些变量。在这里,num就像一个标签,指向存储评估(1 / 5 + 5 / 7)结果的内存位置。同样,den指向存储(7 / 8 – 2 / 3)结果的位置。当say命令执行时,Scratch 会获取标记为num和den的内存内容。然后,它会将这两个数字相除,并将结果传递给say命令进行显示。
我们可以进一步拆解这个表达式,分别评估每个分数,然后显示总表达式的结果,如图 5-5 所示。
图 5-5。使用四个变量(a、b、c和d)来保存表达式中的四个分数
在这里,我们使用四个变量(命名为a、b、c和d)来保存数学表达式中的四个分数。图中还显示了内存分配;这次你可以看到四个变量及其内容。
尽管这三个程序得出了相同的答案,但每种实现方式都有不同的风格。第一个程序将所有内容放在一条语句中,这使得它难以阅读。第三个程序将内容拆解得更为详细,但这样也可能让人难以理解。第二种解决方案将表达式分解到一个合理的层次,并使用变量来使程序更易于理解,同时清晰地展示表达式的主要部分(分子和分母)。正如金发姑娘所说,这种方法恰到好处。
这个简单的示例展示了一个问题可以有多个解决方案。有时你可能关注程序的速度或大小,其他时候你的目标可能是可读性。由于这是一本入门编程书籍,书中的脚本是为了强调可读性而编写的。
现在你已经了解了变量是什么以及为什么要使用它们,让我们创建一些变量,并将我们的 Scratch 应用程序更进一步。
创建和使用变量
DiceSimulator_NoCode.sb2
在这一部分,我们将通过一个简单的应用程序来探索如何创建和使用变量,该应用程序模拟掷一对骰子并显示它们的总和,如图 5-6 所示。
图 5-6. 骰子模拟器的用户界面
我们的骰子模拟器包含三个精灵:Player、Die1 和 Die2。Player 精灵管理模拟过程。当按下绿色旗帜图标时,该精灵生成两个介于 1 和 6 之间的随机数,并将这些值分别保存在名为 rand1 和 rand2 的两个变量中。接着,它会向另外两个精灵(Die1 和 Die2)广播消息,显示随机生成的值;Die1 显示 rand1 的值,Die2 显示 rand2 的值。之后,Player 精灵将 rand1 和 rand2 相加,并使用 say 块显示总和。
让我们从头开始构建这个应用程序。打开文件 DiceSimulator_NoCode.sb2。该文件包含了舞台的背景图像以及应用程序中使用的三个精灵。我们将逐步创建所需的所有脚本。
首先,点击 Player 精灵的缩略图以选择它。选择 数据 面板并点击 创建变量,如图 5-7(左)所示。在弹出的对话框中,如图 5-7(右)所示,输入变量的名称并选择其作用范围。一个变量的 作用范围 决定了哪些精灵可以写入(或更改)该变量的值,下一节中我会详细解释这一点。在此示例中,输入 rand1 作为变量的名称,并选择 对所有精灵有效 选项作为变量的作用范围。完成后点击 确定。
图 5-7. 创建一个变量,命名它,并指定它的作用范围
创建变量后,与其相关的几个新块将出现在 数据 面板中,如图 5-8 所示。
图 5-8. 创建 rand1 变量时出现的新块
你可以使用这些块将变量设置为特定值、按固定值更改它,并在舞台上显示(或隐藏)其监视器。一个变量的 监视器,正如你将在显示变量监视器一节中学习到的那样,显示存储在该变量中的当前值。
命名变量
多年来,人们提出了不同的方式来命名程序中的变量。一个常见的约定是变量名以小写字母开头,后续单词的首字母大写,例如 sideLength、firstName 和 interestRate。
虽然 Scratch 允许变量名以数字开头并包含空格(例如,123Side 或 side length),但大多数编程语言不允许这样,因此我建议你避免使用这些不寻常的变量名。虽然你可以给变量命名任何你想要的名称,但我强烈建议使用描述性且有意义的名称。像 w 和 z 这样的单字母变量应尽量减少使用,除非它们的含义非常明确。另一方面,过长的名称可能会让你的脚本更难阅读。
另外,请注意,在 Scratch 中,变量名是区分大小写的,这意味着 side、SIDE 和 siDE 都是不同的变量。为了避免混淆,尽量不要在同一脚本中使用名字仅在大小写上有所不同的变量。
按照我上面描述的步骤,创建一个名为rand2的变量。数据调色板现在应该包含第二个变量积木(名为rand2),并且图 5-8 中的下拉箭头应该让您在rand1和rand2之间进行选择。现在我们已经创建了两个变量,我们可以为Player角色构建脚本。完整的脚本显示在图 5-9 中。
图 5-9. Player角色的脚本
第一个命令将rand1设置为 1 到 6 之间的随机数字。回想一下我们用盒子做类比的情况:这个命令使得角色找到标记为rand1的盒子,并将生成的随机数字放入其中。第二个命令将rand2设置为另一个 1 到 6 之间的随机值。接下来,脚本会广播一个名为Roll的消息给其他两个角色(Die1和Die2),通知它们需要按照rand1和rand2的指定来切换服装。一旦Die1和Die2角色完成任务,脚本将继续执行并通过说积木显示骰子面上的数字总和。让我们来看一下Die1角色的Roll消息处理程序,见图 5-10。
图 5-10. 要在命令积木中使用变量,只需将该变量拖动到该积木的参数槽中。
在图右上角创建如示的脚本后,将rand1积木从数据调色板拖动到切换服装积木的参数槽中,形成完整的脚本(右下角)。在这个脚本中,重复积木会将骰子的服装随机改变 20 次,以模拟掷骰子的过程(如果需要,您可以更改这个数字)。之后,骰子将其服装设置为由rand1指定的数字。回忆一下,每个骰子有六种服装,分别对应数字 1 到 6。这意味着,如果rand1是 5,那么最后的切换服装命令将显示带有五个点的服装。
现在,我们可以为Die2角色创建脚本,这个脚本应该与我们为Die1创建的几乎相同。由于Die2根据rand2来更换服装,您只需要复制Die1的脚本到Die2,然后将rand1替换为rand2。
我们的骰子模拟器现在已经完成,接下来让我们进行测试。点击绿色旗帜图标,查看模拟效果。如果应用程序无法正常运行,请检查文件DiceSimulator.sb2,其中包含正确的程序实现。
试试看 5-1
选择Player角色,并创建一个名为sum的新变量。将此变量的作用域设置为“仅此角色”。修改Player脚本的最后一个积木,使用这个新变量,像这样:

现在选择Die1(或Die2)角色,并查看数据调色板。你能解释为什么在那里看不到sum变量吗?
变量的作用域
与变量相关的另一个重要概念是作用域。变量的作用域决定了哪些角色可以写入(或更改)该变量的值。
ScopeDemo.sb2
你可以在创建变量时指定其作用域,方法是选择在图 5-7 中看到的两种选项之一。选择“仅此角色”会创建一个变量,该变量只能由拥有它的角色进行更改。其他角色仍然可以读取和使用该变量的值,但不能写入它。图 5-11 中的示例说明了这一点。
图 5-11。只有Cat角色可以写入count。
在这个图中,Cat角色有一个名为count的变量,其作用域为“仅此角色”。Penguin角色可以使用来自传感器调色板的企鹅的 x 位置积木读取count。当你选择Cat作为此积木的第二个参数时,第一个参数将允许你选择Cat角色的一个属性,包括其变量之一。
然而,Scratch 并没有提供允许Penguin角色更改count变量的积木。因此,Penguin角色无法篡改count,避免了对Cat角色脚本运行造成不良影响。对于那些只能由单一角色更新的变量,最好使用“仅此角色”作用域。
使用“仅此角色”作用域创建的变量称为局部作用域,它们可以称为局部变量。不同的角色可以使用相同的名称来命名其局部变量而不会发生冲突。例如,如果你在赛车游戏中有两个赛车角色,每个赛车角色可能都有一个名为speed的局部变量,用于决定赛车在舞台上的运动速度。每个赛车角色可以独立更改其speed变量。这意味着,如果你将第一辆车的speed设置为 10,第二辆车的speed设置为 20,第二辆车应该比第一辆车移动得更快。
变量的数据类型
到这个时候,你可能会想,“Scratch 是如何知道变量的数据类型的?”答案是,它并不知道!当你创建一个变量时,Scratch 并不清楚你打算将该变量用于存储数字、字符串还是布尔值。任何变量都可以存储任何数据类型的值。例如,以下所有命令在 Scratch 中都是有效的。

你需要自行在变量中存储正确的值。然而,正如我在本章前面所描述的,Scratch 会根据上下文尝试在数据类型之间进行转换。要查看当你在变量中存储错误的数据类型时会发生什么,考虑以下两个例子:

由于移动命令期望一个数字参数,Scratch 会在将值传递给移动命令之前,自动尝试将side变量中存储的值转换为数字。在第一个脚本(左边)中,Scratch 无法将字符串“胡说八道”转换为数字。Scratch 不会显示错误消息,而是默默地将转换结果设置为 0,并将此值传递给移动命令。结果,精灵不会移动。另一方面,在第二个脚本(右边)中,Scratch 忽略了字符串中的空格,并将结果数字传递给移动模块,因此精灵向前移动了 100 步。请注意,如果目标模块期望的是一个字符串而不是数字,Scratch 会按原样传递字符串,包括空格。
另一方面,具有“所有精灵”作用域的变量可以被应用程序中的任何精灵读取和更改。这些变量通常被称为全局变量,对于精灵之间的通信和同步非常有用。例如,如果一个游戏有三个按钮,允许用户选择要玩的关卡,你可以创建一个名为gameLevel的全局变量,并让每个按钮精灵在被点击时将该变量设置为不同的数字。然后,你可以通过检查gameLevel轻松找出用户的选择。
选择“所有精灵”选项还会在图 5-7 中启用“云变量”复选框。这个功能允许你将变量存储在 Scratch 的服务器上(云端)。云变量的模块前面有一个小方块,用以与常规变量区分开来,像这样:

任何查看你在 Scratch 网站上分享的项目的人都可以读取该项目中的云变量。例如,如果你分享一个游戏,你可以使用云变量来跟踪所有玩家中记录的最高分。score云变量应几乎立即更新,供所有与游戏互动的人查看。因为这些变量存储在 Scratch 服务器上,即使你退出浏览器,它们的值也会保持不变。云变量使得创建调查和其他需要随时间存储数字的项目变得容易。
现在你已经理解了作用域,接下来是学习如何更新变量——然后使用这些知识来创建更有趣的程序。
更改变量
Scratch 提供了两个命令模块,可以让你修改变量。set to命令直接将一个新值赋给变量,而不管该变量当前的值是什么。另一方面,change by命令则用于根据变量当前的值,通过指定的数量来改变变量的值。图 5-12 中的三个脚本演示了如何使用这些命令以不同方式实现相同的结果。
图 5-12。更改变量值的三种方法
图中的三个脚本都首先将两个变量sum和delta的值分别设置为 0 和 5。第一个脚本使用change命令通过delta的值改变sum的值(即,从 0 变为 5)。第二个脚本使用set命令将当前sum的值加上delta的值(0 + 5),并将结果(5)存回sum。第三个脚本借助一个名为temp的临时变量实现相同的结果。它将sum的值加到delta上,将结果存储在temp中,最后将temp的值复制到sum中。
执行图 5-12 中的任何脚本后,sum将包含数字 5,因此这些脚本在功能上是等效的。请注意,第二个脚本中使用的方法是一种常见的编程实践,我建议你花一点时间研究它,熟悉这一方法。现在,让我们看看change命令的实际应用。
蜘蛛网
SpiderWeb.sb2
我们可以通过绘制几个逐渐增大的六边形来创建一个蜘蛛网,如图 5-13 所示。Triangle 程序绘制一个边长可变的等边三角形,而 Hexagon 程序则调用 Triangle 六次,每次调用后右转 60°(即 360° / 6)。该图清楚地展示了六边形是如何由六个三角形组成的。
图 5-13. 通过绘制几个逐渐增大的六边形来创建蜘蛛网
SpiderWeb 程序仅仅是重复调用 Hexagon,每次传递不同的 sideLength 变量值。这就产生了你在图中看到的同心六边形(即具有相同中心)。注意在 repeat 循环内部如何使用 change 命令来设置 sideLength 的值。复现 SpiderWeb 程序,运行它,看看它是如何工作的。
Pinwheel
Pinwheel.sb2
这个例子与前一个类似,唯一不同的是这次我们将使用一个变量来控制三角形重复的次数。最终的程序(称为 Pins)如图 5-14 所示。图中的 Pinwheel 程序的工作原理与上面的 SpiderWeb 程序相似,但我们还在每次循环时改变画笔的颜色,以达到有趣的彩虹效果。图中展示了不同针数下的 Pinwheel 程序输出。试着用这个程序进行实验,看看你还能创造什么。
图 5-14. 通过旋转等边三角形多次创建风车
现在我们已经探讨了变量的基础知识,你可能会想知道,当你复制一个精灵时,变量会发生什么?复制品是否共享父精灵的变量,还是拥有自己的副本?克隆是否可以访问全局变量?我们将在下一节回答这些问题。
尝试一下 5-2
修改 Pinwheel 程序,使精灵隐藏。这应该会让你在观察绘图时不会被精灵阻碍。
克隆中的变量
每个精灵都有一份与之相关的属性列表,包括它当前的x位置、y位置、方向等。你可以将这份列表想象成一个背包,里面装着精灵属性的当前值,如图 5-15 所示。当你为精灵创建一个作用域为“仅此精灵”的变量时,这个变量会被添加到精灵的背包中。
当你克隆一个精灵时,克隆会继承父精灵的属性副本,包括它的变量。继承的属性在克隆创建时会与父精灵的属性完全相同。但此后,如果克隆的属性和变量发生变化,这些变化不会影响父精灵。父精灵的后续变化也不会影响克隆的属性。
图 5-15. 克隆继承父精灵的变量副本
举例来说,假设父精灵拥有一个名为speed的变量,其当前值为 10。当你克隆父精灵时,新的精灵也会有一个名为speed的变量,值为 10。之后,如果父精灵将speed更改为 20,克隆中的speed值将保持为 10。
CloneIDs.sb2
你可以在应用程序中使用这个概念来区分克隆。例如,看看图 5-16 中的程序。
图 5-16. 使用变量区分克隆
在这个例子中,父精灵拥有一个名为cloneID的变量。当点击绿色旗帜时,它会启动一个循环来创建三个克隆,并在创建克隆之前将cloneID设置为不同的值(在这个例子中为 1、2 或 3)。每个克隆都会拥有自己的cloneID副本,并初始化为不同的值。你现在可以使用if语句块,下一章我们将深入学习它,来检查克隆的 ID,并让它执行相应的操作。
ClonesAndGlobalVars.sb2
现在,让我们讨论克隆如何与全局变量互动。回想一下图 5-15,变量的作用范围为“所有精灵”的变量可以被舞台和所有精灵读取和写入,包括克隆。例如,程序图 5-17 就是利用这一点来检测父精灵的所有克隆何时消失。
图 5-17. 使用全局变量追踪克隆被删除的时刻
在这个脚本中,父精灵将全局变量numClones设为 5,并创建了五个克隆。然后,它等待numClones变为 0,才宣布游戏结束。这些克隆会在舞台上的随机时间和位置出现,喊一声“Hello!”持续两秒钟,然后消失。在一个克隆被删除之前,它会将numClones减去 1。当所有五个克隆消失后,numClones变为 0,主脚本停止等待,原始精灵说“Game Over!”。
在接下来的部分中,你将学习变量的监视器,它们让你可以看到甚至更改变量当前存储的值。能够在舞台上查看和更改变量,将为你创建全新的应用程序类型打开大门。
显示变量监视器
你经常会发现自己想查看一个变量当前存储的值。例如,当你的某个脚本没有按预期工作时,你可能希望追踪一些变量,看看它们是否正确变化。使用变量监视器可以帮助你完成这个调试任务。
你可以将 Scratch 变量显示为变量监视器。选中或取消选中变量名称旁边的框,可以在舞台上显示或隐藏变量的监视器,如图 5-18 所示。你也可以在脚本中使用show variable和hide variable命令控制监视器的可见性。
图 5-18. 通过勾选名称旁的框来显示变量的监视器。
监视器可以作为输出或控制使用,分别显示或允许你更改变量的内容。双击舞台上的监视器框可以选择常规输出(默认状态)、大输出或滑块控制。当你选择显示滑块时,可以通过右键点击滑块并从弹出菜单中选择设置滑块最小值和最大值选项来设置其范围,如图 5-19 所示。
图 5-19. 设置滑块模式下监视器的最小值和最大值
StageColor.sb2
使用滑块可以在脚本运行时改变变量的值,这是用户与您的应用程序交互的一种便捷方式。您可以在图 5-20 中看到一个使用滑块控制的简单示例。
图 5-20. 使用滑块调整舞台颜色
在此示例中,拖动滑块的滑块柄会改变stageColor变量的值,该变量是设置颜色效果为命令中的一个参数。假设此脚本属于舞台,拖动滑块应改变舞台的背景颜色。
注意
一个变量的监视器还表示它的作用范围。如果一个变量属于某个角色,它的监视器应在变量名之前显示角色的名称。例如,监视器Cat speed 0表示speed属于Cat。如果speed变量是全局变量,那么它的监视器只会显示speed 0。这两种情况的区别如下图所示。

在应用程序中使用变量监视器
现在您已经了解了变量监视器的基础,我将向您展示一些使用它们为您的 Scratch 应用程序增加额外功能的方法。
使用显示器作为显示器和控制器的功能为广泛的应用程序开辟了大门,包括游戏、模拟和交互式程序。接下来让我们在以下小节中探索一些利用显示器的例子。
模拟欧姆定律
OhmsLaw.sb2
我们的第一个示例是欧姆定律的模拟。当电压(V)加在电阻(R)上时,会有电流(I)通过该电阻。根据欧姆定律,电流的大小由以下公式给出:

我们的应用程序允许用户使用滑块控制来改变V和R的值。然后,它会计算并显示相应的电流值I。此应用程序的用户界面显示在图 5-21 中。
图 5-21. 欧姆定律应用的用户界面
电池电压滑动条(V)的范围是[0, 10],而电阻滑动条(R)的范围是[1, 10]。当用户通过滑动条改变V或R时,应用计算电路中流动的电流(I)的对应值。灯泡的亮度与通过它的电流值成正比:电流越大,灯泡越亮。图中V、I和R字母的大小也会变化,以表示这些量的相对值。
总的来说,应用有五个角色(命名为Volt、Current、Resistance、Equal和Light)和三个变量(命名为V、I和R)。你在图 5-21 中看到的其他一切(如电池、电线、插座等)都是舞台背景图的一部分。驱动该应用的主脚本,属于舞台,见图 5-22。
图 5-22. 欧姆定律应用的主脚本
脚本初始化了V和R的值,然后进入一个无限循环。在每次循环中,它使用当前由用户通过滑动条控制设置的V和R值来计算I。然后,它广播一条消息给应用中的其他角色,更新它们根据计算结果的外观。图 5-23 显示了当Volt、Current、Resistance和Light角色(分别显示字母V、I、R和灯泡)接收到Update消息时的反应。
图 5-23. 响应Update消息触发的脚本
当接收到Update广播时,Volt、Current和Resistance角色根据各自变量的当前值改变大小(从原始大小的 100%到 200%)。Light角色执行设置幽灵效果为命令,根据I的值改变其透明度。这为灯泡提供了一个逼真的视觉效果,模拟了实际的灯泡。
试试看 5-3
打开欧姆定律模拟器运行它,并研究脚本以了解其工作原理。如果你在Light角色的脚本末尾添加改变颜色效果为 25命令,你认为会发生什么?实施这个更改以检查你的答案。你认为有哪些方法可以增强这个应用?
演示串联电路
SeriesCircuit.sb2
我们的第二个示例模拟了一个包含电池和三个串联连接的电阻的电路。用户可以通过滑动条改变电池电压以及电阻值。流经电阻的电流和三个电阻两端的电压通过大显示屏显示。你可以在图 5-24 中看到该应用程序的界面。(注意,电阻上的色带并不代表电阻的实际值。)
图 5-24. 展示串联电路的应用程序
控制该电路操作的方程式如下所示。我们可以通过将电池电压 V 除以三者电阻之和来计算电路中的电流。之后,计算每个电阻两端的电压时,使用电流与该电阻值的乘积:
| 总电阻:R[tot] = R[1] + R[2] + R[3] |
|---|
| 电路中的电流:I = V ÷ R[tot] |
| R[1] 两端电压:V[1] = I × R[1] |
| R[2] 两端电压:V[2] = I × R[2] |
| R[3] 两端电压:V[3] = I × R[3] |
该应用程序没有精灵,但当点击绿色旗帜时,舞台上执行的脚本如图 5-25 所示。
这个脚本为我们处理数学运算,并将结果显示在舞台上的显示屏上。注意,虽然电阻 R2 和 R3 的滑动条可以在 0 到 10 之间变化,但 R1 的最小值被故意设置为 1。这确保了 Rtot 始终大于 0,避免在计算电流时除以 0。
图 5-25. 点击绿色旗帜时运行的脚本
这个应用程序的大部分工作集中在设计界面上(即舞台背景)。之后,我们只需将显示器和滑动条放置在舞台上的正确位置。
试试看 5-4
SeriesCircuitWithSwitch.sb2
打开串联电路模拟器应用程序并运行。尝试不同的R1、R2、R3和V值。当你拖动滑块控制时,观察V1、V2和V3的计算值。电压总和(V1 + V2 + V3)和电池电压之间有什么关系?这告诉你串联电路中的电压关系是什么?你可以通过添加一个开关图像来进行有趣的增强,该开关能够打开或关闭电路,如下所示。当开关打开时,电路中不会有电流流动。尝试使用下面给出的提示来实现这个更改。

可视化球体的体积和表面积
Sphere.sb2
我们的第三个示例是一个用于计算球体体积和表面积的互动应用程序。用户通过点击用户界面上的一些按钮来改变球体的直径,应用程序会自动计算并显示相应的体积和表面积。为了使应用程序更具吸引力,显示在舞台上的球体大小也会根据所选的直径按比例变化。该应用程序的用户界面如图 5-26 所示。
图 5-26. 球体应用程序的用户界面
应用程序包含三个精灵:两个箭头按钮(分别命名为Up和Down)和一个球体图像(命名为Sphere)。这两个按钮的脚本会广播消息,指示它们已被点击,如图 5-27 所示。
图 5-27. Up和Down精灵的脚本
Sphere精灵有九个服装,分别代表直径为 1、1.25、1.5、1.75、...、3 的球体。当这个精灵接收到Up或Down广播消息时,它会执行如图 5-28 所示的脚本。
图 5-28. 由Up和Down消息触发的脚本
精灵切换其服装,然后调用重新计算过程来更新体积和表面积的计算。注意,这些脚本使用当前服装的值来确定球体是否已经达到最大或最小尺寸,从而确保对上和下按钮的有效响应。我将在下一章详细讲解if块,但现在,让我们先讨论球体的重新计算过程,如图 5-29 所示。
图 5-29. 重新计算 过程
首先,直径变量的值根据以下公式设置:
直径 = 1 + 0.25 × (服装编号 – 1)
由于服装编号的范围从 1 到 9,直径变量的相应值将为 1、1.25、1.50,...、2.75、3,这是我们想要的结果。
该脚本通过将直径除以 2 来找到半径r。然后,它使用图 5-26 中显示的公式计算球体的体积和表面积。计算出的值将自动显示在舞台上的相应显示器上。
尝试一下 5-5
打开应用程序并运行它。研究脚本以理解应用程序的工作原理。向球体精灵添加脚本,使其在应用程序运行时旋转并改变颜色。作为另一个练习,修改原始程序,使用一个服装来代替球体精灵,并使用改变大小模块来改变球体的大小。缩放后的图像可能看起来不太好,但除此之外,应用程序的功能应该完全相同。
绘制 n 叶玫瑰
N-LeavedRose.sb2
在这个例子中,我们将创建一个在舞台上绘制具有多个叶片的玫瑰的应用程序。绘制玫瑰的过程可以分解为以下步骤:
-
从舞台的原点开始。
-
将精灵指向某个方向。按照惯例,希腊字母θ(发音为theta)表示一个角度,因此我们将精灵方向的变量命名为
theta。 -
移动精灵
r步并在舞台上绘制一个点。之后,抬起画笔并返回原点。 -
按一定的角度
theta(我们使用 1°)进行变化,并重复步骤 2-4。距离
r和角度theta之间的关系由以下公式给出:r = a × cos(n × θ)
其中a是一个实数,n是一个整数。这个方程生成一个玫瑰,其大小和叶子数量分别由a和n控制。该方程还涉及余弦三角函数(cos),你可以在Operators调色板中找到它作为一个报告块(检查sqrt块)。给定a和n的值,我们只需要为theta选择不同的值,计算出相应的r值,并在舞台上标出结果点。这个示例的用户界面如图 5-30 所示。
图 5-30. n叶玫瑰应用的用户界面
该应用程序包含两个角色:第一个角色具有Redraw按钮服装,第二个角色(名为Painter)是一个隐藏的角色,用于绘制玫瑰。用户通过滑块控制器调整n值来设定所需的叶子数量,然后点击Redraw按钮来绘制玫瑰。当用户点击该按钮时,按钮角色仅广播一个Redraw消息。当Painter角色接收到该消息时,它将执行图 5-31 中显示的脚本。
图 5-31. 在舞台上绘制带有n个叶子的玫瑰的Redraw程序
脚本首先设置笔的颜色和大小,并清除舞台上先前的笔迹。然后,它将变量a设置为 100,并调用Rose程序,该程序将通过一个循环执行 360 次,在舞台上绘制玫瑰。在每次循环中,程序指向theta方向,移动r步,并在该位置绘制笔迹。接着,它将theta增加 1°,为下一次repeat循环做好准备。
图 5-32 展示了为不同n值创建的一些玫瑰。你能找出n值与叶子数量之间的关系吗?
图 5-32. 由Rose程序创建的一些玫瑰 尝试 5-6
打开应用程序并运行。更改n的值,看看你能用玫瑰程序创造出什么。向应用程序添加另一个滑块,以便用户可以更改a的值并根据需要修改脚本。你还可以修改玫瑰程序,将a作为参数传递。(请参阅传递参数给自定义模块,复习如何向程序添加参数。)
模拟向日葵种子分布
Sunflower.sb2
生物学家和数学家已广泛研究植物茎部叶子的排列方式。让我们通过研究一种几何模型来探讨一下植物学,看看如何用螺旋种子模式表示花朵。特别地,我们将编写两个方程来模拟向日葵种子的分布。为了绘制向日葵的第n颗种子,我们将遵循以下步骤:
-
将精灵朝* n * × 137.5°的方向指向。
-
移动一个距离
,其中c是常数缩放因子(在我们的示例中设置为 5)。 -
在舞台的最终位置绘制一个点。
我们将为每颗要绘制的种子重复这些步骤。对于第一颗种子,我们设置n = 1;对于第二颗种子,我们设置n = 2;依此类推。如果第一步使用与 137.5°不同的角度,将会得到不同的种子排列。如果你对这些方程感兴趣并想了解更多向日葵种子模式的内容,可以参考 Przemyslaw Prusinkiewicz 和 Aristid Lindenmayer 的《植物的算法美学》(Springer-Verlag, 2004),特别是第四章,你可以在书籍的官方网站上找到它,* algorithmicbotany.org/papers/#abop*。
我们的应用程序将生成类似于该研究中描述的模式,您可以在图 5-33 中看到其中的一些模式。
图 5-33. 使用不同角度生成的向日葵模式
这个示例的界面包含一个滑块控件,可以将角度的值从 137°更改为 138°,并且每次增量为 0.01°,同时有一个标记为Redraw的按钮。当用户点击该按钮时,它会向Painter精灵广播消息,精灵会执行图 5-34 中显示的脚本。
Sunflower过程执行一个循环,绘制 420 个种子,虽然你可以根据需要更改这个数字。在每次循环迭代中,过程会计算种子角度①,移动
②步长,然后在该位置绘制一个笔迹。接着,过程递增n,表示种子编号,为绘制下一个种子做准备。
图 5-34. Painter精灵的脚本
我在本节中展示的脚本只是通过使用变量和监视器所能创建的一些惊人应用的示例。通过滑块控件让用户与我们的应用程序进行交互,只是互动应用程序新型发展的起点。在接下来的章节中,你将学习如何创建直接提示用户输入的脚本。
尝试操作 5-7
打开应用程序并运行它。更改角度的值,看看你还能用Sunflower过程创建什么。研究这个过程,理解它是如何工作的,然后想出一些方法来增强它。
从用户获取输入
GettingUserInput.sb2
假设你想创建一个教孩子们基础算术的游戏。你的游戏可能会有一个精灵,显示一个加法题目并要求玩家输入答案。你如何读取玩家的输入,以查看答案是否正确?
Scratch 的Sensing调色板提供了一个命令块,ask and wait,你可以用它来读取用户输入。这个命令块接受一个参数,指定一个字符串,通常是一个问题,以便展示给用户。如图 5-35 所示,执行该命令块时,输出会根据精灵的可见状态(即精灵是显示还是隐藏)有所不同。在图 5-35(右)中显示的输出,执行来自舞台脚本的ask and wait命令时也会出现。
图 5-35. ask and wait命令块的输出可能会根据执行它的精灵是否显示而有所不同。
在执行ask and wait命令后,调用脚本会等待用户按下 ENTER 键或点击输入框右侧的勾选框。当发生此操作时,Scratch 会将用户的输入存储在answer块中,并在ask and wait块后面的命令中继续执行。为了看到这个命令块的实际操作,看看以下示例,说明如何使用它。
读取一个数字
AskAndWait.sb2
图 5-36 中的脚本会询问用户她的年龄,等待答案,并告诉用户她 10 年后会几岁。
图 5-36. 一个接受用户年龄输入的脚本
该图展示了当用户输入18并按下键盘上的 ENTER 键时程序的输出。注意,程序使用了join块(来自运算符调色板)来连接(即拼接)两个字符串。
读取字符
AskAndWait2.sb2
图 5-37 中的脚本会询问用户他的姓名首字母,然后根据用户的回答构造并显示问候语。
图 5-37. 一个使用两个变量读取并存储用户姓名首字母的脚本
该程序使用两个变量(firstInitial 和 lastInitial)来保存用户输入的值。当用户分别在两个提示框中输入字母M和S时,你可以看到程序的最终输出。注意,程序使用了嵌套的join块来构造问候语。你可以使用这种技术在应用程序中创建各种字符串,并显示定制的消息。
执行算术操作
AskAndWait3.sb2
图 5-38 中的脚本会要求用户输入两个数字。然后,它计算这两个数字的乘积,并使用say命令在语音气泡中显示答案。与之前的示例一样,脚本使用了两个变量(num1和num2)来存储用户输入的值。
图 5-38. 根据用户输入计算一个值
该图展示了当用户分别输入 9 和 8 作为对两个提示的回应时程序的输出。同样,注意我将join块嵌套在一起,用来构造输出字符串。
我在本节中展示的例子展示了几种使用询问并等待块来编写接受用户输入并解决各种问题的脚本。例如,你可以编写一个程序,求解形如 ax² + bx + c = 0 的二次方程的根,其中 a、b 和 c 的值由用户输入。然后,你可以使用这个程序检查你自己对方程的答案。我希望这能给你一些启示,告诉你如何使用这个强大的块来解决可能出现的任何数学问题。
摘要
变量是编程中最重要的概念之一。变量是计算机内存中存储单一值(如数字或字符串)的区域的名称。
在本章中,你学习了 Scratch 支持的基本数据类型以及对这些类型进行的操作。接着,你学习了如何创建变量并使用它们存储数据。
你还实现了几个实际应用,利用变量展示不同的功能。你探索了变量的监视器,并用它们创建了不同类型的互动程序。最后,你学会了如何使用询问并等待块来提示用户输入并在程序中处理用户的响应。
在下一章中,你将了解更多关于布尔数据类型的知识,以及它在决策中的基本作用。你还将学习if和if/else块,并使用它们为你的 Scratch 程序增加更高层次的智能。所以,卷起袖子,准备迎接另一个激动人心的章节吧!
问题
| 问: | 1. 创建一个实现以下指令的脚本:
-
将
speed变量设置为 60(英里/小时)。 -
将
time变量设置为 2.5(小时)。 -
计算行驶的距离并将答案保存在
distance变量中。 -
向用户显示计算出的距离,并附上适当的消息。
|
| 问: | 2. 以下每个脚本的输出是什么?复制这些脚本并运行它们以测试你的答案。![]() |
|---|---|
| 问: | 3. 在右侧脚本的重复循环的每次迭代结束时,X 和 Y 的值分别是多少?复制脚本并运行以检查你的答案。![]() |
| 问: | 4. 设 x 和 y 为两个变量。创建与以下语句等效的功能块:
-
将 5 加到 x 并将结果存储在 y 中。
-
将 x 乘以 3,并将结果存储在 y 中。
-
将 x 除以 10,并将结果存储在 y 中。
-
从 x 中减去 4,并将结果存储在 y 中。
-
将 x 平方,结果加上 y,并将结果存储回 x 中。
-
将 x 设置为 y 的两倍加上 y 的立方的三倍。
-
将 x 设置为 - y 的平方。
-
将 x 设置为 x 和 y 之和除以 x 和 y 的积的结果。
|
| 问: | 5. 编写一个程序,要求用户输入一个冠词、一个名词和一个动词。程序将根据输入的内容创建一个句子,形式为 冠词 名词 动词。 |
|---|---|
| 问: | 6. 编写一个程序,要求用户输入一个摄氏度的温度。程序将把该温度转换为华氏度,并以适当的消息将结果显示给用户。(提示:F° = (1.8 × C°) + 32。) |
| 问: | 7. 当电流 I 流过电阻 R 时,电阻耗散的功率 P 为 I ² × R。编写一个程序,读取 I 和 R 并计算 P。 |
| 问: | 8. 编写一个程序,读取直角三角形两边的长度,并计算斜边的长度。 |
| 问: | 9. 编写一个程序,提示用户输入一个盒子的长度(L)、宽度(W)和高度(H)。程序将计算并显示该盒子的体积和表面积。(提示:体积 = L × W × H;表面积 = 2×[(L × W) + (L × H) + (H × W)]。) |
| 问: | 10. 三个电阻(R[1]、R[2] 和 R[3])并联时的等效电阻 R 可通过以下公式计算:1/R = 1/R[1] + 1/R[2] + 1/R[3]。编写一个程序,读取 R[1]、R[2] 和 R[3]* 的值,并计算 R。 |
| 问: | 11. 完成本章前面介绍的打地鼠游戏。文件 Whac-a-Mole.sb2 包含此程序的部分实现。当点击绿色旗帜时,提供的脚本会启动一个循环,随机移动 Cat 精灵到各个洞口。为 Cat 和舞台(Stage)分别添加两个脚本,以适当改变两个变量(hits 和 misses)的值。你还可以尝试添加一些音效,使游戏更有趣!此外,可以增加一个条件,当计时器到达或失误次数达到一定值时结束游戏。![]() |
第六章:做出决策
本章将教你使用 Scratch 工具编写程序,这些程序能够比较值、评估逻辑表达式,并根据结果做出决策。我们还将通过几个有用的示例应用来帮助理解。以下是你将在过程中学习的内容:
-
基本问题解决技巧
-
如何使用 if 和 if/else 块在多个备选操作中做出选择
-
如何构建逻辑表达式以评估给定条件
-
分支语句中的控制流
到目前为止,我们编写的程序遵循简单的执行模型。它们从第一条指令开始,执行该指令,接着执行下一条指令,一直到程序结束。这些程序的命令块按顺序执行,不会跳过或跳转。
然而,在许多编程场景中,你可能希望改变这种顺序执行的程序流程。如果你正在编写一个应用程序来辅导孩子们学习基础算术,你可能希望执行某些代码块来奖励正确答案,并为错误答案执行完全不同的一组代码块(例如,揭示正确答案或提供另一机会)。你的脚本可以通过将学生的输入与正确答案进行比较来决定接下来该做什么。这就是所有决策任务的基础。
在本章中,我们将探讨 Scratch 中的决策命令,并编写几个使用这些命令来测试输入并执行不同操作的程序。
首先,我将向你介绍 Scratch 的比较运算符,并展示如何用它们来比较数字、字母和字符串。接着,我会介绍 if 和 if/else 块,并解释它们在决策中的关键作用。你还将学习如何使用嵌套的 if 和 if/else 块测试多个条件,并编写一个基于菜单的程序来实现这些块的功能。之后,我将介绍逻辑运算符,作为测试多个条件的另一种方式。在最后一部分,我们将编写几个有趣的程序,基于你到目前为止所学的所有概念。
比较运算符
你每天都会做出决策,每个决策通常都会引导你执行某些操作。例如,你可能会想,“如果那辆车的价格低于 2000 美元,我就买它。”然后你会询问这辆车的价格,决定是否购买它。
你也可以在 Scratch 中做出决策。通过使用比较运算符,你可以比较两个变量或表达式的值,判断一个是否大于、少于或等于另一个。比较运算符也叫做关系运算符,因为它们测试两个值之间的关系。Scratch 支持的三个关系运算符见于表 6-1。
表 6-1. Scratch 中的关系运算符
| 运算符 | 含义 | 示例 |
|---|---|---|
![]() |
大于 | ![]() price是否大于 2000? |
![]() |
小于 | ![]() price是否小于 2000? |
![]() |
等于 | ![]() price是否等于 2000? |
现实世界中的布尔值
布尔这个词源于 19 世纪英国数学家乔治·布尔,他发明了一种基于两个值:1 和 0(或真与假)的逻辑系统。布尔代数最终成为现代计算机科学的基础。
在现实生活中,我们经常使用布尔表达式来做出决策。计算机也用它们来确定程序执行的分支。机器人臂可能被编程用于检查流水线上的移动零件,并在goodQuality = true时将每个零件移动到 Bin 1,或者在goodQuality = false时将其移动到 Bin 2。家庭安全系统通常会被编程为在输入错误密码时发出警报(correctCode = false),或者在输入正确密码时停用警报(correctCode = true)。当你在百货商店刷卡时,远程服务器可能根据你的卡是否有效(true)或无效(false)来决定是否授予或拒绝访问。你车上的一台计算机会在发生碰撞时自动弹出安全气囊(collision = true)。当手机电池电量低时,可能会显示警告图标(batteryLow = true),当电池电量正常时,移除该图标(batteryLow = false)。
这些只是计算机通过检查布尔条件结果来触发不同动作的几个例子。
请注意,表 6-1 中的所有块都是六边形形状。正如你可能还记得的第五章,这意味着评估这些块的结果是一个布尔值,可以是“真”或“假”。因此,这些表达式也被称为布尔表达式。
例如,表达式price < 2000测试变量price的值是否小于 2000。如果price小于 2000,块将返回(或计算为)真;否则,返回假。你可以使用这个表达式构建决策条件:“如果(price < 2000),则购买汽车。”
在我们查看if块之前,先来看一个简单的例子,说明布尔表达式是如何在 Scratch 中被评估的。
评估布尔表达式
假设我们设置了两个变量,x 和 y,如下所示:x = 5,y = 10。表 6-2 展示了一些使用 Scratch 关系块的例子。
这些例子揭示了关于关系运算符的几个重要点。首先,我们可以使用它们来比较单个变量(如 x、y)和完整的表达式(如 2 * x 和 x + 6)。其次,比较的结果总是 true 或 false(即布尔值)。第三,x = y 块并不意味着“将 x 设置为 y”。它是在问:“x 是否等于 y?”因此,当执行语句 set z to (x = y) 时,x 的值仍然是 5。
表 6-2. 关系运算块示例
| 语句 | 含义 | z(输出) | 解释 |
|---|---|---|---|
![]() |
z = is(5 < 10)? |
z = true |
因为 5 小于 10 |
![]() |
z = is(5 > 10)? |
z = false |
因为 5 不大于 10 |
![]() |
z = is(5 = 10)? |
z = false |
因为 5 不等于 10 |
![]() |
z = is(10 > 2*5)? |
z = false |
因为 10 不大于 10 |
![]() |
z = is(5 = 5)? |
z = true |
因为 5 等于 5 |
![]() |
z = is(10 < 5 + 6)? |
z = true |
因为 10 小于 11 |
比较字母和字符串
假设我们玩一个游戏,玩家需要猜一个从 A 到 Z 的单个字母的秘密代码。游戏会读取玩家的猜测,将其与秘密代码进行比较,并根据字母的字母顺序指导玩家调整猜测。如果秘密字母是 G,而玩家输入了 B,游戏应该会告诉玩家类似“在 B 之后”的提示,表示秘密代码在字母 B 后面。我们如何比较正确的字母和玩家的输入,来决定显示什么消息呢?
幸运的是,Scratch 中的关系运算符也可以用来比较字母。如图 6-1 所示,Scratch 会根据字母的字母顺序来比较字母。因为字母 A 在字母表中排在字母 B 之前,所以表达式 A < B 会返回 true。然而,需要注意的是,这些比较不区分大小写;大写字母 A 和小写字母 a 被视为相同。因此,表达式 A = a 也会返回 true。
图 6-1. 使用关系运算符比较字母
知道这些信息后,你可以使用以下条件语句来测试玩家的猜测:
IF (answer = secretCode), then say Correct
IF (answer > secretCode), then say Before <answer>
IF (answer < secretCode), then say After <answer>
条件语句是一种形式为“如果条件为真,则执行此操作”的语句。在下一节中,我将教你如何在 Scratch 中实现条件语句,但我们先通过猜字游戏进一步探讨关系运算符。
如果密钥包含多个字母怎么办?例如,玩家可能需要猜测一种动物的名称。你仍然可以使用 Scratch 的关系运算符进行比较吗?幸运的是,简短的回答是肯定的:你可以使用 Scratch 的关系运算符比较字符串。那么,Scratch 如何处理像elephant > mouse这样的比较呢?图 6-2 中的示例说明了比较字符串的结果。
图 6-2. 使用关系运算符比较 ① 完全相同的字符串,② 仅在大小写上不同的字符串,③ 一个字符串与另一个包含多余空格的字符串,④ 根据字母的字典顺序变化的字符串
细致研究图 6-2 显示了以下内容:
-
Scratch 在比较字符串时不区分大小写。例如,在②中,字符串“HELLO”和“hello”被认为是相等的。
-
Scratch 在比较中会计算空格。字符串“HELLO”前后各有一个空格,它与字符串“HELLO”③不同。
-
在比较字符串“ABC”和“ABD”时,如④所示,Scratch 首先比较两字符串的第一个字符。由于它们相同(此例中为字母A),Scratch 会继续检查两字符串的第二个字符。由于这个字符在两个字符串中也是相同的,Scratch 会继续检查第三个字符。由于字母C小于字母D(即C在字母表中排在D前面),因此 Scratch 认为第一个字符串小于第二个字符串。
了解这一点后,你就不会感到惊讶,当表达式 elephant > mouse 计算结果为假时,即使真正的象比老鼠大得多。根据 Scratch 的字符串比较规则,字符串“elephant”小于字符串“mouse”,因为字母 e(elephant 中的第一个字母)在字母 m(mouse 中的第一个字母)之前出现在字母表中。
基于字符的字母顺序对字符串进行比较和排序,广泛应用于许多现实生活中的场景,包括排序目录列表、书架上的书籍、字典中的单词等等。在字典中,单词 elephant 排在单词 mouse 之前,而 Scratch 中的字符串比较也根据这一顺序给出答案。
现在你已经了解了什么是关系运算符以及 Scratch 如何使用这些运算符来比较数字和字符串,接下来是时候学习条件块了。
决策结构
Scratch 的 控制 调色板包含两个块,它们允许你在程序中做出决策并控制操作:if 块和 if/else 块。通过使用这些块,你可以提出一个问题,并根据答案采取行动。在这一部分,我们将详细讨论这两个块,谈论标志,并学习如何使用嵌套的 if 块来测试多个条件。接下来,我将介绍基于菜单的应用程序,并解释嵌套的 if 块如何帮助实现这些应用程序。
if 块
if 块是一个决策结构,它使你能够根据测试条件的结果来指定一组命令是否(或是否不)应执行。if 块的结构及其对应的流程图如 图 6-3 所示。
图 6-3. if 块的结构
在 图 6-3 中,菱形表示一个决策块,它对一个问题给出“是/否”(或“真/假”)的回答。如果 if 块的 头部 中的测试条件为真,程序会执行 正文 中列出的命令,然后再执行 if 块后面的命令(图中的 Command M)。如果测试条件为假,程序会跳过这些命令,直接跳到 Command M。
要查看 if 块的实际应用,请创建并运行 图 6-4 中显示的脚本。该脚本运行一个 forever 循环,移动一个精灵在舞台上,改变其颜色,并使其反弹舞台的边缘。
图 6-4. 此脚本仅在精灵移动到舞台的右半部分时才会改变颜色。
我们脚本中的forever循环包含一个if块,在每次move命令后检查精灵的x位置。如果x位置大于零,精灵应改变其颜色。运行此脚本时,你会发现精灵只有在移动到舞台的右半部分时才会改变颜色。这是因为change color effect by 25块仅在x position > 0条件为真时才会执行。
使用变量作为标志
假设你正在开发一款太空冒险游戏,目标是摧毁一支正在攻击的战舰舰队。玩家扮演舰长,使用键盘上的箭头键操作星际飞船,并通过按空格键发射导弹。如果玩家的星际飞船被敌方攻击命中一定次数,飞船将失去攻击能力。这时,按空格键将不再发射导弹,舰长必须采用防御策略来避免再受攻击。显然,当按下空格键时,程序需要检查星际飞船攻击系统的状态,以决定玩家是否能继续发射。
这种类型的检查通常使用标志来执行,标志是用于表示是否发生了某个感兴趣事件的变量。你可以使用任何两个值来描述事件的状态,但常见做法是使用 0(或 false)表示事件未发生,使用 1(或 true)表示事件已发生。
在你的太空射击游戏中,可以使用一个名为canFire的标志来表示星际飞船的状态。值为 1 表示星际飞船可以发射导弹,值为 0 表示不能。基于此,空格键事件处理程序的代码可能如下所示,如图 6-5 所示。
图 6-5. 使用标志进行条件执行
在游戏开始时,你会将canFire标志的值初始化为 1,以表示星际飞船可以发射导弹。当星际飞船被敌人攻击命中一定次数后,你会将canFire标志设置为 0,表示攻击系统已经失效;此时,按下空格键将不再发射导弹。
尽管你可以根据需要给标志命名,但我建议使用能反映其真/假性质的名称。表 6-3 展示了一些你可能在太空射击游戏中使用的标志示例。
表 6-3. 使用标志的示例
| 示例 | 意图和可能的行动方案 |
|---|---|
![]() |
游戏尚未开始。忽略所有键盘输入。 |
![]() |
游戏已开始。开始处理用户输入。 |
![]() |
游戏尚未结束。显示剩余时间。 |
![]() |
游戏结束。隐藏剩余时间显示。 |
![]() |
星际飞船没有被敌方火力击中。警报关闭。 |
![]() |
星际飞船被导弹击中。播放警报声。 |
现在你已经知道如何使用if块和标志,接下来我们来讨论另一个条件块,它可以让你在某个条件为真时执行一段代码,而当条件为假时执行另一段代码。
if/else 块
假设你正在创建一个游戏,用于教小学学生基础数学。游戏会呈现一个加法问题,然后要求学生输入答案。学生答对了会得一分,答错了则扣一分。你可以使用两个if语句来完成这个任务:
If the answer is correct, add one point to score
If the answer is incorrect, subtract one point from score
你也可以通过将两个if语句合并为一个if/else语句来简化逻辑,从而提高代码效率,代码如下:
If the answer is correct
add one point to score
Else
subtract one point from score
指定的条件会被测试。如果条件为真,if部分的命令将会执行;但如果条件为假,else部分的命令则会执行。程序只会执行if/else块中的其中一组命令。这些替代路径也被称为分支。if/else块的结构及其相应的流程图见图 6-6。
图 6-6。if/else 块的结构
当你想决定午餐吃什么时,可能会使用if/else结构。如果你有足够的钱,你会去一家高级餐厅;否则,你会选择更随意的食物。我们可以将你钱包里的钱叫做availableCash。当你打开钱包时,你就在检查条件availableCash > $20。如果结果为真(你有超过 20 美元),你会去白桌布的餐厅;如果不为真,你就会去附近的汉堡店。
图 6-7 中显示了一个简单的脚本,演示了如何使用 if/else 语句块。这个例子使用了 取模运算符(mod),它返回除法操作的余数,用于判断用户输入的数字是偶数还是奇数。(记住,偶数在除以二时余数为零。)
图 6-7. 这个脚本用于判断用户输入的数字是偶数还是奇数。
图 6-7 展示了用户分别输入 6 和 9 后的两种示例输出。你能解释一下这个脚本是如何工作的吗?
嵌套的 if 和 if/else 语句块
如果你想在执行操作前测试多个条件,可以将多个 if(或 if/else)语句块嵌套在一起,执行所需的测试。例如,考虑图 6-8 中显示的脚本,该脚本用于确定一个学生是否应该获得奖学金。要符合条件,学生必须具备:(1) 平均成绩(GPA)高于 3.8 和 (2) 数学成绩高于 92 分。
图 6-8. 你可以使用嵌套的 if/else 语句块来测试多个条件。
首先,测试表达式 gpa > 3.8。如果这个表达式为假,那么我们不需要检查其他条件,因为学生不符合奖学金的标准。如果表达式 gpa > 3.8 为真,则需要测试第二个条件。这通过嵌套的 if/else 语句块来完成,后者测试条件 mathScore > 92。如果第二个条件也为真,学生将获得奖学金。否则,学生不符合资格,并且会显示一个解释原因的相应消息。
菜单驱动程序
AreaCalculator.sb2
接下来,我们将探讨嵌套 if 语句块的典型用法。特别是,你将学习如何编写程序,向用户提供选择,并根据用户的选择采取行动。
当你启动某些程序时,它们会显示一个可用选项的列表(或菜单),并等待你做出选择。有时,你需要通过输入与期望选项对应的数字来与这些程序交互。这些程序可能使用一系列嵌套的if/else语句块来确定用户的选择并采取相应的行动。为了了解嵌套的if/else语句块是如何工作的,我们将讨论一个应用程序,如图 6-9 所示,它计算不同几何形状的面积。
图 6-9. 区域计算器程序的用户界面
该应用程序的用户界面包含舞台的背景图像,显示可用选项(数字 1、2 或 3),以及Tutor精灵,它会询问用户选择,执行计算并显示结果。主脚本,如图 6-10 所示,在点击绿色旗帜图标时启动。
图 6-10. Tutor 精灵的主脚本
在询问用户输入选择后,Tutor精灵等待用户的输入,并使用三个if/else语句块来处理它。如果用户输入了有效的选择(即 1、2 或 3),脚本会调用相应的过程来计算所选形状的面积。否则,脚本会调用say命令告知用户所输入的选择无效。计算三种形状面积的过程如图 6-11 所示。
图 6-11. 区域计算器程序的过程
每个过程都会要求用户输入对应形状的尺寸,计算面积,并显示结果。例如,矩形过程会要求用户输入矩形的长度和宽度,并将答案分别保存在length和width变量中。然后通过将长度乘以宽度来计算面积,并显示答案。其他两个过程的工作原理类似。
逻辑运算符
在前一节中,你学会了如何使用嵌套的if和if/else块来测试多个条件,但你也可以通过逻辑运算符来实现这一点。使用逻辑运算符,你可以将两个或更多的关系表达式结合起来,得到一个单一的真/假结果。例如,逻辑表达式(x > 5) and (x < 10)由两个逻辑表达式(x > 5和x < 10)组成,它们通过逻辑运算符and结合。我们可以将x > 5和x < 10视为and运算符的两个操作数;只有当两个操作数都为真时,这个运算符的结果才为真。表 6-4 列出了 Scratch 中可用的三种逻辑运算符,并简要解释了它们的含义。
表 6-4. 逻辑运算符
| 运算符 | 含义 |
|---|---|
![]() |
仅当两个表达式都为真时,结果为真。 |
![]() |
如果两个表达式中的任意一个为真,结果为真。 |
![]() |
如果表达式为假,结果为真。 |
现在你已经看过每个运算符的简要概述,我们将逐一详细探讨它们的工作原理。
and 运算符
and运算符接受两个表达式作为参数。如果两个表达式都为真,and运算符返回真;否则返回假。and的真值表,列出了所有可能输入组合下该运算符的输出,如表 6-5 所示。
表 6-5. and 运算符的真值表
| X | Y | ![]() |
|---|---|---|
| true | true | true |
| true | false | false |
| false | true | false |
| false | false | false |
作为使用and运算符的示例,假设我们正在制作一个游戏,在第一关当玩家得分达到 100 时,会获得 200 分的奖励积分。游戏关卡由一个名为level的变量跟踪,得分由一个名为score的变量跟踪。图 6-12 展示了如何通过嵌套的if块①或and运算符②来测试这些条件。
图 6-12. 使用嵌套if块和and运算符检查多个条件
在这两种情况下,只有当两个条件都为真时,才会添加奖励积分。如你所见,and运算符提供了一种更简洁的方式来执行相同的测试。图 6-12 中的if语句块②只有在level等于 1 且score等于 100 时才会执行。如果其中一个条件为假,整个测试结果将为假,将分数增加 200语句块将不会执行。
或运算符
or运算符还接受两个表达式作为参数。如果任一表达式为真,or运算符返回真。只有当两个表达式都为假时,它才返回假。or运算符的真值表见表 6-6。
表 6-6. or 运算符的真值表
| X | Y | ![]() |
|---|---|---|
| true | true | true |
| true | false | true |
| false | true | true |
| false | false | false |
为了演示or运算符的使用,假设某款游戏的玩家在有限的时间内需要到达下一关卡。玩家还从一定量的能量开始,这些能量在玩家通过当前关卡时会逐渐消耗。如果玩家未能在允许的时间内到达下一关卡,或者在到达下一关卡之前消耗了所有分配的能量,游戏就结束。剩余时间由名为timeLeft的变量追踪,玩家当前的能量水平由名为energyLevel的变量追踪。图 6-13 显示了如何使用嵌套的if/else语句块①和or运算符②来测试游戏结束的条件。
图 6-13. 使用嵌套的if语句块和or运算符检查多个条件
请再次注意,or运算符提供了一种更简洁的方式来测试多个条件。图 6-13 中的if语句块②将在timeLeft或energyLevel为 0 时执行。如果这两个条件都为假,整个测试结果将为假,gameOver标志将不会被设置为 1。
非运算符
not运算符只接受一个表达式作为输入。如果该表达式为假,运算符的结果为真;如果表达式为真,结果为假。此运算符的真值表可见于表 6-7。
表 6-7. not 运算符的真值表
| X | ![]() |
|---|---|
| 正确 | 错误 |
| 错误 | 正确 |
回到我们之前假设的游戏场景,假设玩家如果分数未超过 100 分就无法进入下一关。这时使用not运算符是一个不错的选择,如图 6-14 所示。你可以这样理解这段代码:“如果分数不大于 100,执行if块中的命令。”
图 6-14. 使用not 运算符的示例
实际上,如果score变量的值为 100 或更低,测试表达式的结果为真,say命令将会执行。注意,表达式not (score > 100) 等价于 (score ≤ 100)。
使用逻辑运算符检查数值范围
当你需要验证用户输入的数据或筛选掉无效输入时,可以使用逻辑运算符来判断一个数字是否在数值范围内(或外)。表 6-8 展示了一些数值范围的例子。
表 6-8. 表达数值范围
| 表达式 | 值 |
|---|---|
| (x > 10) and (x < 20) | 如果x的值大于 10 且小于 20,结果为真。 |
| (x < 10) or (x > 20) | 如果x的值小于 10 或大于 20,结果为真。 |
| (x < 10) and (x > 20) | 总是错误的。x 不能同时小于 10 又大于 20。 |
尽管 Scratch 没有内建支持≥(大于或等于)和≤(小于或等于)运算符,但你可以使用逻辑运算符来实现这些测试。例如,假设你需要在程序中测试条件x ≥ 10。此不等式的解集如图 6-15 所示①。图中的实心圆表示数字 10 包含在解集中。
测试此条件的一种方法如图 6-15 ② 所示。该图显示了x < 10 的解集,其中空心圆表示相应的点不在解集中。如图所示,补集解(即“x 不小于 10”)等价于 x ≥ 10。另一种执行不等式测试的方法如图 6-15 ③ 所示。显然,如果 x ≥ 10,则 x 要么大于 10,要么等于 10。
图 6-15. 实现不等式 x ≥ 10
表格 6-9 中提供的示例展示了如何使用 Scratch 的关系运算符和逻辑运算符来表示包含 ≥ 和 ≤ 运算符的不等式。
表格 6-9. 测试不等式的示例
| 表达式 | 使用逻辑运算符实现 |
|---|---|
| x ≥ 10 | ![]() |
| x ≥ 10 | ![]() |
| x ≤ 10 | ![]() |
| x ≤ 10 | ![]() |
| 10 ≤ x ≤20 | ![]() |
| 10 ≤ x ≤20 | ![]() |
到目前为止,我们在这一章中已经探讨了几个 Scratch 概念,包括比较、条件语句和逻辑运算符。现在,让我们运用这些知识创建一些有趣且实用的应用程序。
比较十进制数字
在使用等于运算符比较十进制数字时必须特别小心。由于这些数字在计算机内的存储方式,这种比较有时可能不准确。考虑此处显示的命令块:

将 1 除以 3 的结果是 0.3333...,其中 3 会无限重复。由于计算机使用固定的空间来存储结果,分数 1/3 不能被计算机精确存储。尽管 Scratch 在 ① 处告诉你除法结果是 0.33,实际结果内部存储的精度要高得多。因此,图中前两个比较(② 和 ③)的结果为假。
根据你的编程环境,你可以通过使用以下一种方法来防止这种类型的错误:
-
尽可能使用小于(
<)和大于(>)运算符,而不是等于运算符(=)。 -
使用round代码块来四舍五入你需要比较的两个数字,然后比较四舍五入后的数字是否相等。
-
测试你比较的两个值之间的绝对差。例如,代替测试x是否等于y,我们可以检查x和y之间的绝对差是否在可接受的容差范围内,使用一个类似于这个的代码块:
![没有说明的图片]()
根据数字的精度和计算这些数字的方法,这种方法可能足以满足你的需求。
Scratch 项目
本章中你所学到的新命令应该使你能够创建各种有用的 Scratch 应用程序,希望我在本节中展示的项目能够给你一些关于自己项目的灵感。我鼓励你尝试这些应用程序,理解它们是如何工作的,然后思考如何改进它们。
猜我的坐标
GuessMyCoordinates.sb2
在本节中,我们将开发一个互动游戏,用于测试任何人对笛卡尔坐标系的知识。游戏包含一个精灵(称为Star),它代表舞台上的一个随机点(见图 6-16)。
图 6-16. 猜我的坐标界面
每次运行游戏时,精灵会移动到舞台上的不同位置,并要求用户猜测其x和y坐标。游戏检查用户的答案,并提供适当的反馈信息。Star精灵的主要脚本如图 6-17 所示。
该脚本使用两个变量,X 和 Y,来保存精灵的随机坐标。我将在下面解释图 6-17 中的每个编号部分是如何工作的。
-
X变量从集合 {–220, –200, –180, ..., 220} 中随机选择一个值。通过首先选择一个在 –11 到 11 之间的随机整数,然后将结果乘以 20 来实现。同样,Y变量从集合 {–160, –140, –120, ..., 160} 中随机选择一个值。选择的X和Y值确保结果点位于图 6-16 中的网格交点之一。然后,精灵会移动到由X和Y指定的位置。
图 6-17. 猜我的坐标游戏的脚本 -
脚本要求用户输入精灵的 x 坐标,并等待回答。
-
如果答案正确,脚本将进入第 4 步。否则,它将调用 ShowAnswer 程序来显示正确的坐标。
-
当用户输入正确的 x 坐标时,脚本提示用户输入精灵的 y 坐标,并等待回答。
-
如果用户回答正确,脚本会显示消息“做得好”。否则,它会调用 ShowAnswer 来显示正确的坐标。
ShowAnswer 程序见图 6-18。point 变量首先通过 join 操作符构建为一个形如 (X,Y) 的字符串。然后,程序使用 say 命令将正确答案展示给用户。
图 6-18. ShowAnswer 程序试试看 6-1
通过一些有趣的修改来增强这个猜谜游戏。例如,你可以在某人获胜时播放音乐,错误回答时触发蜂鸣声,自动运行(无需每次点击绿旗),或者跟踪正确回答的次数来显示玩家的分数。
三角形分类游戏
TriangleClassification.sb2
如图 6-19 所示,三角形可以根据其边长分类为不等边三角形、等腰三角形或等边三角形。在这一节中,你将探索一个对这些概念进行测试的游戏。
图 6-19. 根据三角形的边分类
游戏在舞台上绘制一个三角形,并要求玩家将该三角形归类为三种类型之一。此游戏的用户界面见图 6-20。
图 6-20. 三角形分类游戏的用户界面
图 6-20 显示该游戏包含五个精灵。三个精灵(分别命名为 Scalene、Isosceles 和 Equilateral)表示用户点击以选择答案的按钮,另一个隐藏的 Painter 精灵则在舞台上绘制三角形。
注意
我通过在精灵信息区取消勾选 Show 复选框使 Painter 精灵变为不可见。如果你希望通过脚本控制精灵的可见性,可以添加一个hide 块来在游戏开始时明确隐藏精灵。
Tutor 角色是游戏的主要驱动程序;它决定每次运行时绘制的三角形类型,并检查用户的答案。Tutor 角色的脚本如图 6-21 所示。
图 6-21. Tutor 角色的脚本。主要驱动程序脚本(左上角)调用 NewQuestion(右侧)和 CheckAnswer(左下角)。
当点击绿旗图标以开始游戏时,主脚本进入一个无限循环。每次循环时,脚本将 choice 设置为 0(表示玩家尚未回答),绘制一个不同的三角形,并等待答案。当用户点击任何一个答案按钮时,choice 变量应该改变。当用户点击一个按钮来分类三角形时,脚本会检查答案并提供适当的反馈。我们来更详细地看一下每一步。
NewQuestion 程序首先通过随机设置 type(它决定了在舞台上绘制的三角形类型)为 1、2 或 3 来开始。然后,脚本使用两个 if/else 块根据 type 的值设置 name 变量的值。name 变量有两个作用:(1)它指定发送哪个广播消息,以便 Painter 角色知道要绘制什么(三星如何使用 广播并等待 块中的 name),(2)它在 CheckAnswer 程序中用于创建用户的反馈消息。当 Painter 角色绘制完成后,NewQuestion 程序使用 say 命令提示用户输入答案。
当 Painter 角色收到广播消息时,它会在舞台上绘制相应的三角形。为了让游戏更加刺激,Painter 角色使用随机值来确定三角形的大小、方向和颜色,如图 6-22 所示。
图 6-22. Painter 角色的脚本
在请求用户分类绘制的三角形后,主脚本使用 wait until 块(来自 控制 调色板)暂停,直到 choice > 0 成为真。三个按钮角色会在点击时改变 choice。Scalene 按钮将 choice 设置为 1,Isosceles 按钮将 choice 设置为 2,而 Equilateral 按钮将 choice 设置为 3(见图 6-23)。
图 6-23. 三个按钮精灵的脚本
当按钮被按下时,它的精灵会稍微向下和向右移动,给人一种按钮被按下的视觉效果。当鼠标释放时,精灵返回到原来的位置,并设置变量choice的值以表示用户点击了该按钮。注意,每个精灵都会将choice设置为不同的数字。这些脚本中用于移动按钮的块并非严格必要,如果需要,可以将其移除。
一旦用户选择了一个三角形类型,choice的值就会大于零,主脚本会调用CheckAnswer过程。这个过程将type变量(指定所画三角形的类型)与choice变量的值进行比较。如果两个变量的值相同,那么用户的答案是正确的。否则,用户的答案是错误的,脚本会显示正确的分类。
试试第 6-2 节
打开这个游戏并玩几次。一旦你理解了它的工作原理,尝试添加一些额外的功能。以下是一些想法:
-
让游戏保持计分。它可以为每个正确的答案加一分,并为每个错误的答案扣一分。
-
给用户一个退出游戏的选项。
-
定义一个游戏结束的标准。例如,你可以设置主循环运行 20 次而不是一直运行。你也可以在五次错误答案后停止游戏。
-
在游戏进行时,让一些令人兴奋的事情发生。例如,你可以创建一个名为
specialNumber的变量,并在游戏开始时为它赋予一个随机值。当正确答案的数量与specialNumber匹配时,游戏可以给用户加分、播放音乐,甚至讲一个笑话。 -
通过图形效果让按钮“活起来”。例如,如果你在每个按钮上添加如下脚本,当鼠标悬停在按钮上时,按钮的颜色将发生变化。
![没有标题的图片]()
线路跟随器
LineFollower.sb2
我们能否让精灵像图 6-24 中所示那样自己在舞台上跟随(或追踪)一条路径?答案是肯定的,在这一部分,我们将编写一个程序来实现这一点。如果你仔细观察图中的精灵,你会注意到我们用不同的颜色为猫的鼻子和两个耳朵上了色。图中还展示了猫头部的放大视图。
图 6-24. 精灵跟随的示例路径
计划是利用猫的鼻子和耳朵作为颜色传感器来检测下面的黑线。我们的黑线追踪算法使用以下的启发式(基于逻辑推理和反复试验的规则):
-
如果猫的鼻子(粉色)接触到线条,则向前移动。
-
如果猫的左耳(黄色)接触到线条,则逆时针转弯并以较低的速度向前移动。
-
如果猫的右耳(绿色)接触到线条,则顺时针转弯并以较低的速度向前移动。
当然,精确的速度(移动步数)和转弯角度可以根据不同的路线有所不同,需要通过实验来确定。实现上述算法并使精灵沿线行走的脚本如图 6-25 所示。
图 6-25 中的脚本使用了一个新的积木:颜色接触?(来自感应调色板)。这个积木检查精灵上的颜色(在第一个颜色方块中指定)是否与另一个颜色(在第二个颜色方块中指定)接触。如果精灵上的指定颜色与另一个颜色接触,积木返回 true;否则返回 false。可以通过点击颜色方块,然后在 Scratch 项目中的任何地方点击来选择颜色。
图 6-25. 线条跟踪算法尝试练习 6-3
打开应用程序并运行它,查看它是如何工作的。尝试调整给定的值,使精灵以最快的速度完成赛道。一位评论者在 11 秒内完成了赛道。你能打破记录吗?创建其他赛道,看看这个简单的算法是否仍然有效。
直线方程
直线方程
连接两点 P = (x[1], y[1]) 和 Q = (x[2], y[2]) 的直线方程是 y = mx + b,其中 m = (y[2] – y[1]) / (x[2] – x[1]) 是直线的斜率,b 是 y-截距。垂直线的方程为 x = k,水平线的方程为 y = k,其中 k 是常数。在本节中,我们将开发一个应用程序,用于找到连接笛卡尔平面中两点的直线方程。该应用程序的用户界面如图 6-26 所示。
图 6-26. 方程查找应用程序的用户界面
用户将表示线条端点的两个精灵拖到舞台上,应用程序会自动显示生成的直线方程。应用程序包含四个精灵:Point1 和 Point2 用于标记线条的两个端点;Drawer 是一个隐藏精灵,负责在两个端点之间绘制直线;而 Tutor 负责计算和显示直线方程。
Point1 和 Point2 的脚本非常相似。它们包含一些逻辑(这里没有显示)来限制精灵的位置只能处于网格的交点上。实际上,当用户拖动 Point1 精灵时,它会更新保存其坐标的变量(命名为 X1 和 Y1),并广播 Redraw 消息。类似地,当用户拖动 Point2 精灵时,它会更新保存其坐标的变量(命名为 X2 和 Y2),并广播相同的消息。所有四个变量(X1、X2、Y1 和 Y2)的值仅能是 -9 到 9 之间的整数。你可以在文件 EquationOfALine.sb2 中查看这些脚本的详细信息。现在,让我们来看一下 Drawer 精灵的脚本,如 图 6-27 所示。
图 6-27. Drawer 精灵的脚本
当游戏开始时,该精灵设置其画笔的大小和颜色,并准备绘制。当它接收到 Redraw 消息时,它会移动到 Point1 精灵,清除舞台,然后移动到 Point2 精灵。结果是一条连接 Point1 和 Point2 的直线。
当 Tutor 精灵接收到 Redraw 消息时,也会执行一个脚本,如 图 6-28 所示。
图 6-28. Redraw 消息处理程序 for Tutor 精灵
脚本执行以下检查:
-
如果
Point1和Point2的坐标相同,则没有需要处理的线。脚本只会显示“相同的点”。 -
如果两个点不同,但它们的 x 坐标相同,那么我们得到一条垂直线。脚本显示一个类似 x = constant 的方程。
-
如果两个点不同,但它们的 y 坐标相同,那么我们得到一条水平线。脚本显示一个类似 y = constant 的方程。
-
否则,两个点形成一条直线,其方程为 y = mx + b。脚本首先调用 计算 过程来找出斜率和 y 截距。然后它调用 ShowEquation 来将方程格式化并显示给用户。
计算 过程如 图 6-29 所示。它计算斜率(m)和 y 截距(b),然后将这些值四舍五入到最接近的百分位。
图 6-29. 计算 过程
ShowEquation过程如图 6-30 所示。它使用两个变量(term1和term2)和两个子过程来正确格式化方程以供显示。
图 6-30。ShowEquation过程
ShowEquation过程在格式化直线方程时会考虑以下特殊情况:
-
如果斜率为 1,
term1将被设置为x(而不是 1x)。 -
如果斜率为–1,
term1将被设置为–x(而不是–1x)。 -
term2是使用y-截距的正确符号(加号或减号)形成的。 -
如果y-截距为 0,方程将具有形式y = mx。
尝试一下 6-4
打开应用程序并运行它。将两个点拖动到舞台上的不同位置,并检查显示的方程式。为了增强此应用程序,尝试添加一个脚本,如果Tutor精灵与Point1和Point2精灵显示的坐标重叠,则将Tutor精灵移开。
其他应用程序
GuessMyNumber.sb2
现在让我们讨论一些您将在本书的附加资源中找到的游戏(请从nostarch.com/learnscratch/下载附加资源)。附加材料包含两个经典游戏,您可以自行探索。第一个是“猜我的数字”游戏。应用程序会在 1 到 100 之间随机选择一个整数,并提示玩家猜测该数字。然后,应用程序通过显示“太高”或“太低”来告诉玩家猜测的数字是比秘密数字高还是低。玩家有六次机会猜测秘密数字。猜对了数字则赢得游戏,否则就是失败。
RockPaper.sb2
第二个游戏允许用户与计算机玩“石头、剪刀、布”游戏。玩家通过点击表示石头、布或剪刀的三个按钮之一来做出选择。计算机做出随机选择。根据以下规则选出获胜者:布胜(包裹)石头,石头胜(砸碎)剪刀,剪刀胜(剪)布。
总结
在本章中,您了解了 Scratch 中的比较运算符,并使用它们来比较数字、字符和字符串。之后,您学习了if和if/else块,并使用它们在多个程序中做出决策和控制动作。您还学习了如何使用嵌套的if和if/else块来测试多个条件,并应用此技术开发了一个基于菜单的应用程序。您还了解了逻辑运算符,它们作为一种替代方法,是测试多个条件的更简洁的方式。最后,您探索了几个完整的应用程序,展示了决策结构的实际应用。
下一章节将带你深入了解控制面板,展示 Scratch 中可用的各种重复结构,并教你如何使用它们编写更强大的程序。
问题
| Q: | 1. 执行此脚本中的每个命令后,W 的值是多少?![]() |
|---|
| Q: | 2. 使用if块表达以下每个语句:
-
如果
x除以y等于 5,则将x设置为 100。 -
如果
x乘以y等于 5,则将x设置为 1。 -
如果
x小于y,则将x的值加倍。 -
如果
x大于y,则将x的值加 1。
|
| Q: | 3. 编写一个程序,提示用户输入五个介于 1 和 10 之间的测试分数。程序将计算并显示大于 7 的分数个数。 |
|---|
| Q: | 4. 使用if/else块表达以下每个语句:
-
如果
x乘以y等于 8,则将x设置为 1;否则,将x设置为 2。 -
如果
x小于y,则将x的值加倍;否则,将x的值加 1。 -
如果
x大于y,则同时将x和y增加 1;否则,同时将两者减少 1。
|
| Q: | 5. 按照右侧脚本的流程,依次检查每种情况的输出结果:
-
x= –1,y= –1,z= –1 -
x= 1,y= 1,z= 0 -
x= 1,y= –1,z= 1 -
x= 1,y= –1,z= –1
|
| Q: | 6. 编写一个程序,要求用户输入三个数字。程序将确定并打印出这三个数字中最大的一个。 |
|---|
| Q: | 7. 一家公司销售五种不同的产品,其零售价如下表所示。编写一个程序,要求用户输入产品编号和销售数量。程序将计算并显示总零售价。 |
| 产品编号 | 零售价 |
| --- | --- |
| 1 | $2.95 |
| 2 | $4.99 |
| 3 | $5.49 |
| 4 | $7.80 |
| 5 | $8.85 |
|
| Q: | 8. 构造一个逻辑表达式,表示以下每个条件:
-
score大于 90 且小于 95。 -
answer是 y 或 yes。 -
answer是介于 1 和 10 之间的偶数。 -
answer是介于 1 和 10 之间的奇数。 -
answer介于 1 和 5 之间,但不等于 4。 -
answer是介于 1 和 100 之间并且能被 3 整除的数。
|
| Q: | 9. 三角不等式定理指出,三角形的任意两边之和大于第三边的长度。编写一个程序,从用户那里获取三个数字,并判断它们是否可以代表三角形的三边。 |
|---|---|
| 问: | 10. 毕达哥拉斯定理指出,如果a和b是直角三角形的两条直角边,c是斜边的长度(最长的一边),那么a² + b² = c²。编写一个程序,获取用户输入的三个数字,并判断它们是否能够表示直角三角形的三条边。 |
第七章。重复:深入探索循环
你之前已经见过一些 Scratch 的重复结构,但本章将更详细地讲解它们。现在是时候讨论创建循环、嵌套循环和递归的新块了。在本章结束时,我们将探索以下编程概念:
-
用于重复执行语句的重复结构
-
如何验证用户输入
-
计数器控制的循环和事件控制的循环
-
可以通过递归调用自身的过程
尽管大多数人觉得重复性任务无聊,但计算机似乎最喜欢做的就是这些。重复结构,也就是更常见的循环,是编程命令,指示计算机反复执行某个语句或一系列语句。最简单的循环类型是确定循环,它会在特定次数内重复执行一系列语句。这些循环也叫计数器控制的循环或计数循环。其他类型的循环则会一直重复,直到满足某个条件;这些被称为条件控制的循环或不确定循环。还有一种循环叫做无限循环,它会永远重复下去。
在本章中,你将学习 Scratch 中不同的重复结构。我将详细解释计数器控制的循环和条件控制的循环,并介绍停止块,你可以用它来结束无限循环。你还将学会如何使用循环来验证用户输入。
本章还讨论了嵌套循环(包含其他循环的循环),并展示了几个使用它们的例子。我们还将讨论递归——一个过程调用自身——作为实现重复的另一种方式。最后,我们将开发一些有趣的应用程序,既使用循环也使用条件语句,并探索如何将循环融入实际程序中。
Scratch 中的更多循环块
正如你在第二章中学到的那样,循环块允许你在程序中重复执行命令或一组命令。Scratch 支持图 7-1 中显示的三种重复块。
图 7-1. Scratch 的重复块
在本书的多个示例中,你已经使用过这两个块:重复块和永远块。在本节中,我们将讨论第三种循环块——直到重复块,并解释与循环相关的一些技术术语。
每次循环的重复称为迭代,而计数一词通常用来描述循环重复的次数。你非常熟悉的repeat块是一个计数控制的循环,因为它会在指定的次数内重复执行命令。当我们知道循环需要执行多少次时,通常会使用这个循环,就像我们想要绘制一个已知边数的多边形时。
另一方面,repeat until 块是一个条件控制的循环。这个块内的语句会根据其测试表达式的真假值进行重复。当我们无法预知循环需要重复多少次,并且希望循环在某个条件满足之前持续进行时,我们使用这个块。例如,你可以说:“重复ask命令,直到用户输入一个正数。”或者,“重复发射导弹,直到玩家的能量值降到某个特定值以下。”接下来的章节将更详细地解释条件控制循环。
repeat until 块
假设你正在开发一个游戏,游戏向玩家提出一个简单的数学问题。如果玩家的回答错误,游戏会再次提问,给玩家另一次机会。换句话说,游戏会一直提问直到玩家输入正确答案。显然,repeat块不适合这个任务,因为你无法预知玩家需要多少次才能输入正确答案;第一次就可能答对,也可能需要 100 次。repeat until块可以帮助你解决像这样的场景。repeat until块的结构如图 7-2 所示。
图 7-2. repeat until块允许你反复执行一系列指令,直到某个条件成立。
这个块包含一个布尔表达式,其值在进入循环时会被测试。如果表达式为假,循环内的命令将被执行。当循环中的最后一条命令执行完毕后,循环将重新开始,并再次测试表达式。如果表达式仍为假,循环内的命令会再次执行。这个过程将一直重复,直到测试表达式为真。当那时,循环内的命令会被跳过,程序将继续执行紧跟循环后的命令。
注意,如果程序第一次运行循环时测试条件已经为真,那么循环中的命令将不会执行。而且,repeat until块不会终止,除非某个命令(无论是在循环内部还是程序的其他活动部分)使得测试条件变为真。如果测试条件的结果永远不会为真,我们就会陷入一个无限循环。
图 7-3 展示了使用repeat until块的实际示例。在这个示例中,只要Player精灵与Guard精灵之间的距离超过 100 步,Guard精灵就会继续沿当前方向移动(此情况下为水平方向),并在接触舞台的左右边缘时反弹。如果两者之间的距离小于 100,repeat until块将终止,Guard精灵将开始追逐Player精灵。追逐的代码在图中没有显示。distance to块可以在Sensing面板中找到。
图 7-3。一个简单的示例,展示了 repeat until 块的实际应用。尝试操作 7-1
打开应用程序Chase.sb2并运行它。使用箭头键将Player精灵移到接近Guard精灵的位置,看看追逐效果。你会如何改变测试条件,使得如果Player精灵的y位置超出某个范围(例如,-50 到 50),Guard精灵会被释放?实施这个更改来验证你的解决方案。
Chase.sb2
构建一个 forever if 块
无限循环在很多编程场景中都非常有用。例如,在前面的章节中,你使用了forever块来播放背景音乐,并通过不断更换角色服装来动画化精灵。forever块是一个无条件的无限循环,因为它没有控制内部命令执行的测试条件。
然而,你可以通过将if块嵌套在forever块内来轻松改变这一点,从而创建一个条件无限循环,如图 7-4 所示。if块的测试条件会在每次迭代开始时进行检查,只有当测试条件为真时,其命令才会执行。注意,由于forever块是无限执行的,你不能在其后连接命令块。
图 7-4。你可以通过将 forever 块与 if 块结合来创建一个 forever/if 循环。
组合永远/如果结构通常用于用键盘箭头键控制精灵的运动,如图 7-5 所示。
图 7-5. 这些脚本允许你用键盘箭头键来移动精灵。每个脚本响应其中一个箭头键。
ArrowKeys1.sb2
当按下绿色旗帜图标时,四个方向键(左、右、上、下)将在四个独立的无限循环中被监视。当按下任意一个键时,相应的循环会导致精灵的x坐标或y坐标发生变化。
在 Scratch 中创建这些脚本(或打开ArrowKeys1.sb2)并运行程序。注意,如果同时按下上箭头和右箭头键,精灵会向东北方向对角移动。尝试其他箭头键组合,看看应用程序如何响应。
试试看 7-2
另一种用箭头键控制精灵运动的方法如下所示。将这种方法与图 7-5 中的方法进行比较。哪一种对键盘敲击更为灵敏?如果同时按下两个键(例如,上键和右键),替代脚本会如何表现?现在,尝试将图 7-5 中四个如果区块放在一个永远循环中,并同时按下两个箭头键。精灵的行为有何变化?

停止命令
假设你正在编写一个程序,用来找到第一个小于 1,000 的能被 3、5 和 7 整除的整数。你可以编写一个脚本,在循环中逐一检查数字 999、998、997,依此类推。当你找到你要找的数字时(在这个例子中是 945),你想要停止搜索。
如何告诉 Scratch 结束循环并停止脚本?你可以使用停止命令(来自控制调色板)来结束活动脚本。下拉菜单提供了如图 7-6 所示的三个选项。
图 7-6. 在 Scratch 中使用停止命令
第一个选项立即终止调用它的脚本。第二个选项则停止应用程序中所有正在运行的脚本;它等同于舞台顶部的红色停止图标。请注意,在使用这两个选项时,stop 块之后不能连接任何命令。
StopDemo.sb2
第三个stop选项允许一个精灵或舞台结束其所有脚本,除了调用stop块的那个脚本。这个命令呈堆叠块的形状,因此你可以在它下面添加块,在暂停精灵其他脚本后执行它们。让我们通过一个简单的游戏来看看这个命令的作用,见图 7-7。
图 7-7。在这个游戏中,玩家在舞台上移动巫婆,同时尽量避开两个球。
图中的两个球在舞台上移动并追逐巫婆。玩家用键盘移动巫婆精灵,并尽量避免被两个球碰到。如果红色球在任何时候碰到玩家,游戏结束。如果绿色球碰到玩家,它将停止追逐玩家,但红色球会开始稍微加速——这使得逃避红球变得更具挑战性。
移动巫婆精灵的脚本与图 7-5 的脚本类似,所以这里不再展示。两个球的脚本如图 7-8 所示——我们来看一下这些脚本。
图 7-8。绿色球(左)和红色球(右)的脚本
当绿色球碰到玩家时,它会增加speed变量(该变量设置红色球的移动速度),并调用stop this script命令来终止它的脚本。游戏中的其他脚本应该继续正常运行。在这里使用stop this script命令非常合适,因为我们只希望加速红色球一次。然而,如果红色球碰到玩家,它会执行stop all命令,这将导致应用程序中所有正在运行的脚本停止。
尝试示例 7-3
加载这个游戏并进行游戏,看看它是如何运作的。观察图 7-8 中两个脚本周围的黄色边框,在绿色球和红色球碰到Player时会发生什么。
你还可以使用stop语句块在程序执行的任何点终止一个过程并使其返回给调用者。下一节将展示这一概念的实际应用。
结束计算循环
NumberSearch.sb2
假设我们要找到大于 1,000 的第一个 2 的幂。我们将编写一个过程,在一个循环中检查 2¹、2²、2³、2⁴等。当我们找到所需的数字时,我们希望程序说出答案并停止过程。图 7-9 展示了实现这一方法的两种方式。
图 7-9. 两种方法找到大于 1,000 的第一个 2 的幂
图 7-9 中左侧的程序将result变量初始化为 2,这是要检查的第一个 2 的幂,并进入一个无限循环以寻找答案。它在每次循环迭代中检查result的值。如果result大于 1,000,则程序调用停止此脚本命令停止并返回给调用者。否则,if语句块后的命令(即将result的前一个值乘以 2)将执行,下一次迭代开始。如果你跟踪这个过程,你会看到if语句块在第一次迭代时将result设为 2,第二次迭代时为 4,第三次为 8,以此类推。这将一直持续,直到result超过 1,000;此时,程序停止并返回给调用者,调用者使用say语句块显示结果。
图 7-9(右)展示了另一种实现该过程的方法。在这里,我们使用了一个repeat until语句块,该语句块将继续循环,直到result大于 1,000。与第一种实现方式一样,循环会继续将result的值翻倍,直到它超过 1,000。当发生这种情况时,循环自然终止,过程返回给调用者。请注意,在这种情况下我们不需要使用stop语句块。
当你需要验证用户输入时,stop语句块也非常有用。接下来你将看到这种实际应用的例子。
验证用户输入
当你编写一个从用户那里读取数据的应用时,你应该始终在开始处理数据之前检查输入的值是否有效。重复结构可以帮助你完成这个任务。如果用户输入无效,你可以使用一个循环显示适当的错误信息并要求用户重新输入值。
举个例子,假设你正在开发一个有两个等级的游戏,并希望让用户选择一个等级进行游戏。在这种情况下,唯一有效的输入是数字 1 和 2。如果用户输入一个不在这两个数字范围内的数字,你可能希望再给他们一次输入有效值的机会。实现这个检查的一种方式在图 7-10 中展示。
图 7-10. 使用 forever 块进行输入验证
GetLevel过程要求用户输入一个选择,并在forever循环内检查答案。如果用户的答案无效,循环会提示用户重新输入等级。如果用户输入有效的数字,过程会调用stop this script来终止循环并结束过程。当这种情况发生时,主脚本(一直在耐心等待GetLevel过程返回)继续执行say命令。图 7-11 展示了如何使用重复直到块实现相同的任务。
图 7-11. 使用重复直到块进行输入验证
图 7-11 中的过程要求用户输入一个选择并等待答案。如果用户输入 1 或 2,重复直到块头部的条件会评估为真,从而自然终止循环并结束过程。另一方面,如果用户输入除了 1 或 2 之外的其他内容,循环条件会评估为假,循环内的ask命令会执行。这个命令会再次等待用户的输入,重复直到块会继续要求输入,直到用户输入一个有效的选择。再次提醒,这个实现不需要stop块。
计数器
有时候,你需要跟踪一个循环执行的次数。例如,如果你想给用户三次机会输入正确的密码,你需要计算他们的尝试次数,并在第三次尝试后锁定他们。
你可以通过使用一个变量(通常称为循环计数器)来处理这种编程场景,这个变量会统计循环的迭代次数。让我们直接进入并探索一些展示如何实际使用循环计数器的例子。
检查密码
图 7-12 中的程序要求用户输入密码以解锁笔记本电脑。Laptop 精灵有两种服装:off 图像表示笔记本电脑已被锁定,on 图像表示笔记本电脑已解锁。如果用户连续三次输入无效密码,则将拒绝访问该笔记本。
Password Check.sb2
图 7-12. 此脚本给用户三次机会输入正确的密码。
当点击绿色旗帜时,Laptop 精灵切换到 off 服装,并调用 GetPassword 过程来验证用户身份。此过程预计通过 gotPass 标志将密码检查结果返回给主脚本。当过程返回时,if/else 代码块会检查 gotPass 标志,以决定是否允许用户访问系统。如果 gotPass 被设置为 1,表示用户输入了正确的密码,则 if 代码块执行 say 命令,显示 Access granted(访问授权),并将笔记本的服装更改为 on 图像。否则,脚本显示 Access denied!(访问拒绝!),并且精灵继续显示其初始的 off 服装。
GetPassword 过程将 gotPass 标志设置为 0,表示尚未收到有效密码,并将 failCount 变量(我们的循环计数器)初始化为 0。然后执行一个 repeat 循环,最大重复次数为三次。在每次循环中,系统提示用户输入密码。如果用户输入了正确的密码(例如 Pass123),则将 gotPass 标志设置为 1,过程通过调用 stop this script 命令停止自身,并将执行返回给调用者。否则,如果用户还没有用完三次尝试机会,则显示错误消息,并给予用户再次尝试的机会。如果用户连续三次失败,repeat 循环会自动终止,并且过程返回给调用者,gotPass 标志仍然保持为 0。
尝试实践 7-4
打开此应用程序并运行它。如果你输入 paSS123(而不是 Pass123)作为密码,会发生什么?这告诉你在 Scratch 中字符串比较的特点是什么?尝试使用 repeat until 代码块实现 GetPassword 过程。
按固定数量计数
当然,你不必每次通过循环时都将计数器增加 1。例如,图 7-13 中的脚本 ① 会使精灵从 5 计数到 55,每次增加 5。脚本 ② 会使精灵从 99 计数到 0,每次减少 11——也就是说,99, 88, 77, ... 11, 0。
CountingBy ConstAmount.sb2
图 7-13。你可以通过其他增量来递增和递减计数器,而不仅仅是 1。
为了在实际应用中看到这种计数技巧,我们假设我们想要计算从 2 到 20(包括 20)所有偶数的和。(也就是说,我们想要计算 2 + 4 + 6 + 8 + ... + 20 的和。)图 7-14 的脚本正是执行了这个任务。
图 7-14。这个脚本计算了从 2 到 20 所有偶数的和。
这个脚本首先将 sum 变量初始化为 0,并将 count 变量初始化为 2,然后进入一个条件循环,直到 count 超过 20 为止。在每次迭代中,count 的值会被添加到当前的和中,并且 count 变量会增加 2,以获得序列中的下一个偶数。预测这个脚本的输出,然后运行它来检查你的答案。
非整数重复计数
你认为如果让 Scratch 重复一个循环 2.5 次,会发生什么情况?下面的三个示例展示了 Scratch 如何处理非整数的重复计数。
Non-Integer RepeatCount.sb2

当然,根本没有“重复 2.5 次”这种说法,但 Scratch 并没有阻止你输入这样的值。Scratch 不会给出错误信息,而是自动将小数重复计数四舍五入到最接近的整数。
重新审视嵌套循环
在旋转方块一节中,我们使用了嵌套循环来绘制旋转的方块。一个循环(内循环)负责绘制方块,而另一个循环(外循环)控制旋转的次数。在这一节中,你将学习如何结合使用循环计数器和嵌套循环,在二维(或更多维度)中创建迭代。这项技术是编程的一个重要部分,正如你接下来会看到的,它可以用来解决各种编程问题。
假设有一家本地餐厅提供四种披萨(P1、P2、P3 和 P4)和三种沙拉(S1、S2 和 S3)。如果你在这家餐厅用餐,你将有 12 种可能的组合可以选择;你可以将 P1 与三种沙拉中的任意一种搭配,P2 与三种沙拉中的任意一种搭配,以此类推。餐厅老板想要打印一份菜单,列出所有可用的披萨/沙拉组合,以及它们的价格和卡路里含量。让我们看看如何使用嵌套循环来生成所有可能的组合。(计算价格和卡路里内容的部分留给你作为练习。)
如果你仔细想想,你会发现我们只需要两个循环:一个循环(外循环)用来遍历披萨类型,另一个循环(内循环)用来遍历沙拉类型。外循环从 P1 开始,而内循环依次尝试 S1、S2 和 S3。然后外循环转到 P2,内循环再次选择 S1、S2 和 S3。这个过程一直持续,直到外循环遍历完所有四种披萨类型。这个思路的实现方式在图 7-15 中得到了说明。
NestedLoops1.sb2
图 7-15. 可视化嵌套循环。变量 P 控制外循环,变量 S 控制内循环。
该脚本使用了两个循环和两个计数器。外循环的计数器名为 P,内循环的计数器名为 S。在外循环的第一次迭代(P = 1 时),计数器 S 的值被设置为 1,内循环重复执行三次。每次执行时,它会执行一个 say 命令来显示当前的 P 和 S 的值,然后将 S 增加 1。因此,外循环的第一次迭代会导致精灵依次说出“P1,S1”、“P1,S2”和“P1,S3”。
当内循环在执行三次后终止时,P 的值增加 1,外循环的第二次迭代开始。此时,S 的值重置为 1,内循环再次执行。这会导致精灵依次说出“P2,S1”、“P2,S2”和“P2,S3”。这一过程以类似的方式继续,直到精灵说出“P3,S1”、“P3,S2”和“P3,S3”,最后是“P4,S1”、“P4,S2”和“P4,S3”,然后脚本结束。通过跟踪这个脚本,确保你理解它是如何工作的。
现在你已经了解了嵌套循环的应用,让我们将这一技巧应用于解决一个有趣的数学问题。我们要编写一个程序,找到三个正整数 n[1]、n[2] 和 n[3],使得 n[1] + n[2] + n[3] = 25 且 (n[1])² + (n[2])² + (n[3])² = 243。由于计算机擅长执行重复任务,我们的计划是尝试所有可能的数字组合(这种技巧称为 穷举搜索),让计算机完成繁重的工作。
根据我们的第一个方程,第一数字 n[1] 可以是 1 到 23 之间的任何值,因为我们需要再加两个数字才能得到 25。(你可能已经注意到,n[1] 不能大于 15,因为 16² = 256,已经大于 243。但我们暂时忽略第二个方程,还是将循环的上限设置为 23。)
第二个数字 n[2] 可以是 1 到 24 - n[1] 之间的任何值。例如,如果 n[1] 为 10,则 n[2] 的最大可能值是 14,因为 n[3] 至少为 1。如果我们知道了 n[1] 和 n[2],就可以计算出 n[3] 为 25 - (n[1] + n[2])。然后,我们需要检查这三个数字的平方和是否等于 243。如果是,我们就完成了。否则,我们需要尝试不同的 n[1] 和 n[2] 组合。你可以在 图 7-16 中看到完成的脚本,用来找到 n[1]、n[2] 和 n[3]。
NestedLoops2.sb2
图 7-16。此脚本搜索三个正整数,其和为 25,且平方和为 243。
外层循环尝试从 1 到 23 的所有 n1 值。对于每个 n1 值,内层循环尝试从 1 到 (24 - n1) 的所有 n2 值。对于每个 n1 和 n2 的组合,脚本将 n3 设置为 25 - (n1 + n2),然后检查这三个数字的平方和是否等于 243。如果是,脚本会输出答案并停止。
实验 7-5
创建 图 7-16 中显示的脚本并运行它,以找出 n[1]、n[2]* 和 n[3]。如果你仔细研究脚本,会发现它会多次尝试某些 (n[1]、n[2]) 组合。例如,数字 (1, 2) 在外层循环的第一次迭代中被测试,而数字 (2, 1) 会在第二次迭代中被测试。这两个测试是冗余的,我们只需要其中一个。你可以通过让内层循环从 n[1] 而不是 1 开始来修复这个问题。对脚本进行此修改后,运行它并确保它仍然按预期工作。
递归:调用自身的过程
到目前为止引入的重复结构允许我们通过迭代重复执行一个命令或一组命令。另一种强大的产生重复的技术是递归。递归允许一个过程直接调用自身,或通过另一个过程间接调用自身(例如,A调用B,B调用C,然后C调用A)。这可能不容易理解为什么要这么做,但事实证明,递归可以简化许多计算机科学问题的解决方案。让我们通过考虑图 7-17 中展示的简单示例来演示这个概念。
Recursion.sb2
图 7-17. 一个递归过程
Tic过程执行两个say命令(第一个说“Tic”,第二个说“Tac”),然后再次调用自身。第二次调用做同样的事情,如果没有外部操作停止,它会继续不断地说“Tik Tac”。当然,在这种情况下,唯一能停止它的方法是点击红色停止图标。让一个过程以这种方式调用自身,使我们能够无限重复这两个say命令,而无需使用任何循环块。这个示例中使用的递归形式称为尾递归,因为递归调用位于过程的最后一行。Scratch 也允许递归调用出现在最后一行之前,但我们在本书中不会探讨这种递归类型。
由于无限递归通常不是一个好主意,你必须使用条件语句来控制递归过程的执行。例如,过程可以包括一个if语句块,用于判断是否应该进行递归调用。为了演示这一技巧,图 7-18 展示了一个递归过程,该过程从某个初始数字(由参数count指定)开始倒数到 0。
图 7-18. 使用 if 语句块来确定是否进行递归调用
让我们通过一个实例如CountDown的调用,来了解它是如何工作的,当它的参数为 3 时。当过程开始时,say命令显示数字 3,然后检查count是否大于 0。由于 3 大于 0,过程会从count中减去 1,并以 2 作为参数再次调用自己。
在第二次调用中,过程显示数字 2,并且由于 2 大于 0,它会再次调用自己,参数为 1。这一过程持续进行,直到调用CountDown(0)。在语音气泡中显示数字 0 后,过程检查count是否大于 0。由于if块中的表达式计算结果为假,之后不会进行任何递归调用,过程返回。试着按照图 7-18 所示的返回路径进行跟踪。
现在我们已经涵盖了尾递归的基本概念,可以将其应用于更有趣的应用场景。例如,考虑图 7-19 中所示的Blade过程。
RecursionBlade.sb2
图 7-19. 使用精灵的方向来停止递归
我们假设执行此过程的精灵从舞台上的某个位置开始,朝着 90°的方向指向。绘制等边三角形后,精灵向前移动 12 步,然后逆时针转 10°。此过程随后检查精灵的新方向。如果精灵没有指向 90°的方向,过程会再次调用自己,绘制序列中的下一个三角形。否则,不会发生递归调用,过程在绘制完图 7-19 所示的锯齿形后结束。
对于像这里展示的简单例子,使用repeat块可能更容易实现所需的重复。但是正如我在本节开始时提到的,很多问题使用递归比使用迭代更容易解决。
尝试一下 7-6
以下过程的功能是什么?实现它并使用不同的参数进行调用,以验证你的答案。

Scratch 项目
现在你已经知道如何在 Scratch 脚本中利用重复来获得优势,是时候将我们在本章学到的内容付诸实践了。在这一节中,我将引导你完成一系列项目,帮助你加深对编程的理解,并为你的项目提供一些灵感。
模拟时钟
Sensing调色板中的当前模块可以报告当前的年份、月份、日期、星期几、小时、分钟或秒数,具体取决于你从下拉菜单中选择的项。我们的第一个项目将使用此模块来实现如图 7-20 所示的模拟时钟。该应用包含四个精灵:Sec、Min和Hour精灵,代表时钟的三根指针,和Time精灵(一个小白点),显示数字格式的时间(见图中的思考气泡)。
AnalogClock.sb2
图 7-20. 模拟时钟应用
当点击绿色旗帜时,时钟开始运行。作为响应,所有四个精灵都开始一个永远循环,根据当前系统时间更新它们的状态。Sec和Min精灵的脚本如图 7-21 所示。
图 7-21. Sec和Min精灵的脚本
当前模块报告的秒数和分钟数范围从 0 到 59。当系统报告 0 秒时,Sec精灵应指向上方(朝向 0°),在 15 秒时,Sec精灵应指向右方(朝向 90°),以此类推。每秒钟,Sec指针应顺时针旋转 6°(360°除以 60 秒)。同样的逻辑适用于Min指针。如果你观察这个时钟运行,你会注意到Sec指针每秒跳动一次,而Min指针每分钟跳动一次。现在,让我们看看Hour精灵的脚本,如图 7-22 所示。
图 7-22. Hour精灵的脚本
当前(小时)模块报告系统时钟的小时数,范围从 0 到 23。我们需要让Hour指针在小时 0 时指向 0°(即朝上),小时 1 时指向 30°,小时 2 时指向 60°,依此类推,如图所示。当然,如果当前时间是 11:50,我们并不希望Hour指针精确指向 11,而是更接近 12。我们可以通过考虑当前的分钟数来进行这种调整。
由于每小时(或 60 分钟)对应时钟面上的 30°,每分钟对应 2°。因此,每分钟,我们需要根据当前的分钟数除以 2 来调整Hour指针的角度,如脚本所示。
Time精灵的脚本很简单,在这里不展示。它使用嵌套的join积木构造类似hour:min:sec的显示字符串,并将该字符串显示在思维气泡中,如图 7-20 所示。
实践 7-7
打开应用程序并运行。修改Min精灵的脚本,使其平滑移动,而不是每分钟跳动一次。(提示:使用我们之前用来平滑时针移动的相同思路。)此外,修改Time精灵的脚本,使其显示类似“3:25:00 PM”格式的字符串(12 小时制),而不是“15:25:00”(24 小时制)。想想其他可以增强应用程序的方式,并尝试实现它们。
鸟类射击游戏
现在,让我们制作一个简单的游戏,使用本章介绍的大部分积木。玩家的目标是将两只鸟从天上打下来,您可以在图 7-23 中看到用户界面。
BirdShooter.sb2
图 7-23. 鸟类射击游戏的用户界面
如图所示,游戏包含五个精灵:Bird1、Bird1的克隆、Bird2、一个射击者和一颗子弹。玩家可以使用键盘左右箭头水平移动射击者。按下空格键会发射一颗子弹进入空中。如果子弹击中Bird1或其克隆,玩家会获得 1 分。Bird2是濒危物种,玩家不能射击它;如果子弹击中该精灵,游戏结束。玩家有一分钟时间尽可能多地射击鸟类。
每只鸟使用两个服装。在这两个服装之间切换时,鸟看起来像是在拍动翅膀。
舞台有两个背景,分别命名为start和end。start背景如图 7-23 所示。end背景与之相同,只是在图像的中央添加了游戏结束字样。属于舞台的脚本如图 7-24 所示。
图 7-24. 鸟类射击游戏中舞台的脚本
当点击绿色旗帜时,舞台切换到start背景,重置计时器,并启动一个循环,更新并检查剩余的游戏时间,这由TimeLeft变量跟踪。当TimeLeft为 0 或舞台收到GameOver广播消息时,执行GameOver处理程序。这个脚本等待一段时间,允许小鸟隐藏自己,切换到end背景,并调用stop all终止所有正在运行的脚本。如你所见,GameOver消息会在Bullet精灵击中Bird2时发送。现在我们来看一下Shooter精灵的脚本,如图 7-25 所示。
图 7-25. Shooter精灵的脚本
这个脚本首先将射手定位在舞台底部中间位置。脚本随后进入一个无限循环,检测左箭头或右箭头是否被按下,并根据按键方向移动射手。接下来,让我们来看一下Bird1的脚本,如图 7-26 所示。
图 7-26. Bird1精灵的脚本
游戏开始时,Bird1会克隆自己,移动到舞台的左边缘,并调用开始过程。克隆也从舞台的左边缘开始(但高度不同),并调用开始。这个过程使用forever循环,将小鸟及其克隆横向移动整个舞台,从左到右,步长随机。当小鸟接近舞台的右边缘时,会被移回左边缘,好像绕了过去重新出现。最后一个脚本会在GameOver消息广播时隐藏两只小鸟。
Bird2的脚本与Bird1的脚本非常相似,因此我们在此不再展示。当点击绿色旗帜时,Bird2会移动到舞台右侧的高度 40 位置,然后执行一个类似于图 7-26 的开始过程的循环。小鸟从左向右移动,遇到舞台右侧时会绕回去。Bird2还会响应GameOver广播,隐藏自己。
当然,玩家光靠移动射手是无法击中任何小鸟的,这就是Bullet精灵的作用。该精灵的主脚本如图 7-27 所示。
图 7-27. Bullet精灵的主脚本
当点击绿色旗帜时,脚本会初始化变量 Fired(发射的子弹数量)和 Hits(击中鸟类的数量)为 0。然后它会将 Bullet 精灵指向上方并将其隐藏。之后,进入一个无限循环,反复检查空格键的状态。当按下空格键时,脚本会将 Fired 增加 1,并创建一个 Bullet 精灵的克隆体,让子弹向上移动,如我们接下来所看到的。脚本接着等待一段时间,以防玩家过快发射下一个子弹。现在我们准备好研究克隆子弹的脚本,见图 7-28。
首先,Bullet 被移动到 Shooter 的中心并显示出来①。然后,使用repeat until块以 10 步的增量将 Bullet 向上移动②。如果子弹的 y 坐标超过 160,则表示 Bullet 已经到达舞台的上边缘且未击中任何鸟类。在这种情况下,repeat until 块退出⑤,克隆体被删除。然而,每次子弹移动时都会执行击中检查。如果子弹触碰到 Bird1(或其克隆体)③,脚本会增加 Hits 变量并播放一个音效,让游戏更加刺激。另一方面,如果子弹触碰到 Bird2 ④,脚本会广播 GameOver 信号,标志着游戏结束。在这两种情况下,克隆体都会被删除,因为它已经完成了任务。
图 7-28. 克隆 Bullet 的启动处理程序
现在游戏已经完全功能化,但你可以为其添加许多新特性。这里有两个建议:
-
给玩家设定一个有限的子弹数量,并根据错过的射击次数进行记分。
-
添加更多的鸟类,并让它们以不同的速度移动。对于击中速度更快的鸟类,奖励玩家更多的分数。
试试 7-8
打开游戏并进行游玩,看看它是如何工作的。根据上述建议进行修改,或者想出一些自己的增强功能并实现它们!
自由落体模拟
在这一部分,我将展示一个模拟自由落体运动的应用程序。忽略浮力和空气阻力的影响,当一个静止物体从某个高度被释放时,物体在时间 t(秒)内下落的距离 d(米)由公式 d = ½ gt² 给出,其中 g = 9.8 m/s² 为重力加速度。这个模拟的目标是展示物体在 0.5 秒、1.0 秒、1.5 秒、2.0 秒等时间点的下落位置,直到物体触地。这个模拟的界面如图 7-29 所示。
FreeFall.sb2
图 7-29. 自由落体仿真用户界面
一个静止的物体(图中的球)将被允许从 35 米的高度下落。通过简单的代入上述公式可以得出物体将在
秒后到达地面。该应用程序有一个精灵(称为Ball),它有图中显示的两个服装。当需要显示下落球体的位置时,精灵会短暂地切换到marker服装,做出印章,并切换回ball服装。
仿真在点击绿色旗帜时开始。作为响应,Ball精灵运行图 7-30 所示的脚本。
在初始化①期间,精灵移动到起始位置,切换到ball服装,清除上次运行的语音气泡,并清除舞台上之前的印章。接着,初始化t和counter为 0。变量t代表下落的持续时间,counter用于跟踪循环的重复次数。
然后,脚本进入一个无限循环②,以在不同的时间间隔计算仿真参数。它每隔 0.05 秒③进行一次计算并更新球的位置,以确保球的平滑运动。每 0.05 秒,时间变量t的值会更新,计算球下落的距离(d)。counter变量的值也会增加 1。
图 7-30. 自由落体仿真中的Ball精灵脚本
如果球到达地面(当d ≥ 35时发生),脚本将球的y位置设置为地面的高度,显示实际的运动时间,并停止脚本以结束仿真④。
否则,脚本会根据下落的距离⑤设置球的垂直位置。由于 35 米的高度对应舞台上的 268 像素(见图 7-29),d米的距离对应于268 *(d / 35)。最终的y位置通过从初始y位置(136)中减去这个数值来确定。
由于迭代时间为 0.05 秒,获取 0.5 秒需要 10 次迭代。因此,当计数器达到 10、20、30 等时,Ball精灵会切换到(并且印上)marker服装,以显示下落球体在这些瞬间的位置⑥。
图 7-31 展示了运行该模拟的结果。注意每个时间间隔内物体下落的距离是如何随着物体下落而增大的。由于重力,球体加速——其速度增加——加速度为 9.8 m/s²。
尝试一下 7-9
打开应用程序并运行它,以理解其工作原理。尝试将模拟转变为一款游戏,玩家需要将球投掷到地面上的移动物体上。你可以添加得分,改变目标的速度,甚至设定在另一个星球上的动作(改变重力加速度)。
图 7-31. 自由落体模拟的结果
投射运动模拟器
考虑一颗初速为 (v[0]) 的球从炮筒发射,炮筒与水平方向夹角为 q。你可以通过将速度向量 (v[0]) 在不同时间点分解成水平和垂直分量来分析球的轨迹。水平分量保持不变,但垂直分量受到重力的影响。当这两个分量的运动结合起来时,结果路径是一个抛物线。让我们来研究一下支配投射运动的方程(忽略空气阻力)。
Projectile .sb2
我们坐标系的原点是球开始飞行的点,因此球体在任何时刻 t 的 x 坐标由 x(t) = v[0][x]t 给出,y 坐标由 y(t) = v[0][y]t – (0.5)gt² 给出,其中 v[0][x] = v[0] cos q 是 v[0] 的 x 分量;v[0][y] = v[0] sin q 是 v[0] 的 y 分量;g = 9.8 m/s² 是重力加速度。利用这些方程,我们可以计算出球的总飞行时间、最大高度和水平射程。这些量的方程如图 7-32 所示。
图 7-32. 球的抛物线轨迹
这些信息是我们实际模拟球体运动所需的全部内容,因此让我们创建一个 Scratch 程序,以便观察这段物理过程的实际表现,并加深我们对轨迹的理解。模拟的用户界面如图 7-33 所示。
图 7-33. 投射运动模拟器的用户界面
如图所示,应用程序包含四个角色精灵。Wheel精灵为大炮提供了旋转轴,而Cannon精灵则根据angle滑块旋转,提供发射角度的视觉指示。Fire精灵是一个按钮,用户点击它来发射球,而Ball精灵包含计算球坐标和绘制轨迹的主脚本。用户通过两个滑块控制指定发射角度和初始速度,然后点击Fire按钮。Ball从舞台上的点(–180, –140)开始,并根据指定的参数绘制抛物线轨迹。舞台右下角的两个监视器显示飞行时间和飞行中的水平射程。
模拟开始时,点击绿色旗帜图标。Cannon精灵的脚本(这里未显示)将大炮指向由angle滑块控制指定的方向。用户也可以通过点击并拖动大炮来指定角度。当用户点击Fire按钮时,它会广播一个Fire消息,该消息由Ball精灵通过图 7-34 中的脚本接收并处理。
为了准备发射①,Ball(球)在Cannon(大炮)和Wheel(轮子)前方移动并定位到发射点。它放下笔并清除舞台上的所有笔迹。然后,脚本计算初始速度的水平(或x)和垂直(或y)分量(分别命名为vx和vy),并将时间变量(t)初始化为 0。
图 7-34。Ball精灵的脚本
然后,脚本进入一个无限循环②,计算并更新球的位置,每 0.02 秒更新一次。首先,计算精灵的垂直距离(dy)③。如果计算出的值为负,则表示球已到达地面。当发生这种情况时,调用停止此脚本命令来结束模拟。
如果dy不是负值,则计算水平距离(d)④。然后,两个距离(dy和d)会根据舞台的背景进行缩放。在垂直方向上,我们有 320 步(从–140 到 180),对应 100 米;在水平方向上,我们有 420 步(从–180 到 240),也对应 100 米。这意味着dy米的垂直距离相当于320 * dy / 100步,而d米的水平距离相当于420 * d / 100步。然后,更新球的x和y坐标,并将球移动到其轨迹上的当前位置。接着,时间变量(t)会增加一个小量(在这种情况下为 0.02 秒),并重复循环以计算球的下一个位置。
例如,如果将球以 70° 的发射角度和 30 m/s 的初速度发射,如图 7-33 所示,总飞行时间为 5.75 秒,射程为 59 米。对图 7-33 中的监视器进行检查表明,我们的模拟非常准确。我们可以通过更频繁地更新计算(例如,每 0.01 秒而不是每 0.02 秒)来改进模拟,但这样会减慢模拟速度。为了在速度和准确性之间取得良好的平衡,需要调整此参数。
尝试一下 7-10
打开应用并运行它,以理解它是如何工作的。然后试着将这个模拟转换成一个游戏。例如,你可以在舞台的右边缘以随机高度显示一个物体,并要求玩家尝试击中它。如果玩家未能击中目标,游戏可以提供一些关于调整发射角度和速度的提示。
其他应用
本书的额外资源(可在 nostarch.com/learnscratch/ 获取)包含了三个你可以自行探索的游戏,每个脚本都有详细的解释。第一个是一个教育性游戏,可以用来测试小学生的计数能力。它显示一定数量的便士,并要求玩家找出获得该金额所需的最少硬币数量。
MatchThat Amount.sb2
第二个应用是一个简单的太阳系行星运动模拟,其中包含太阳和一颗行星。第三个应用也是一个模拟,展示了单个气体分子与容器壁碰撞时的运动动态。
Orbit.sb2 Molecules InMotion.sb2
打开这些应用,运行它们,并阅读我的解释,以理解它们是如何工作的。如果你有兴趣挑战自己的编程技能,试着修改这些脚本,让它们做些新的事情!
总结
在本章中,我们探讨了在 Scratch 中重复命令的不同方式。我们首先研究了各种循环块,并解释了与之相关的技术术语。接着,我们讨论了确定性循环和不确定性循环,以及计数控制循环和条件控制循环之间的区别。我们探讨了repeat until块和forever if结构,并在多个示例中使用了它们。我还解释了 Scratch 的stop命令,以及如何使用它们来停止无限循环和过程。之后,我们讨论了如何使用循环来验证用户输入的数据。
你接着学习了如何使用计数器来跟踪循环执行了多少次,以及如何在嵌套循环中使用计数器来创建多维度的迭代。之后,我们探讨了递归——一种通过调用自身来实现重复的过程。在最后一部分,我们开发了几个应用程序,将这些新概念结合起来,创建了实用的程序。
下一章将扩展你在本章学到的知识,教你如何使用计数器和循环来处理字符串,并创建一类有趣的程序,例如二进制转十进制转换器、猜字游戏和一个教学分数的数学辅导程序。
如果你想进一步探索本章中的新概念,建议你尝试一些以下的练习题。
问题
| 问: | 1. 创建一个输入验证循环,仅接受 1 到 10 范围内的数字。 |
|---|---|
| 问: | 2. 编写一个脚本,询问用户:“你确定要退出吗 [Y, N]?”然后脚本检查用户的输入,仅接受字母 Y 和 N 作为有效答案。 |
| 问: | 3. 编写一个程序,计算并显示 1 到 20 之间所有整数的和。 |
| 问: | 4. 编写一个程序,计算并显示 1 到 20 之间所有奇数的和。 |
| 问: | 5. 编写一个程序,显示以下数列中的前 10 个数字(使用 say 命令):5, 9, 13, 17, 21, ....![]() |
| 问: | 6. 右侧的脚本是做什么的?实现该脚本并运行它,检查你的答案。 |
| 问: | 7. 如果一个整数 (x) 除以另一个整数 (y) 的余数为 0,我们就说 y 是 x 的因数。例如,1、2、4 和 8 是 8 的因数。下面的脚本会查找并显示一个给定数字的所有因数(不包括该数字本身)。研究这个脚本并解释它是如何工作的。当输入数字为 125、324 和 419 时,脚本的输出是什么?![]() |
| 问: | 8. 一个整数如果只被 1 和它本身整除,则称其为素数。例如,2、3、5、7、11 是素数,而 4、6 和 8 不是。下一页的程序会测试一个数是否是素数。研究这个程序并解释它是如何工作的。对于输入 127、327 和 523,这个程序的输出是什么?![]() |
| 问: | 9. 尽管第 8 问中的程序检查了输入值的一半范围内的所有整数,但实际上将上限设置为输入值的平方根就足够了。对程序进行这个修改,并进行测试,看它是否仍然给出相同的答案。 |
| 问: | 10. 数字序列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ....被称为斐波那契数列。数列中的前两个数字是 0 和 1。之后的每个数字都是前两个数字之和。编写一个程序,计算斐波那契数列的第n项,其中n由用户输入。 |
| 问: | 11. 请考虑以下程序及其生成的输出。重新创建该程序并运行,观察它是如何工作的。改变转向角度(从 10°开始)和递归调用中的参数(例如side + 1或side + 3,依此类推),以发现还能创造出什么其他形状。![]() |
第八章:字符串处理
字符串是一系列字符,作为一个整体单元来处理。你可以编写程序来组合、比较、排序、加密和以其他方式操作字符串。以下是你将在本章中学到的内容:
-
Scratch 如何存储字符串
-
如何使用 Scratch 中可用的字符串操作积木
-
多种字符串处理技巧
-
如何编写有趣的程序来处理字符串
我们将从详细了解字符串数据类型开始,然后编写程序来管理和操作字符串。这些程序将移除和替换字符、插入和提取子字符串,以及随机化字符顺序。之后,我们将使用这些程序并应用这些技巧来编写一些有趣且实用的应用程序。
重温字符串数据类型
正如我在 第五章中提到的,Scratch 有三种数据类型:布尔值、数字和字符串。从最简单的角度来看,字符串只是一个有序的字符序列。这些字符可以包括字母(大写和小写)、数字以及其他你可以在键盘上输入的符号(如 +、-、&、@ 等)。你可以在程序中使用字符串来存储名字、地址、电话号码、书名等信息。
在 Scratch 中,字符串的字符是按顺序存储的。例如,如果你有一个名为name的变量,执行命令将 name 设置为 Karen会按图 Figure 8-1 所示的方式存储这些字符。
Figure 8-1. 字符串作为字符序列存储。
你可以使用letter of操作符访问字符串中的单个字符。例如,letter 1 of name块返回字母K,而letter 5 of name块返回字母n。Scratch 还提供了length of操作符,它返回字符串中的字符数。如果你将这两个操作符与repeat积木一起使用,你可以计数字符、检查多个字符并做许多其他有用的事情,正如我将在接下来的小节中演示的那样。
统计字符串中的特殊字符
我们的第一个示例脚本,如图 Figure 8-2 所示,用于统计输入字符串中有多少个元音。它要求用户输入一个字符串,然后统计并显示该字符串中的元音数量。
VowelCount.sb2
Figure 8-2. 元音计数程序
程序逐个检查输入字符串中的每个字母,并查找元音字母。每次找到一个元音字母时,它都会将名为vowelCount的变量加 1。脚本使用名为pos(position的缩写)的变量来跟踪正在检查的字符的位置。让我们更详细地探讨这个脚本。
首先,脚本要求用户输入一个句子①。Scratch 会自动将用户的字符串保存在内置的answer变量中。然后,它将vowelCount设置为 0(因为尚未看到任何元音字母),并将pos设置为 1 以访问输入字符串的第一个字母。
接下来,repeat循环②检查输入字符串中的每个字母。length of运算符报告输入字符串中的字符数,这决定了循环应重复多少次。
在每次迭代中,循环使用ch(字符的缩写)检查输入字符串的一个字符③。在循环的第一次迭代中,ch被设置为answer的第一个字母。第二次迭代将ch设置为第二个字母,依此类推,直到循环到达字符串的末尾。pos变量用于访问所需的字符。
if模块接着检查所检查的字符是否为元音字母④。如果字符是元音字母,无论是大写还是小写,vowelCount都会加 1。
检查完一个字符后,循环将pos加 1⑤并重新开始读取下一个字符。当输入字符串中的所有字母都被检查完后,循环结束,程序会使用say模块显示它计数的元音字母数量。
本例中使用的技术将在本章的其余部分多次应用。加载脚本VowelCount.sb2,运行几次,并确保你完全理解它。
比较字符串字符
我们的第二个例子检查用户输入的整数是否为回文数。回文数是一个数字(或文本字符串),无论是从前往后还是从后往前读都相同。例如,1234321 和 1122332211 是回文数。同样,Racecar、Hannah 和 Bob 也是一些文本回文数。为了说明我们的回文检测算法,假设输入的数字是 12344321,如图 8-3 所示。
Palindrome.sb2
图 8-3. 使用两个变量检查数字是否为回文数
要检查一个数字是否是回文数,我们需要比较第一个和第八个数字,第二个和第七个数字,第三个和第六个数字,依此类推。如果任何一次比较结果为假(即两个数字不相等),那么该数字就不是回文数。实现此回文数测试算法的程序如图 8-4 所示。
图 8-4. 该程序测试用户输入的整数是否为回文数。
该脚本使用两个变量(图 8-3 中的pos1和pos2)来访问需要比较的数字,这两个变量分别朝相反的方向移动。第一个变量(pos1)从第一个数字开始并向前移动,而第二个变量(pos2)从最后一个数字开始并向后移动。所需的比较次数最多为输入数字的数字个数的一半。对于输入的数字 12344321,我们最多需要进行四次比较,因为该数字有八个数字。(如果输入的整数有奇数个数字,逻辑也是相同的,因为中间的数字不需要进行比较。)一旦程序确定用户的数字是否为回文数,它会显示一个包含结果的消息。
试一试 8-1
加载Palindrome.sb2并运行它,以了解其工作原理。由于 Scratch 处理小数重复次数的方式,如果输入数字的位数是奇数,脚本会多进行一次比较,比较围绕中间数字的两个数字。尝试修复程序,在输入数字为奇数位时执行正确的重复次数。
Palindrome.sb2
在下一节中,我们将探索一些最常见的字符串操作,并了解在 Scratch 中编写字符串操作过程的一些策略。
字符串操作示例
letter of 操作符只能读取字符串的单个字符。如果你想在字符串中插入字符(或删除字符),你必须自己完成所有的工作。
在 Scratch 中,你不能更改字符串中的字符,因此改变字符串的唯一方法是创建一个新字符串。例如,如果你想把字符串“jack”的第一个字母大写,你需要创建一个新的字符串,其中包含字母J,然后是剩下的字母ack。这个思路是使用letter of 操作符来读取原始字符串中的字母,并根据需要使用join操作符将这些字母附加到新字符串中。
在本节中,我们将开发一些简单的应用程序,展示常见的字符串操作技巧。
Igpay Atinlay
如果我们的精灵能够说一种秘密语言呢?在这一节中,我们将教它们一种名为猪拉丁语的编码语言。我们创建猪拉丁语单词的规则很简单。要将一个单词转换为猪拉丁语,将第一个字母移到单词的末尾并加上ay。因此,单词talk变成alktay,fun变成unfay,依此类推。现在你知道规则了,你能猜出这一节的标题最初是什么吗?
PigLatin.sb2
我们用来将单词转换为猪拉丁语的策略在图 8-5 中进行了说明,使用的单词是scratch。
图 8-5. 如何将英文单词转换为猪拉丁语
我们首先会一个一个地将输入单词中的所有字母(除了第一个字母)附加到输出单词中 ①。然后,我们将输入单词的第一个字母添加到输出中 ②,接着是ay ③。我们的PigLatin过程实现了这些步骤,如图 8-6 所示。
该过程使用三个变量来创建我们的编码单词。变量outWord保存组装中的输出字符串。一个名为pos(表示位置)的计数器告诉脚本从原始字符串中哪个字符应该附加到outWord。最后,一个名为ch的变量保存输入字符串中的一个字符。该过程将你想要翻译成猪拉丁语的单词作为参数,命名为word。
图 8-6. PigLatin 过程
首先,程序创建一个空字符串作为outWord,并将pos设置为 2 ①。(空字符串是一个不包含任何字符的字符串;它的长度为 0。)然后,程序使用repeat块将输入字符串(word)中除了第一个字母之外的所有字母附加到输出字符串(outWord)中 ②。由于我们跳过了第一个字符,因此重复次数比输入字符串的长度少一个。在每次循环迭代中,word的一个字符被附加到outWord。循环结束时,word的第一个字母被附加到outWord ③,后面跟着字母ay ④。
试一试 8-2
加载PigLatin.sb2并运行它来测试这个过程。应用程序会要求输入一个单词,然后输出它的猪拉丁语翻译。修改它以翻译一个短语,比如“Would you like some juice?”(提示:对于每个单词调用PigLatin,以拼凑输出短语。)作为另一个挑战,编写一个过程,输入一个猪拉丁语单词并显示其原始英文单词。
PigLatin.sb2
修正我的拼写
在本节中,我们将开发一个简单的游戏,该游戏生成拼写错误的单词,并要求玩家输入正确的拼写。该游戏通过在英文单词的随机位置插入一个随机字母来生成拼写错误的单词。当然,拼写错误的简单单词可能有多个正确拼写。例如,如果原始单词是wall,而游戏生成了mwall,那么mall或wall都是正确的。为了保持游戏的简单性,我们将忽略这种可能性,并坚持特定的拼写作为正确答案。
FixMySpelling.sb2
首先,让我们创建一个通用的过程,在字符串的特定位置插入字符。这个过程叫做Insert,它有三个参数:输入的单词(strIn)、要插入的字符串(或字符)(strAdd)以及你想要插入这些新字符的位置(charPos)。该过程生成一个新字符串(strOut),其中strAdd被插入到strIn的正确位置,就像图 8-7 中的例子所示。
图 8-7. 插入过程示意图
我们将一个一个地将strIn中的字符添加到strOut中。当我们到达charPos时,我们将在strOut中添加strAdd中的字符,然后再附加strIn中charPos位置的字母。完整的过程如图 8-8 所示。
图 8-8. 插入过程
首先,过程将strOut初始化为空字符串,并将pos设置为 1,以访问输入字符串的第一个字母①。然后,它开始一个repeat循环,将strIn的字母一个一个地附加到strOut中②。每次迭代都会获取strIn的下一个字母,并将其放入ch变量中③。如果当前字符的位置与charPos匹配,过程会将strAdd附加到strOut中④。在所有情况下,ch都会被附加到strOut中⑤,pos会增加以访问strIn的下一个字母⑥。
现在我们有了Insert过程,让我们看一下游戏的主脚本,如图 8-9 所示。
图 8-9. Fix My Spelling 游戏的主脚本
alpha字符串包含所有字母的字母表。它将提供随机字母来插入到我们要拼写错误的单词中①。脚本从预制的单词列表中随机挑选一个单词,并将该单词保存为inWord②。你将在下一章学习更多关于列表的内容;目前,只需将此列表看作一个单词库。接下来,脚本从alpha中随机选择一个字母(randChar)③,并选择一个随机位置(randPos)将这个字母插入到inWord中④。然后,脚本调用我们的Insert过程来创建拼错的单词(strOut)⑤。之后,脚本启动一个循环来获取玩家的答案⑥。在循环中,脚本会要求玩家输入正确的拼写⑦,并使用if/else块来检查答案⑧。如果玩家的答案与原始单词(inWord)匹配,则游戏结束;否则,玩家需要重新尝试。
尝试它 8-3
加载FixMySpelling.sb2并多次播放,以理解它是如何工作的。你能修改游戏,使得混淆的词包含两个额外字母,而不仅仅是一个吗?
FixMySpelling.sb2
解开混乱
我们的最后一个示例展示了另一个稍微更具挑战性的单词游戏。我们将从一个英文单词开始,打乱其字母,并要求玩家猜出原始单词。
Unscramble.sb2
让我们首先创建一个过程,将给定字符串的字符按随机顺序重新排列。调用者设置输入字符串(strIn),而名为Randomize的过程则修改该字符串,将其字符打乱,如图 8-10 中所示。
图 8-10. 展示 Randomize 过程
我们将从strIn中随机挑选一个字母,并将该字母附加到临时字符串 str1中。(这个临时字符串从一开始就是空的,我们将在构建混淆词时将其存储在这里。)然后,我们会从strIn中移除该字母,以避免重复使用,直到strIn为空,整个过程才会结束。Randomize过程按照图 8-11 中所示的步骤来实现这些操作。
图 8-11. Randomize 过程
首先,Randomize将len设置为输入字符串strIn的长度,并清空临时字符串str1①。接下来,过程开始一个重复循环来组装打乱的单词②。重复次数等于输入字符串的长度。每次循环迭代,我们从strIn中随机选择一个位置③,并将该字母追加到str1中④。请注意,我们在步骤③中使用了长度,因为strIn及其长度会在循环内部发生变化。之后,我们调用名为删除的过程来删除我们刚刚使用的字符⑤。当循环结束时,strIn被设置为打乱后的单词(str1)⑥。
删除过程,如图 8-12 所示,允许我们避免将同一个字母添加到已经打乱的单词中两次。它会从strIn中删除你通过charPos参数指定的位置的字符。
图 8-12。删除过程
这个过程使用了另一个临时字符串,名为str2,来构建我们想要创建的新字符串。它首先清空str2并设置一个循环计数器n为 1,以访问strIn的第一个字符①。然后,过程开始一个重复循环,来组装输出字符串②。如果我们不想删除当前字符,就将它追加到str2中③。接着,循环计数器增加 1,访问strIn的下一个字母④。当过程结束时,strIn被设置为新单词(str2)⑤。
现在我们准备探索游戏的主要脚本,如图 8-13 所示。
图 8-13。Unscramble 游戏的主要脚本
脚本从列表中随机选择一个单词,并将该单词保存到inWord中①。然后,它将strIn设置为inWord②,并调用Randomize来打乱strIn中的字符③。接着,脚本开始一个循环以获取玩家的答案④。在循环内部,脚本要求玩家输入未打乱的单词⑤,并使用if/else块来检查答案⑥。这个部分与我们在上一节的 Fix My Spelling 游戏中所做的完全相同。
前面的例子只是你可以对字符串执行的不同操作的一小部分。尝试使用相同的技巧在你自己的项目中修改字符串!
在本章的其余部分,我们将探索一些使用字符串来解决有趣问题的程序。
Scratch 项目
你刚才看到的过程演示了处理字符串的基础知识。在本节中,我们将应用所学的内容,编写几个实际应用。在这个过程中,你将学到一些新的编程技巧,可以用在你自己的创作中。
Shoot
这个游戏旨在通过有趣和富有吸引力的方式教授相对运动的概念。游戏的目标是估算舞台上两个物体之间的转角和移动距离。游戏的用户界面如 图 8-14 所示。
Shoot.sb2
图 8-14. 射击游戏的用户界面
游戏开始时,系统会将 Player 和 Target 精灵随机放置在舞台上的某个位置。接下来,它会提示玩家估算转角和 Player 精灵需要移动的距离,以便击中 Target。然后,Player 精灵会根据玩家输入的数字进行移动。如果精灵停在 Target 周围的某个半径范围内,玩家就赢得游戏。否则,Player 精灵将返回初始位置,玩家可以再次尝试。当点击绿色旗帜图标开始游戏时,Player 精灵运行 图 8-15 中所示的脚本。
图 8-15. 点击绿色旗帜图标时运行的 Player 精灵脚本
脚本广播 NewGame 来指示 Helper 精灵为 Player 和 Target 精灵分配新的位置①。Helper 精灵运行一个简单的过程(未显示),该过程使用随机数更新以下五个变量,以确保 Player 和 Target 在舞台上可见(并且保持一定距离):
XPlayer 和 YPlayer |
Player 精灵的 x 和 y 坐标 |
|---|---|
XTarget 和 YTarget |
Target 精灵的 x 和 y 坐标 |
initAngle |
Player 精灵的初始方向 |
一旦脚本为 Player 和 Target 设定了新位置,它会广播 StartGame 来将 Target 精灵移动到新的位置②。(Target 精灵的脚本未在此显示。)接着,脚本进入一个无限循环,为玩家提供多次机会击中目标③。该循环将在玩家击中目标时通过 stop all 命令(在 CheckAnswers 过程里)终止。
每次循环迭代都会设置 Player 精灵的初始位置和方向,并清除舞台 ④ 上所有的画笔痕迹,以删除之前猜测所留下的轨迹。脚本接着广播 GetAnswers ⑤,响应时,Helper 精灵提示玩家输入答案,如图 8-16 所示。然后,Helper 精灵将答案拆分为两部分(逗号前后),并相应更新 angle 和 distance。跟随图 8-16 上的注释,了解这个脚本如何工作。
然后,Player 精灵在玩家的指示下移动,且其画笔处于下压状态⑥。这样,玩家可以看到一条视觉轨迹,帮助他们在下一轮中更好地估计位置。
图 8-16. GetAnswers 脚本
最后,Player 精灵执行CheckAnswers程序,检查它是否足够接近目标。游戏仅在 Player 精灵与目标非常接近时结束。图 8-17 展示了 Player 精灵如何检查与目标的距离。
Player 精灵使用distance to积木检查它与 Target 精灵的距离。如果距离小于 20 步,游戏认为这是一次命中,并显示:“你赢了!”否则,射击尝试被视为未命中,forever 循环重新开始,玩家获得下一次机会。
图 8-17. Player 精灵的 CheckAnswers 程序。试试 8-4
修改 Shoot 游戏,记录玩家击中目标的次数,并根据此为玩家分配相应的分数。
二进制转十进制转换器
二进制(基数 2)数字只有两个可能的数字:0 和 1。大多数计算机使用二进制数字进行操作和通信。然而,人类更喜欢使用十进制(基数 10)系统进行计算。在这一部分,你将开发一个应用程序,将二进制数字转换为其十进制等效值。以后,你可以将其作为游戏来测试你进行这种转换的能力。
BinaryToDecimal.sb2
我们首先讨论如何将二进制转换为十进制。图 8-18 展示了一个使用二进制数字 10011011 的示例。
图 8-18. 将二进制数字转换为十进制数字
我们需要做的就是将每个二进制位乘以其对应的位置值,并将结果相加。位置值对应于从右到左基数的逐次幂,第一个位置的幂为 0。由于二进制是基数为 2,因此最右边的数字具有位置值 2⁰ = 1,所以你将该数字乘以 1。接着,下一位乘以 2¹ = 2,再下一位乘以 2² = 4,以此类推。
图 8-19 展示了二进制到十进制转换应用程序的用户界面。该程序要求用户输入一个 8 位的二进制数。然后,它将在舞台上显示输入的数字,并用Bit精灵展示,Bit精灵使用两种服装来表示 0 和 1。程序还会计算等效的十进制数字,并且Driver精灵(其服装为计算机样式)将该值展示给用户。
图 8-19。二进制到十进制转换程序 试一试 8-5
为了检查你的理解,练习以下二进制转十进制的转换: (a) 1010100,(b) 1101001,和 (c) 1100001。
当绿色旗帜图标被点击时,程序开始运行。该事件由Driver精灵捕获,Driver精灵执行图 8-20 中所示的脚本。
图 8-20。Driver精灵的脚本
该脚本准备舞台,并要求用户输入一个二进制数字,以便Bit精灵开始新的转换轮次。当Bit精灵完成任务后,Driver精灵将显示计算出的十进制值,该值由Bit精灵在一个名为decimal的共享变量中计算并存储。
Bit精灵在接收到Initialize消息后执行的脚本如图 8-21 所示。
图 8-21。Bit精灵的Initialize脚本
该脚本绘制了一个表示八个零的位模式。在稍后的说明中,你将看到,每当用户输入的字符串中出现二进制 1 时,脚本会在相应的位上盖上数字 1 的服装。当用户输入需要转换的二进制数字时,Bit精灵应该接收BinaryToDecimal消息,并执行图 8-22 中所示的脚本。
图 8-22。Bit精灵的BinaryToDecimal脚本
首先,转换程序初始化它将使用的所有变量①:
-
length是用户输入的二进制数字的位数。 -
pos指向输入数字的最右边的数字。 -
weight从最右边二进制数字的位值开始。 -
decimal设置为 0,但将在最后存储转换结果。 -
xPos从图像最右边二进制数字的 x 坐标开始。
在repeat循环②内部,程序检查每个数字,看看它是 1 还是 0。如果循环发现是 1③,它将当前weight的值加到decimal中,并在数字 0 的图像上盖上数字 1 的服装。
在循环的末尾,脚本在进入下一次迭代之前更新几个变量:
-
pos更新为指向我们刚处理的数字左侧的数字。 -
xPos对齐到下一个数字图像的中心,以防我们需要盖上新的图像。 -
weight乘以 2,这意味着随着循环的迭代,它将取值为 1、2、4、8、16 等。
试试看 8-6
让Driver角色在广播BinaryToDecimal消息给Bit角色之前,验证用户输入的数字。你需要验证:(1)用户输入的数字是一个二进制数字(即,它只包含 1 和 0),以及(2)输入的长度最多为八位。
刽子手
在本节中,我们将编写一个经典的刽子手游戏。图 8-23 展示了游戏的实际操作。
Hangman.sb2
图 8-23. 刽子手游戏的用户界面
程序随机选择一个秘密的六个字母的单词,并为每个字母显示一个问号。玩家有八次机会猜测单词中的字母。如果玩家猜对了字母,程序会显示该字母在单词中所有出现的位置。否则,程序会展示刽子手形象的一部分(头、身体、左臂等等)。经过八次错误的猜测后,程序完成刽子手的形象,玩家输掉游戏。如果玩家在八次尝试或更少的次数内猜对了秘密单词,则结果为胜利。该应用包含以下四个角色:
Driver 这个角色在游戏开始时隐藏自己,提示玩家输入猜测,并处理玩家的答案。当游戏结束时,角色显示以下两种服装之一:
刽子手 这个角色展示了刽子手逐步形成的图像。它总共有九个服装,每个服装展示了刽子手身体的一个新增部位,如图 8-24 所示。
图 8-24. Hangman角色的九个服装
New 这个角色在舞台上显示
New按钮。Helper 这个不可见的角色显示玩家猜测的字母以及剩余的猜测次数。它使用七个带有监视器的变量,监视器配置为大显示屏并放置在舞台上的正确位置。通过使用不同的角色来更新显示,将游戏逻辑与用户界面分开。例如,您可以更改此角色,在舞台上显示更花哨的字母,而不会影响应用程序的其他部分。
当玩家按下New角色(New按钮)时,它会广播NewGame消息,通知Driver角色游戏开始了。当Driver角色接收到此消息时,它会执行图 8-25 中显示的脚本。
图 8-25. Driver角色的NewGame脚本
脚本重置游戏的用户界面①,并启动一个循环②来读取字母猜测。另一个由Driver角色调用的过程将在检测到游戏结束条件时,通过stop all块终止此循环。
在每次循环迭代中,Driver角色会要求玩家猜一个字母并等待输入③。当玩家输入猜测时,脚本调用ProcessAnswer,该过程会更新一个标志(名为gotLetter),用于指示字母是否正确。
当ProcessAnswer返回时,脚本检查gotLetter标志④,并根据玩家的猜测是否正确采取行动。我接下来将解释NewGame调用的过程,从图 8-26 中的脚本开始。
图 8-26. 从初始化过程触发的脚本
在初始化期间,Driver角色会隐藏自己,将displayWord初始化为包含六个问号的字符串,并将remAttempts(玩家剩余猜测次数)设置为 8。然后,它会从预定义的六个字母单词列表中选择secretWord。接下来,过程广播Update,以便Helper角色将其变量(在舞台上可见的监视器)赋值为正确的数值。最后的指令广播Reset消息给Hangman角色。当Hangman角色接收到此消息时,它会切换到其start服装,显示一个空的绞刑架。
现在让我们考虑一个简单的例子,帮助我们理解ProcessAnswer过程的作用(见图 8-27)。假设秘密单词是across,这是游戏的第一轮(也就是说displayWord是“??????”)。如果玩家的第一次猜测是r,那么ProcessAnswer应该将gotLetter设置为 1,表示猜对了,将displayWord设置为“??r???”,显示字母的位置,并将qmarkCount(更新后的显示字符串中的问号数量)设置为 5。当qmarkCount为 0 时,玩家已经猜出了秘密单词中的所有字母。ProcessAnswer属于Driver精灵,完整的脚本可以在图 8-27(左)中看到。
ProcessAnswer开始时将gotLetter标志和qmarkCount都重置为 0。它会在每个未知字母出现时,将qmarkCount增加 1。临时变量temp用于在每次猜测后构建显示字符串,初始化为空字符串。pos变量用作循环计数器。
图 8-27. ProcessAnswer 过程
循环检查secretWord的每个字母,使用pos作为索引。如果检查到的字母(保存在char中)等于猜测的字母(保存在 Scratch 的内置answer变量中),则将gotLetter标志设置为 1。否则,char变量会被设置为displayWord变量中对应位置的字母。不论哪种情况,脚本都会将char添加到temp的末尾,如图 8-27(右)所示。
当循环结束时,displayWord变量将包含六个字母,这些字母将在舞台上显示,并考虑到用户最近的猜测。该循环还会跟踪显示字符串中问号的数量。如果没有问号,则说明用户已经成功猜出秘密单词。
当ProcessAnswer返回时,NewGame消息处理程序会检查gotLetter,以查看玩家是否猜对了。如果没有,它将调用ProcessWrongGuess,如图 8-28 所示。
该过程广播WrongGuess,通知Hangman精灵显示其下一个服装,然后将剩余猜测次数减 1。如果用户已经没有猜测机会,脚本会揭示秘密单词并结束游戏。否则,脚本广播Update消息,显示玩家剩余的猜测次数。
图 8-28. ProcessWrongGuess 过程
如果玩家的字母正确,则应调用图 8-29 中显示的 ProcessCorrectGuess,而不是 ProcessWrongGuess。
图 8-29. ProcessCorrectGuess 程序
ProcessCorrectGuess 广播 Update 消息,显示玩家正确猜出的字母。接着,它检查 qmarkCount 的值。如果 qmarkCount 为 0,说明玩家已正确猜出了所有字母,于是 Driver 精灵显示其 win 服装并结束游戏。
尝试一下 8-7
Hangman 程序不验证用户输入;你可以输入一个非字母字符甚至是整个单词。修改程序,使其拒绝用户的任何无效输入。
分数导师
在我们的最后一个例子中,我们将展示一个教学分数的教育游戏。该游戏的界面如图 8-30 所示。玩家可以选择一个运算符(+、–、× 或 ÷),并点击 New 按钮创建一个新问题。当玩家输入答案并点击 Check 按钮时,Teacher 精灵(一个女性形象)会检查答案并提供适当的反馈消息。
FractionTutor.sb2
图 8-30. 分数导师应用程序的用户界面
该应用程序包含六个精灵。Operation 允许玩家选择数学运算。Read 显示答案输入按钮,New 显示 New 按钮,Check 显示 Check 按钮。Teacher 精灵检查玩家的答案,而一个名为 Digit 的隐形精灵在舞台上打印出与当前问题对应的数字。
当玩家点击 New 精灵(New 按钮)时,它会执行图 8-31 中显示的脚本。该脚本将 1 到 9 之间的随机值分配给两个操作数的分子和分母,这些值由四个变量 num1、den1、num2 和 den2 表示。然后,它广播一个 NewProblem 消息,告诉 Digit 精灵在舞台上打印这些数字。
图 8-31. New 精灵的脚本
Digit精灵有 12 个服装(命名为d1至d12),如图 8-32(右图)所示。当该精灵接收到NewProblem广播时,它会盖上代表两个操作数的分子和分母的服装。图 8-32 还展示了执行实际盖图过程的步骤。
图 8-32。Digit精灵的功能
该过程使用嵌套的if/else块来确定哪个服装对应于要盖上的数字。注意,数字 1 到 9 的服装名称是通过join运算符组合而成的。切换到正确的服装后,Digit精灵会移动到指定的(x,y)位置,并在该位置盖上服装的图像。
当新问题显示时,用户可以点击Read按钮输入答案。与此按钮关联的脚本如图 8-33 所示。脚本中将玩家的答案解析为两个标记(分子和分母)的部分与图 8-16 中提取Shoot游戏中answer的angle和distance部分类似,因此此处不再显示。有关完整过程,请查看FractionTutor.sb2文件。
图 8-33。Read精灵的脚本
首先,用户被要求输入一个分数形式的答案(例如,3/5 或–7/8)。然后,脚本提取答案字符串的分子和分母(它们由除号分隔),并分别将它们赋值给num3和den3变量。例如,如果用户输入–23/15,num3将被设置为–23,den3将被设置为 15。之后,脚本广播一个GotAnswer消息,告诉Digit精灵在舞台上展示用户的答案。当Digit精灵接收到该消息时,它会以与显示两个操作数分子和分母相同的方式,在舞台上正确的位置盖上num3和den3的数字。您可以查看FractionTutor.sb2文件了解详情。
输入答案后,用户可以点击Check按钮查看答案是否正确。Check精灵的脚本广播一个CheckAnswer消息,通知其他精灵用户的请求。该消息由Teacher精灵捕获并处理,Teacher精灵将执行图 8-34 所示的脚本。
图 8-34. CheckAnswer脚本
当前Operation精灵的服装告诉我们需要执行哪种操作过程(加法、减法、乘法或除法)①。操作以num1、den1、num2和den2为输入,设置ansNum和ansDen的值,分别代表正确答案的分子和分母。这四个过程如图 8-35 所示。
在找到答案后,CheckAnswer需要将其简化。例如,2/4 应该简化为 1/2。为了执行此简化,脚本首先找到分子和分母的最大公约数(GCD),也称为最大公因数②。(稍后我们将看这个过程。)
图 8-35. Teacher精灵的加法、减法、乘法和除法过程
找到 GCD 后,脚本将ansNum和ansDen分别除以该值③,并调用GiveFeedback④来显示用户的答案是否正确。
现在让我们更仔细地看看这些过程的细节,从图 8-35 中展示的四个操作过程开始。
这些过程计算执行类似以下形式的操作结果:

并将结果存储在两个变量中(ansNum和ansDen),分别对应答案的分子和分母。
现在让我们继续讲解FindGCD过程,如图 8-36 所示。
图 8-36. Teacher精灵的 FindGCD 过程
让我们跟踪FindGCD的操作,当num1 = –10 且num2 = 6 时。我们需要找到一个最大的正整数,它能整除num1和num2而没有余数。过程从将gcd(结果)设置为两个数的较小绝对值开始,在本例中为–6。然后,循环测试数字 6、5、4,以此类推,直到num1和num2都能被检查的数字整除。这就是我们要找的结果。在本例中,gcd将被设置为 2,因为两个数(–10 和 6)都能被 2 整除且没有余数。
最后一个需要检查的过程是GiveFeedback过程,它将用户的答案与正确答案进行比较,并显示适当的信息,如图 8-37 所示。该图还展示了一些示例,演示了if/else结构的不同情况。
图 8-37. Teacher 精灵的 GiveFeedback 过程 尝试 8-8
修改分数辅导程序,以跟踪正确和错误答案的数量。设计一个计算分数并显示给用户的方案。
总结
字符串处理是一个重要的编程技能。在本章中,你学习了如何访问字符串的单个字符,以便将它们组合、比较、删除和重新排列。
我们从详细了解字符串数据类型以及字符串如何作为字符序列存储开始。然后我们编写了几个程序,演示了基本的字符串操作技术。之后,我们使用这些技术编写了几个有趣且实用的应用程序。这些项目中发展出的概念可以应用于许多其他领域,我真心希望它们能帮助你想到自己的项目。
在下一章中,你将学习关于列表的内容,以及如何使用它们来存储和操作一组值。掌握了这一新的数据结构后,你将拥有编写专业程序所需的所有工具。
问题
| 问: | 1. 编写一个程序,要求用户输入一个单词,然后输出该单词 N 次,其中 N 是输入单词中字符的数量。 |
|---|---|
| 问: | 2. 编写一个程序,要求用户输入一个单词。程序随后确定字母 a 在输入单词中的出现次数。 |
| 问: | 3. 编写一个程序,读取用户输入的一个单数英语名词。程序随后生成该名词的复数形式。(提示:检查输入单词的最后一个字母和倒数第二个字母。)为了保持程序简单,仅考虑以下规则:如果单词以 ch、x 或 s 结尾,则加 es 构成复数,否则只加 s。 |
| 问: | 4. 编写一个程序,读取用户输入的一个字符(介于 a 和 z 之间),并输出该字符在字母表中的位置(a = 1,b = 2,c = 3,依此类推)。大写字母和小写字母应当视为相同。(提示:定义一个名为 alpha 的变量,存储字母表中的字母,正如我们在图 8-9 中所做的,然后使用循环查找输入字符在变量 alpha 中的位置。) |
| 问: | 5. 编写一个程序,要求用户输入一个字母,然后显示该字母前面的字母。(提示:使用与上一问题中相同的技巧。) |
| 问: | 6. 编写一个程序,读取用户输入的正整数,然后找到并显示其各位数字的和。例如,如果用户输入3582,程序应该显示18(3 + 5 + 8 + 2)。 |
| 问: | 7. 编写一个程序,读取用户输入的一个单词,然后使用说积木以反向顺序显示字母。 |
| 问: | 8. 编写一个程序,从用户那里获取一个数字,并在每对数字之间插入一个空格。例如,如果输入数字是1234,则输出字符串应为1 2 3 4。(提示:通过在输入数字的每个单独数字之间加入空格来构建输出变量。) |
| 问: | 9. 在这个问题中,您将创建一个游戏,让玩家比较分数。用户界面如下所示。当点击新建按钮时,游戏会随机选择两个分数进行比较。用户通过点击运算符按钮选择小于(<)、大于(>)或等于(=)。当用户点击检查按钮时,游戏会检查答案并提供反馈。打开文件CompareFractions.sb2并添加必要的脚本来完成游戏。Compare Fractions.sb2![]() |
第九章 列表
到目前为止,我们编写的程序使用普通变量来存储单一数据。但是,当你需要存储一堆值时,比如朋友的电话号码、书名或一个月的温度读数,这种变量就不太实用了。
例如,如果你希望你的程序记住 20 个朋友的电话号码,你需要 20 个变量!显然,编写和维护一个包含 20 个变量的程序会非常繁琐。在本章中,我们将探索另一种内建数据类型——列表,它提供了一种方便的方式来组织相关的值。以下是我们将要讨论的内容:
-
如何创建和操作列表
-
初始化并访问列表中的单个元素
-
基本的排序和搜索技巧
-
使用列表创建强大的应用程序
首先,我将解释如何在 Scratch 中创建列表,演示你可以使用的命令,并展示如何用用户输入的数据填充列表。接下来,我们将讨论数字列表和对其执行的常见操作,例如查找最小值、最大值和元素的平均值。之后,我们将学习一种用于对列表中的元素进行排序的算法。最后,我们将通过几个示例程序展示列表的一些实际应用。
Scratch 中的列表
列表就像一个容器,你可以在其中存储和访问多个值。你可以把它看作一个有许多抽屉的衣柜,每个抽屉存储一个项目。当你创建一个列表时,你需要为它命名,就像你为变量命名一样。然后,你可以通过它们在列表中的存储位置来访问列表的单个元素。图 9-1 中展示了一个名为dayList的列表,它存储了星期一到星期日的名称。
你可以通过存储索引(或位置)来引用列表中的项。在 Scratch 中,第一个项的索引为 1,第二个项的索引为 2,依此类推。例如,由于星期二是列表中的第三项,因此它的索引为 3。因此,你可以通过“item 3 of dayList”命令来引用我们的dayList中的第三个元素。
让我们直接开始在 Scratch 中创建一些列表。我们还将了解一些可以帮助我们管理和操作程序中列表的命令,并学习 Scratch 如何响应无效的列表命令。
图 9-1:包含星期几的列表
创建列表
创建列表几乎与创建变量相同。选择数据面板并点击创建列表,以弹出图 9-2(右)中的对话框。接下来,输入列表的名称(我们将使用 dayList)并指定其作用域。选择“对所有精灵有效”选项会创建一个全局列表,应用程序中的任何精灵都可以访问,而选择“仅对该精灵有效”选项会创建一个局部列表,仅属于当前选定的精灵。局部列表只能被所属的精灵读取(和写入)。
当你点击确定确认输入时,Scratch 会创建一个新的空列表并显示与列表相关的块,如图 9-3 所示。这与创建新变量时看到的情况类似。空列表是指不包含任何项的列表。
你可以使用这些新命令在脚本运行时操作列表的内容。你可以添加新项、在特定位置插入项、删除某些项或替换现有项的值。
图 9-2. 在 Scratch 中创建列表类似于创建变量。
图 9-3. 可以与列表一起使用的命令和功能块
当创建一个新列表时,Scratch 还会在舞台上显示该列表的监视器,如图 9-4 所示。该列表最初为空,因此它的长度从 0 开始。你可以使用此监视器块在设计程序时向列表添加条目。
图 9-4. 新创建的列表的监视器显示在舞台上。
如果你知道要存储在列表中的数据(就像我们的 dayList 一样),你可以在此时将数据添加到列表中。图 9-5 展示了如何通过其监视器将天数添加到 dayList。
图 9-5. 填充 dayList
在左下角点击加号七次,创建七个条目,然后在每个编辑框中输入一周的某一天。使用 TAB 键在列表项之间导航。按一次 TAB 键会将下一个列表项高亮显示,带有黄色边框。再按一次 TAB 键会高亮选中的可编辑文本,并去掉黄色边框。如果在当前选中的项目周围有黄色边框时点击加号,新列表项会被添加到当前项之后;否则,它会被添加到当前项之前。试着导航列表吧!
练习 9-1
按照图 9-5 所示,使用星期几的名称填充dayList。
列表命令
图 9-3 描述了当我们创建dayList时,Scratch 添加的所有块。在本节中,我们将更详细地查看这些块,以便更好地理解它们的功能。
添加和删除
add命令将一个新项目添加到列表的末尾,而delete命令则从特定位置删除一个项目。图 9-6 展示了这些命令的实际应用。
脚本首先执行delete命令删除列表中的第二项,即“橙子”。然后,脚本使用add命令将“柠檬”添加到列表的末尾。
图 9-6. 使用 add 和 delete 改变列表内容前后的效果
add命令非常直接,但让我们更仔细地查看delete命令。你可以直接将要删除的元素的索引输入到块的参数框中,或者点击下拉箭头。下拉菜单(参见图 9-6)显示了三个选项:1、last 和 all。选择 1 删除列表中的第一个项目(“苹果”),选择 last 删除最后一个项目(“芒果”),或选择 all 删除列表中的所有项目。
插入和替换
假设您希望按字母顺序存储朋友们的姓名和电话号码,就像手机中的联系人列表一样。在制作列表时,您需要将每个朋友的联系信息插入到合适的位置。之后,如果某个朋友有了新电话号码,您将需要编辑列表来输入它。insert和replace命令可以帮助您完成这些任务。图 9-7 展示了如何在我们的phone列表中使用insert和replace命令的示例。
图 9-7。使用 insert 和 replace 命令更新电话号码列表
replace命令将位置为 3 的当前字符串替换为 Kim 的新电话号码。insert命令将在列表的第 4 个位置插入新朋友 Mark 的电话号码。请注意,现有的元素下移一个位置,为新的条目腾出空间。
在replace和insert命令中,点击项目编号的下箭头会显示一个下拉菜单,包含三个选项:1、last 和 random(见图 9-7)。如果选择 random,所选命令将随机选择一个项目编号。您将在本章后面看到这一功能的一些有用应用。
访问列表元素
正如我们之前提到的,您可以使用该元素的索引来访问列表中的任何元素。例如,图图 9-8 中的脚本演示了使用item of块来访问我们的dayList中的元素。该脚本使用了一个名为pos(位置的缩写)的变量,通过它来迭代列表中的每个元素,并通过say命令显示内容。
图 9-8。该脚本使得精灵显示我们的dayList中的七天。
该脚本将pos的初始值设为 1,以便访问dayList中的第一个元素,然后进入循环。该循环的重复次数设置为 7,这是我们列表中元素的数量。在每次循环中,循环将显示索引等于pos的列表项,并将pos的值递增,以便访问下一个元素。换句话说,我们正在使用pos作为索引来定位列表中的特定元素。
练习 9-2
将 repeat 循环中的数字 7 替换为dayList 的长度积木。这通常是你在不知道列表包含多少项时遍历列表的做法。另外,在item of积木的第一个下拉菜单中选择random。这样应该会使脚本从列表中随机显示一个项目。
contains 积木
你可以通过使用contains来检查某个字符串是否在列表中,这是一个布尔型积木,它会根据列表是否包含该字符串返回真或假。图 9-9 展示了这个积木的一个使用示例。由于dayList包含字符串“Friday”,因此if积木内的say命令将被执行。
注意
该 contains 积木是不区分大小写的。例如,积木 dayList contains friDAY也会返回 true。
图 9-9. 使用 contains 积木检查字符串是否在列表中
边界检查
四个列表积木(delete、insert、replace和item of)需要一个输入参数来指定你想访问的项目的索引。例如,要删除dayList中的第七个元素,我们使用delete 7 of dayList。但你认为,如果使用无效的索引与这些积木之一一起使用,会发生什么呢?例如,如果你要求 Scratch 删除我们dayList中的第八个元素(它只有七个元素),Scratch 会如何响应?
试图访问超出列表边界的元素,严格来说,是一种错误。然而,Scratch 并不会显示错误信息或突然终止程序,而是默默地尝试用合适的方式处理出现问题的积木。因此,缺少错误信息并不意味着没有错误。你的代码中可能仍然存在问题,当这些问题出现时,你仍然需要修复它们。Scratch 不会抱怨积木中的无效索引,但结果通常不会是你预期的。表 9-1 展示了当你尝试使用越界索引访问dayList时可能发生的情况。
表 9-1. 使用无效列表索引的意外结果
| 命令或功能积木 | 结果 |
|---|---|
![]() |
返回空字符串,因为dayList只有七个项目。如果你使用小于 1 的索引,也会发生相同的情况。 |
![]() |
Scratch 忽略.9 并返回dayList中的第一个项目,“Sunday”。类似地,如果你要求获取第 5.3 个项目,Scratch 将返回第五个项目,“Thursday”。 |
![]() |
Scratch 会忽略此命令,因为它试图在列表中创建一个间隙。列表保持不变。 |
![]() |
这与add命令的效果相同。它将“Newday”添加到列表的末尾。 |
![]() |
此命令被忽略(因为 dayList 只有七个元素),列表保持不变。 |
表格 9-1 中的示例表明,尽管 Scratch 的积木在输入无效时会尝试做一些合理的处理,但它们不一定会做出正确的操作。你必须为你的程序提供正确的输入,这样它才能按你希望的方式工作。
到目前为止,我们的示例使用了通过其监视器手动创建的简单列表。现在的问题是:如果你在编写程序时不知道列表的内容该怎么办?例如,你可能需要创建一个用户输入的数字列表,或者每次运行程序时用随机值填充一个列表。我们将在接下来解决这个问题。
动态列表
列表之所以强大,是因为它们可以在程序运行时动态增长或缩小。例如,假设你正在编写一个成绩册应用程序,教师可以输入学生的测试成绩以供进一步处理。(教师可能需要为一个班级找出最大分数、最小分数、平均分、中位数等。)然而,每个班级的学生数量可能不同。教师可能需要为班级 1 输入 20 个成绩,为班级 2 输入 25 个成绩,依此类推。你的程序如何知道教师已经完成了成绩输入?本节将解答这个问题。
首先,我们将介绍两种方法来填充用户输入的数据到列表中。然后,我们将探讨数字列表,并查看在它们上面执行的一些常见操作。一旦你理解了这些基本概念,你就能准备好将这些技巧应用到自己的应用程序中。
使用用户输入填充列表
填充列表的常见方法有两种。在第一种方法中,程序首先询问将有多少条输入数据,然后开始一个循环来收集用户的输入。演示此技术的脚本见于图 9-10。
图 9-10. 询问用户将输入多少分数
一旦用户告诉脚本期望输入多少个分数,脚本会开始一个循环,循环的重复次数等于用户的输入。每次循环迭代中,脚本会要求用户输入一个新的分数,并将该值添加到名为 scoreList 的列表中。
动态填充列表的第二种方法是让用户输入一个特殊的值(称为哨兵)来标记列表的结束。当然,你应该选择一个不会与列表成员混淆的哨兵值。例如,如果你预计输入的是一组名字或正数,那么 –1 是一个不错的选择。另一方面,如果用户会输入负值,那么 –1 就不适合作为哨兵值。使用 –1 作为我们的scoreList的哨兵是可行的,示例脚本在图 9-11 中使用这个哨兵值来判断用户何时完成输入值。
图 9-11. 使用哨兵控制列表增长
在每次循环迭代中,脚本会提示用户输入一个数字,并将该值与哨兵进行比较。请注意,脚本在提示用户时会指定哨兵值(在此示例中为 –1)。如果用户输入 –1,脚本会停止,因为它知道用户已经完成了分数的输入。否则,输入的值会被添加到列表中,用户将被提示输入另一个值。图 9-11 展示了当用户输入三个分数后再输入哨兵值时,scoreList 应该是什么样子。
创建柱状图
作为一个实际的例子,我们来写一个应用程序,从用户输入的数字中绘制柱状图(也叫做直方图)。为了简化,我们只接受 1 到 40 之间的五个数字。当程序收集到所有五个数字后,它将绘制五个柱状条,高度与输入值成比例。我们柱状图制作器的用户界面如图 9-12 所示。
BarChart.sb2
图 9-12. 柱状图应用程序
这个应用程序包含三个精灵。Driver精灵控制应用程序的流程;它包含接受用户输入、填充列表并指示Painter精灵开始绘图的脚本。Painter精灵是一个不可见的精灵,负责绘制条形图。Frame精灵纯粹是装饰性的;它隐藏每个条形的底部,使其看起来平坦;如果没有它,垂直条形的底部会有圆形的尖端。五个条形的数值通过五个变量显示,命名为n1到n5,它们的显示器位于舞台的右侧。当你点击绿色旗帜图标开始应用程序时,Driver精灵运行图 9-13 中显示的脚本。
图 9-13. Driver精灵的主脚本
首先,Driver精灵出现在舞台上并清除任何先前的笔迹①。这样,如果已经有条形图,它会在绘制新图之前被清除。脚本随后清空numList,以便我们可以用它来收集用户的新输入,并调用ShowValues ②来设置n1到n5,以便它们的显示器是空白的。
当舞台准备好后,脚本进入repeat循环③,循环执行五次。在循环内部,Driver提示用户输入一个数字,并将该数字附加到numList中。在收集到用户的五个数字并将它们保存在numList中后,Driver精灵隐藏自己④,为条形图腾出空间。然后它再次调用ShowValues,用用户的新值更新n1到n5,并广播Draw消息,这样Painter精灵就会绘制五个条形。
在查看Painter如何绘制条形图之前,先来看一下ShowValues过程,如图 9-14 所示。
图 9-14. ShowValues过程
ShowValues只是将变量n1到n5设置为它们在numList中的相应值。由于第一次调用ShowValues是在清空numList之后立即进行的,因此在这个调用后,所有五个变量将包含空字符串。这会导致清除舞台上的五个显示器,这正是我们想要的。当numList包含来自用户的数据时,调用ShowValues会将这些数据展示在相应的显示器上。
现在让我们来看看Draw过程,它在Painter精灵收到Draw消息时执行。你可以在图 9-15 中看到这个脚本。
图 9-15. Painter精灵的 Draw 脚本
精灵首先设置画笔颜色。然后,它将画笔的大小设置为较大的值,以绘制粗条形图。为准备绘制五个垂直条形图,精灵将方向指向上方①。
脚本启动一个重复循环来绘制五根条形图②。我们事先知道每根条形图的* x 位置,因此创建了一个名为xPos的列表来存储这些值(如图所示)。在每次循环迭代中,Painter精灵移动到当前条形图的 x *位置,放下画笔,然后向上移动以绘制垂直线。
每条线的高度与numList中对应的值成正比。我们舞台上的图表区域高 224 像素,由于 40 是最高值,输入 40 应当使条形图和图表一样高。要找出numList中任意数字的高度(以像素为单位),我们需要将该数字乘以 5.6(即 224/40)。图 9-16 显示了在从用户那里获取一些数据后应用程序的输出。注意,Frame精灵覆盖了宽画笔的圆顶,使得条形图在底部看起来是平的。
图 9-16. 柱状图应用程序的输出示例试试看 9-3
多次运行此应用程序以理解其工作原理。修改脚本,使得每个条形图都用不同的颜色绘制。提示:为Painter精灵创建一个新的列表,名为color,存储五个条形图的颜色编号,并在绘制每个条形图之前使用以下命令:

数字列表
数字列表在许多实际应用中都会出现。我们可以有考试成绩、温度测量、产品价格等的列表。在本节中,我们将探讨一些你可能想对数字列表执行的常见操作。特别是,我们将编写程序来查找最大值或最小值,并计算存储在列表中的数字的平均值。
查找最小值和最大值
假设你是一名教师,需要知道你班级上次考试的最高分。你可以编写程序来比较所有这些考试成绩并找到最大值。我们的第一个例子,见图 9-17,找到一个名为score的列表中的最高分。
FindMax.sb2
FindMax过程首先将maxScore变量的值设置为列表中的第一个数字。然后,它启动一个循环,比较列表中其余数字与当前maxScore的值。每次找到比maxScore更大的值时,它就将maxScore设置为那个值。当循环结束时,maxScore中存储的值将是列表中包含的最大值。
图 9-17. 查找列表中的最大数字
查找列表中的最小值遵循类似的算法。我们首先假设列表中的第一个元素是最小的元素,然后使用循环检查其余的元素。每当我们找到一个更小的值时,我们就更新保存最小值的变量。
试试看 9-4
使用本节中学到的内容创建一个名为FindMin的过程,找到score列表中的最小值。
计算平均值
在下一个示例中,我们将编写一个过程,计算存储在score列表中的分数的平均值。你可以通过首先计算这些数字的和,再将总和除以N,来找到一系列N个数字的平均值。图 9-18 中展示的过程正是如此。
FindAverage.sb2
图 9-18. 查找一组数字的平均值
FindAverage过程使用循环逐一处理列表中存储的分数,将它们加在一起,并将结果存储在名为sum的变量中。(在循环开始之前,这个变量初始化为 0。)当循环终止时,脚本通过将sum除以分数的数量来计算平均值,并将结果保存在名为average的变量中。
注意
特别注意我们在循环中如何累积 sum 变量。这个模式,称为累加器模式,在编程中非常常见。
在下一节中,我们将探讨如何搜索和排序列表,这两个问题在编程中非常常见。我还将带你走过执行每个操作的一些简单算法。
试试看 9-5
将FindAverage、FindMax和FindMin合并为一个过程(称为ProcessList),该过程将同时显示score列表的平均值、最大值和最小值。
搜索和排序列表
假设你有一个没有特定顺序的联系人列表。如果你想组织这些联系人,你可能会根据他们的名字将其排序为字母顺序。如果你需要知道某人的电话号码,而你有他们的姓氏,你就需要搜索列表,查看其中是否包含该人的联系信息。本节的目标是介绍搜索和排序列表的基本编程技巧。
线性搜索
Scratch 的contains块提供了一种简单的方法来检查列表中是否包含特定项目。如果我们还想知道所搜索项目在列表中的位置,那么我们必须自己进行搜索。
SearchList.sb2
本节将解释一种查找列表项的方法,称为线性搜索(或顺序搜索)。这种方法易于理解和实现,适用于任何列表,无论列表是否排序。然而,由于线性搜索会将目标值与列表中的每个元素进行比较,如果列表很大,这种方法可能会花费很长时间。
举例来说,假设你正在寻找名为fruit的列表中的特定项。如果列表中包含你要找的项,你还需要知道该项的确切位置。图 9-19 中的SearchList过程对fruit列表进行了线性搜索,以帮助我们找到所需的答案。
图 9-19. SearchList 过程
从第一个元素开始,SearchList会将列表中的每个水果与我们要找的项进行逐一比较,该项由target参数表示。如果该过程找到值或者到达列表末尾,就会停止。如果脚本找到了我们想要的值,pos变量将包含该项找到的位置。否则,过程会将pos设置为一个无效值(在本例中为-1),以指示目标项不在列表中。图 9-20 展示了调用此过程的一个例子及其相应的输出。
图 9-20. 使用 SearchList 过程
检查pos的值可以告诉调用者两件事:(a)我们要找的项是否在列表中;(b)如果该项存在,它的确切位置。运行此脚本将pos设置为 4,表示“桃子”在fruit列表的第四个位置找到了。
出现频率
假设你的学校对其食堂的食品质量进行了调查。学生们根据 1 到 5 的评分标准对口味进行打分(1 = 差,5 = 优秀)。所有投票已被输入到一个列表中,你被要求编写一个程序来处理这些数据。目前,学校只想知道有多少学生完全不喜欢食物(即有多少人打了 1 分)。你会如何编写这样的程序?
ItemCount.sb2
显然,你的程序需要一个过程来统计数字 1 在列表中出现的次数。为了模拟学生的投票,我们使用一个包含 100 个随机投票的列表。填充该列表的过程如图 9-21 所示。这个过程将 100 个介于 1 到 5 之间的随机数字添加到一个名为survey的列表中。
图 9-21. FillList 过程
现在我们已经有了一个投票列表,我们可以统计某个评分在该列表中出现的次数。我们将使用GetItemCount过程,如图 9-22 所示。
图 9-22. 统计列表中某个项目出现的次数
target参数表示要查找的项目,而itemCount变量则跟踪目标项目出现的次数。该过程首先将itemCount设置为 0,然后开始一个repeat循环,在列表中查找target中指定的值。在每次循环迭代中,程序检查由循环计数器n索引的列表项。如果该项等于目标,脚本会将itemCount增加 1。
要提供关于对餐厅食物感到厌恶的主要信息,我们只需要使用参数为 1 调用GetItemCount,如图 9-23 所示。
图 9-23. 使用 GetItemCount 过程 尝试 9-6
当你回答了这个问题后,校长突然好奇有多少学生给餐厅打了优异的评分。校长还想知道有多少学生参加了调查。修改程序并再次运行,以提供这些额外的信息。
冒泡排序
如果你有一组名称、游戏成绩或其他任何你想以特定顺序显示的东西——按字母顺序、从大到小等等——你就必须对列表进行排序。有许多方法可以排序列表,而冒泡排序是最简单的算法之一。(这个名字指的是值是如何“冒泡”到它们正确的位置的。)在本节中,我们将学习冒泡排序,并编写一个 Scratch 程序来执行这种排序。
BubbleSort.sb2
假设我们需要按降序排列数字列表[6 9 5 7 4 8]。以下步骤说明了冒泡排序算法是如何工作的。
-
我们将首先比较列表中的前两个元素。由于 9 大于 6,我们可以交换它们的位置,如下所示。
![没有标题的图片]()
-
现在我们可以比较第二个和第三个元素,它们是 6 和 5。由于 6 大于 5,这两个数字已经按顺序排列,我们可以继续比较下一个元素。
-
我们将重复此过程,比较第三个和第四个、第四个和第五个,最后比较第五个和第六个元素。看一下这三个比较后的列表,如下所示。
![没有标题的图片]()
-
这次冒泡排序已经结束,但我们的列表仍然没有正确排序。我们需要进行第二次遍历,从第一步开始。再一次,我们将比较每一对元素,如果需要,就交换它们。第二次遍历后的列表如下:
![没有标题的图片]()
-
我们将重复冒泡排序过程,直到在一次遍历中没有交换任何数字,意味着我们的列表已经排序。算法的最后三次遍历如下所示:
![没有标题的图片]()
现在你已经了解了冒泡排序是如何工作的,让我们在 Scratch 中实现它。脚本,如图 9-24 所示,有两个循环。内层循环遍历列表,根据需要进行比较和交换,并在需要再次遍历时将标志(名为done)设置为 0。外层循环会重复,直到done标志为 0,因为该值表示我们尚未完成排序。如果内层循环在没有交换元素的情况下完成一遍,外层循环将退出,结束过程。
让我们更详细地探讨这个过程。由于我们还没有进行任何排序,它将done设置为 0 ①。外层循环使用repeat until块遍历列表,直到它被排序(也就是说,直到done变为 1)②。在每次遍历开始时,这个循环将done设置为 1 ③(即假设我们不会进行任何交换)。它还将pos设置为 1,从第一个数字开始排序。
内层循环然后比较列表中每对元素。循环需要执行N - 1 次比较④,其中N是列表中项的数量。
如果pos+1索引处的项大于pos处的项⑤,则需要交换这两个项。否则,过程会将pos加 1,以便比较下一对项。如果我们确实需要交换,过程将借助名为temp的临时变量进行交换⑥。
当前遍历结束后,如果内层循环交换了数字,它会将done重新设置为 0;如果没有做出任何更改,则done保持为 1 ⑦。外层循环将继续,直到列表被排序。
图 9-24. 冒泡排序过程 尝试 9-7
制作一个包含名字的列表,而不是数字,并使用冒泡排序脚本将列表按顺序排列。排序是否仍然能按预期工作?另外,为了使它按升序排序,你需要对过程做出什么更改?
查找中位数
Median.sb2
现在我们知道如何对列表进行排序,我们可以轻松找到任何数字序列的中位数值。回忆一下,中位数是排序数字集合中的中间值。如果我们有奇数个项,我们可以直接取中间的数字。如果有偶数个项,则中位数是两个中间数字的平均值。我们可以描述一个排序列表中N项的中位数如下:

执行此计算的过程如图 9-25 所示。它假设列表已按顺序排列。
图 9-25:寻找排序数字列表的中位数
该过程使用 if/else 块来处理偶数和奇数列表的两种情况。如果列表中的项数能够被 2 整除且没有余数(即列表包含偶数个项)①,则 median 变量计算为中间两个数字的平均值 ②。否则,列表包含奇数个项 ③,median 变量被设置为列表中间的数字 ④。
到目前为止,我们已经涵盖了很多内容,是时候将我们新学到的知识应用到更具挑战性的任务中。本章的其余部分将通过几个示例,展示如何在更复杂的应用中使用列表。
Scratch 项目
在本节中,您将探索一些实用的 Scratch 项目,突出展示列表的不同方面。我还将介绍一些新的创意和技术,您可以在自己的创作中使用它们。
诗人
让我们从本章的项目开始,首先是一个诗歌生成器。我们的人工诗人从五个列表(article、adjective、noun、verb 和 preposition)中随机选择单词,并根据固定模式将它们组合起来。为了给我们的诗歌一个中心主题,所有列表中的单词都与爱情和自然有某种关联。(当然,我们可能仍然会写出一些傻乎乎的诗歌,但那也很有趣!)
Poet.sb2
注意
该程序的思想改编自 Daniel Watt 的 《Logo 学习》 (McGraw-Hill,1983 年)。您可以在该项目的 Scratch 文件中找到我们使用的完整单词列表,Poet.sb2。
每首诗由三行组成,遵循以下模式:
-
第 1 行:冠词,形容词,名词
-
第 2 行:冠词,名词,动词,介词,冠词,形容词,名词
-
第 3 行:形容词,形容词,名词
记住这些构造后,我们来看看构建诗歌第一行的过程,如图 9-26 所示。
图 9-26:编写诗歌的第一行
这个脚本从 article 列表中选择一个随机单词并存储在 line1 中。然后,脚本添加一个空格,一个随机的 adjective 列表中的单词,另一个空格,以及一个随机的 noun 列表中的单词。最后,诗人精灵说出完整的一行。我没有展示诗歌的其他两行的过程,因为它们非常相似,但你可以打开 Poet.sb2 来查看它们。
这里是我们的机器诗人创作的两首诗:
-
每条迷人的道路
-
每条白色房屋后面都有一条鱼在游动
-
宁静的蓝色池塘
-
每一滴冰冷的水珠
-
每扇可怕的门下都有一颗心在注视
-
害羞安静的女王
试试看 9-8
打开 Poet.sb2 并运行几次,看看这个机器诗人能创作出什么。然后修改程序,使其使用三个精灵,每个精灵负责诗歌的一行,允许你一次性在舞台上阅读整首诗。
Poet.sb2
四边形分类游戏
我们的下一个项目是一个简单的游戏,帮助你探索不同种类的四边形。游戏会在舞台上显示六种形状之一(平行四边形、菱形、矩形、正方形、梯形或风筝形),并要求玩家通过点击正确的按钮来分类该形状,如图 9-27 所示。
QuadClassify.sb2
图 9-27. 四边形分类游戏的用户界面
游戏包含七个精灵:六个用于答案按钮,另一个(名为 Driver)包含主脚本。如图 9-27 所示,Driver 精灵有六种服装,分别对应游戏中的六种四边形。当点击绿色旗帜图标时,Driver 精灵执行图 9-28 所示的脚本以开始游戏。
首先,Driver 精灵移动到顶部绘图层 ①,确保没有按钮遮挡它。在游戏的主循环中 ②,脚本每次循环时使用 ShowShape 显示一个随机的四边形 ③。显示四边形后,脚本将全局变量 choice 设置为 0,表示用户尚未作答 ④。
图 9-28. Driver 精灵的主脚本
然后,脚本会等待 ⑤,直到 choice 变为非零数值,这将在玩家点击六个答案按钮中的一个时发生。当玩家猜测出一个形状时,脚本调用 CheckAnswer ⑥ 来告诉玩家该答案是否正确。
现在你知道了主脚本是如何工作的,接下来我们来看看 ShowShape 过程,如图 9-29 所示。
首先,ShowShape 将 Driver 精灵移动到舞台中心并随机指向一个方向 ①。它将 shape 变量赋值为 1 到 6 之间的随机值,并切换精灵的服装 ②,展示一个四边形供玩家识别。
图 9-29. Driver精灵的 ShowShape 过程
为了保持背景网格的可见性,ShowShape将透明度③设置为 25 到 50 之间的随机值。为了给人一种每回合都会出现新形状的错觉,该过程还将颜色效果设置为随机值,以改变服装④的颜色,并将精灵的大小调整为原始大小的 80%、90%......或 150%⑤。
接下来,我们简要查看六个按钮精灵的脚本,如图 9-30 所示。它们除了赋给 choice 变量的值不同外,其它完全相同。
图 9-30. 按钮精灵的脚本
这些单行脚本会根据玩家按下的按钮,将choice的值设置为不同的数字。一旦choice包含了玩家的答案,CheckAnswer过程就可以将其与shape的值进行比较,shape指定了所绘制的四边形的类型。
如果choice和shape相等,那么玩家的答案是正确的。否则,答案是错误的,精灵会说出正确的形状。CheckAnswer使用shape变量作为索引,查找一个名为quadName的列表(也见于图 9-31),以获取显示形状的正确名称。
尝试 9-9
打开 QuadClassify.sb2 并运行几次,了解它的工作原理。按原样,这个游戏会一直运行下去。修改程序,添加游戏结束的标准。同时,跟踪玩家的正确和错误回答数量。
QuadClassify.sb2
图 9-31. CheckAnswer 过程
数学小 wizard
这个应用展示了两种使列表更有用的方法。我们将探讨如何使用列表来存储非统一的记录(即记录大小不同的情况),以及如何使用一个列表作为另一个列表的索引。记录就是关于一个人、地点或事物的相关数据集合。在我们的例子中,每个记录包含一个谜题的答案和该谜题的说明。虽然每个谜题只有一个答案,但每个谜题的说明数量不相同。
MathWizard.sb2
我们的数学魔法师要求用户想一个“秘密”数字,并对其执行一系列数学操作(例如,数字翻倍、减去 2、将结果除以 10,依此类推)。最后,在玩家完成所有这些计算后,魔法师凭借魔法力量告诉用户他得到了什么数字,即使魔法师并不知道用户最初的数字。表 9-2 说明了游戏的工作原理。
表 9-2. 数学魔法师的工作原理
| 魔法师的指令 | 你的数字 |
|---|---|
| 想一个数字。 | 2 |
| 加 5。 | 7 |
| 乘以 3。 | 21 |
| 减去 3。 | 18 |
| 除以 3。 | 6 |
| 减去你最初的数字。 | 4 |
在最后一条指令后,魔法师会告诉你,按照这些指令你得到了数字 4,尽管游戏并不知道你最初是从 2 开始的。试着用不同的数字来做这个谜题,弄清楚魔法师的诡计!
应用程序的界面如 图 9-32 所示。
图 9-32. 数学魔法师应用程序的用户界面
该应用程序包含三个精灵:Wizard 精灵,它给玩家提供指令;OK 和 New 精灵,分别对应“确定”和“新游戏”按钮。它还使用了 图 9-33 中说明的两个列表。
图 9-33. Wizard 精灵使用的两个列表
instr 列表(右侧)包含 11 条谜题记录。每条记录包括:(a)谜题的答案,(b)指令,(c)一个空元素,用于标记该记录的结束。左侧的列表项(名为 index)标识 instr 列表中每个谜题的起始索引。例如,index 列表中的第二个元素是 9,这意味着第二个谜题的记录从 instr 列表中的第九个位置开始,如 图 9-33 中所示。我们来概述一个开发该游戏的策略:
-
当用户开始新游戏时,选择一个 1 到 11 之间的随机数字(因为我们的游戏当前包含 11 个谜题)。
-
查阅
index列表,找到所选谜题记录的起始位置。例如,如果选择第二个谜题,index列表会告诉我们该谜题的记录从instr列表中的第 9 个索引位置开始。 -
访问步骤 2 中找到的索引位置上的
instr列表。该索引位置的第一个元素被解释为谜题的答案。接下来的元素代表巫师将要说的指令。 -
让巫师逐一说出谜题指令,直到遇到空元素,空元素标志着最后一条指令。在说出新指令之前,巫师应该等待用户按下
OK按钮。 -
揭示谜题的答案。
现在我们已经了解了游戏的高级工作方式,让我们来看一下图 9-34 中显示的两个按钮的脚本。
图 9-34. 两个按钮角色的脚本
New Game按钮在点击时广播NewGame消息。当用户点击OK按钮以响应某个指令时,角色将clicked设置为 1,通知Wizard角色玩家已经完成了她被要求执行的指令。当Wizard角色收到NewGame消息时,它会执行图 9-35 中显示的脚本。
图 9-35. NewGame脚本的Wizard角色
NewGame通过清除前一个谜题的对话框(如果有的话)并将clicked变量初始化为 0①开始。然后,它将随机选择的谜题的编号保存在名为puzzleNum的变量中②。之后,它从index列表中读取所选谜题的起始位置,并将其保存在pos变量中③。脚本随后使用pos读取该谜题的答案并将其保存在puzzleAnswer中④。接下来,脚本将 1 加到pos,使其指向第一个谜题指令,并开始一个repeat until循环,按顺序说出谜题的指令⑤。每说完一个指令,脚本会等待clicked变量被设置为 1,然后再移动到下一个指令⑥。当循环发现空元素时,它会退出,并且脚本会说出谜题的答案⑦。
试试看 9-10
如果你删除了其中一个谜题或更改了某些谜题的指令数量,那么你需要重新构建index列表,以与instr列表匹配。编写一个过程,根据instr列表的当前内容自动填充index列表。关键是要在instr列表中搜索空字符串,因为这些空字符串表示一条记录的结束和下一条记录的开始。
花卉解剖学测验
在本节中,我将通过一个关于花朵各部分的测验,演示如何在 Scratch 中构建简单的测验。图 9-36 展示了我们示例应用程序的界面,包括测验开始时和程序检查用户答案后的界面。任何参加测验的人都会输入字母来匹配花朵各部分的标签,然后点击Check按钮来检查答案。程序将用户的答案与正确答案进行比较,并通过绿色的勾号和红色的X图标为每个答案提供反馈。
FlowerAnatomy.sb2
图 9-36. 花朵测验的用户界面
这个测验使用了三个列表。第一个列表(命名为correctAns)包含了对应于测验九个部分正确答案的字母。第二个列表(命名为ans)包含用户的输入,第三个列表(命名为cellYCenter)包含了Letter和YesNo精灵使用的 11 个垂直位置(以便它们知道应该在哪里盖上它们的服装)。当用户在任何答题框上点击鼠标时,舞台精灵会检测到鼠标点击并请求一个答案。舞台精灵会更新ans列表中对应的元素,以匹配用户输入的内容,并将该字母印在答题框上。打开FlowerAnatomy.sb2以查看读取和显示用户答案的脚本。
当用户点击Check按钮时,YesNo精灵(它包含勾号和X图像的服装)执行图 9-37 中显示的脚本。
图 9-37. YesNo精灵的检查过程
脚本逐一比较correctAns和ans列表中的元素。如果两个值相等,它会打上勾,表示用户回答正确。否则,它会在用户答错的地方打上红色的X。无论哪种情况,Check都会查阅cellYCenter列表,获取打上图像的正确位置。请参阅对面页面的尝试一下 9-11。
其他应用
你从本书网站下载的额外资源(*nostarch.com/learnscratch/)包含了三个应用程序,你可以自行探索,每个应用都有完整的解释。第一个应用是一个关于排序分数和小数的两人游戏。每位玩家从 31 张卡牌中随机抽取 5 张。然后每位玩家从剩下的卡组中抽取一张卡片。你可以选择丢弃这张新卡,或者将它拖到你当前的五张卡片之一,替换掉旧的那张。第一个将五张卡片按升序排列的人赢得游戏。
SayThat Number.sb2
尝试一下 9-11
打开这个应用程序并测试它。然后,考虑你可以创建并实现的其他不同学科领域的测验。以下是一个示例,展示在文件USMapQuiz.sb2中。打开这个文件并完成缺失的部分,使这个测验正常工作。
USMapQuiz.sb2

第二个应用程序是一个拼写整数的程序。它提示用户输入一个数字,然后用单词拼出这个数字。例如,如果用户输入3526,程序将说“three thousand five hundred twenty six”。其思路是将数字从右到左分解为三位一组,每组按需附加倍数词(千、百万等)。
SortEmOut.sb2
第三个程序展示了厄拉多斯筛法,这是一种查找小于 100 的所有质数的算法。
Sieve.sb2
总结
列表在编程中非常有用,它们提供了一种方便的方式来存储多个元素。在这一章中,我们探索了在 Scratch 中创建列表,学习了可以用来处理它们的命令,并通过用户输入的数据动态地填充列表。
我们还研究了数值列表,并演示了如何找到它们元素的最小值、最大值和平均值。之后,我们学习了简单的算法来搜索和排序列表。我们通过几个程序结束了这一章,展示了列表在实际应用中的使用。
问题
| Q: | 1. 创建一个包含前 10 个质数的列表。编写一个脚本,使用say块显示这些数字。 |
|---|---|
| Q: | 2. 创建三个列表来存储个人记录。第一个列表存储姓名,第二个列表存储出生日期,第三个列表存储电话号码。编写一个程序,询问用户需要某个人的联系信息。如果此人的姓名存在于第一个列表中,程序将显示该人的出生日期和电话号码。 |
| Q: | 3. 创建两个列表,用于存储在杂货店出售的商品及其对应的价格。编写一个程序,提示用户输入商品名称,然后显示该商品的价格,如果它在列表中找到的话。 |
| Q: | 4. 执行下一页显示的脚本后,numList中存储了什么?重新创建该过程并运行,以检查你的答案。![]() |
| Q: | 5. 编写一个程序,将存储在数值列表中的每个元素翻倍。 |
| Q: | 6. 编写一个程序,提示用户输入学生的姓名和成绩,并将这些输入存储在两个列表中。当用户输入 -1 作为学生的姓名时,停止收集数据。 |
| Q: | 7. 编写一个程序,提示用户输入一年的 12 个月的最高和最低温度。将输入的值存储在两个列表中。 |
| Q: | 8. 编写一个程序,提示用户输入 10 个整数。只有当输入的数字不是之前已输入的重复数字时,才将其存入列表中。 |
| Q: | 9. 编写一个程序,处理包含 100 项的测试的 20 个分数,并找出得分在 85 到 90 之间的学生人数。 |
附录 A. 分享与协作
Scratch 让你能够轻松地与全球的人们协作并分享你的作品,本附录重点介绍了 Scratch 2 中促进与他人连接的功能。具体来说,你将学习如何创建账户,如何使用背包与他人创建的角色和脚本合作,如何混合他人的项目,以及如何发布你的作品并与 Scratch 社区分享。
创建一个 Scratch 账户
尽管你不需要账户就可以使用 Scratch,但拥有账户会带来一些好处。它让你能够在 Scratch 网站上保存你的作品,与其他用户交流,并在线分享你的作品。按照以下步骤创建一个 Scratch 账户:
-
访问
scratch.mit.edu/,然后点击屏幕右上角的 加入 Scratch 链接。在弹出的对话框中(参见 图 A-1),输入用户名和密码,然后点击 下一步。
图 A-1. 账户创建过程中的第一个对话框 -
在第二个对话框中(参见 图 A-2),输入你的出生日期、性别、国家和电子邮件地址。然后点击 下一步。
图 A-2. 账户创建过程中的第二个对话框 -
你将看到一个欢迎你加入 Scratch 用户社区的对话框(参见 图 A-3)。点击 确定,开始吧!,你将登录到你的新账户。
图 A-3. 账户创建过程中的最后一个对话框
屏幕顶部的导航栏将显示你的用户名,如 图 A-4 所示。使用导航栏中的四个链接(创建、探索、讨论和帮助)来启动 Scratch 的项目编辑器,探索可用项目,与其他 Scratch 用户协作,并查找有用的指南和额外的 Scratch 资源。
图 A-4. 登录用户的导航栏
以下部分将讨论一些在你登录 Scratch 账户后可以使用的功能。
使用背包
背包(仅限登录用户使用)允许你从任何项目中复制精灵、脚本、服装、背景和音效,并在自己的项目中使用。点击探索链接,如图 A-4 所示,进入项目探索页面,如图 A-5 所示。在这里,你可以试用其他人创建的 Scratch 项目。
图 A-5. 项目探索页面
你可以按类别查看项目,搜索包含特定标签的项目,并根据不同的标准进行排序(分享、最受喜爱、最多浏览或最多混合)。当你找到想要探索的项目时,双击其缩略图进入该项目的页面,如图 A-6 所示。
图 A-6. 一个示例项目页面
点击右上角的查看内部按钮,查看此项目的内容,如图 A-7 所示。
图 A-7. 查看另一个 Scratcher 项目的内容
如果你想在自己的应用程序中使用此项目的部分内容(如精灵、脚本、服装、背景或音效文件),只需将这些部分拖到你的背包中。要删除背包中的项目,右键点击并从弹出菜单中选择“删除”。
你的背包内容保存在 Scratch 服务器上,因此你登出时不会丢失它们。要使用背包中的项目,只需将它从背包拖到你的项目中。
创建你自己的项目
有许多方法可以开始在 Scratch 中编程。你可以创建一个干净的项目, remix 一个已经在 Scratch 网站上分享的项目,或者打开一个旧项目并进行修改。我们将逐一看看这些选项。
开始一个新项目
要开始一个全新的项目,点击导航栏中的创建链接,打开 Scratch 的项目编辑器,如图 A-8 所示。
图 A-8. 登录用户的 Scratch 项目编辑器
这个界面与你未登录时看到的非常相似,但也有一些重要的区别:
-
背包面板可见。
-
右上角会出现两个新按钮(分享和查看项目页面)。
-
手提箱图标和用户名出现在工具栏的右侧。
-
文件菜单中有了新的选项。
工具栏及其新选项的详细信息显示在图 A-9 中。
图 A-9. 已登录用户的工具栏
当你登录时,Scratch 会自动将你的作品保存到云端(即 Scratch 服务器),但在退出 Scratch 前,仍然建议你点击保存。另存为副本选项可以将当前项目保存为不同的名称。例如,如果当前项目名为Test,新项目将被命名为Test copy。(你可以通过在项目名称编辑框中输入新名称来更改该名称。)恢复选项会丢弃自打开当前项目以来所做的所有更改。
如果你希望将项目保存在电脑上而非云端,可以使用下载到电脑选项。相反,从电脑上传选项允许你从电脑加载一个 Scratch 项目到项目编辑器中。你可以使用此选项上传使用 Scratch 1.4 创建的项目并将其转换为 Scratch 2 格式。
重混一个项目
当你有新的想法想要添加到其他 Scratch 用户的项目中时,点击重混按钮。这将把所选项目复制到你的账户,并为你的工作提供一个起点。
你还可以点击项目页面上的查看重混树(见图 A-6)查看项目如何随着时间发展,并选择你想要复制的分支。
如果你分享了你的重混项目,项目页面将列出原始创作者并提供链接到他们的项目。
项目页面
点击右上角的查看项目页面按钮,进入图 A-8 以编辑你的项目页面,该页面如图 A-10 所示。你可以为使用你应用程序的人输入说明,给使用过你想法或作品的人致谢,并指定一些标签,帮助其他人找到你的应用。
图 A-10. 项目页面
分享你的项目
完成应用程序后,你可以通过点击分享按钮将其分享给 Scratch 社区。当你分享一个项目时,任何人都可以在网上找到它并查看其中的内容。
要查看所有项目的列表,点击工具栏中用户名下方的下拉箭头,并从下拉菜单中选择我的内容。 (你也可以点击行李箱图标。)这将带你到“我的内容”页面,如图 A-11 所示。
图 A-11. 我的内容页面
“我的内容”页面让你可以控制和查看项目和工作室的各个方面。你可以在此页面创建、分享、编辑、取消分享和删除项目。你还可以创建工作室——项目的集合——并将项目添加到其中。工作室使得将相关项目分组变得更加方便。
注意
如果你删除了未共享的项目,该项目将被移动到回收站文件夹,回收站文件夹充当项目的回收站。回收站文件夹的界面允许你将已删除的项目恢复到“我的内容”页面。
第十章:关于在线资源
在本书附带的在线资源中,你将找到许多有用的材料。只需访问* nostarch.com/learnscratch/* 即可开始!
在阅读过程中,打开每一章提到的 Scratch 脚本(.sb2 文件)以跟随示例。每当你解决一个练习问题或“尝试一下”练习时,可以通过Solutions文件夹中的文件检查你的答案。通过阅读Extra Resources文件夹中的信息,你还可以进一步了解 Scratch 的绘图编辑器、数学函数和几何图形绘制。如果你想尝试更多的引导示例,还可以在Bonus Applications文件夹中找到与几章内容相关的额外游戏和仿真程序。





















这个游戏包含两个精灵,分别名为







图 4-11. 创建 DrawFlower 自定义块后出现的新块对话框
图 4-12. 作为自定义块实现的DrawFlower过程
图 4-13. 从




,其中c是常数缩放因子(在我们的示例中设置为 5)。

































图 6-17. 猜我的坐标游戏的脚本

















图 A-1. 账户创建过程中的第一个对话框
图 A-2. 账户创建过程中的第二个对话框
图 A-3. 账户创建过程中的最后一个对话框
浙公网安备 33010602011771号