自制积木
自制积木就是自己造一块积木:给一段脚本起个名字、定几个参数,做好之后像内置积木 一样从积木栏里拖出来用。Gandi 3.0 的自制积木比原版多几样:积木有三种形状、 参数可以是下拉菜单、做好之后每一格都还能改;还能像扩展积木那样配颜色、 在积木栏里分类、加一句悬停说明。

打开方式#
积木栏最后一个分类「自制积木」→「制作新的积木」。
要改一块已经做好的:在工作区里的定义块(「定义 …」那块)或者任何一块调用 块上右键 →「编辑」。积木栏里那块调用块也能右键,菜单里同样有「编辑」。
怎么用#
表单最上面那块就是这个积木本身 —— 用积木编辑器画的真积木,不是示意图:形状、颜色、 C 口、字体,和它做好之后躺在工作区里的样子一模一样。改哪一处它当场就变成什么样。
字就在积木上改。 点积木上的哪一格,光标就落进那一格 —— 光标、选中、打出来的字都在 积木里面,那一格还是它平时的样子。同时下面展开这一格的设置。
第一格是积木开头那段字,可以清空:清空后积木直接从参数开始,比如「(数量) 个苹果」。
想在最前面再加字,就「添加」一段文本,把它拖到最前面。积木上一格都不剩时做不出来,
点「完成」会提示「给积木起个名字」。固定文字和参数名可以包含 %,例如 100% 或字面文字
%s;这些文字不会被误认为参数占位符,编辑后再次保存也不会重复转义。
「添加」一行往积木末尾接一格:
| 按钮 | 加进来的是 |
|---|---|
| 输入框 | 一个圆槽,能填数字或文字 |
| 布尔值 | 一个六边形槽,只收「成立 / 不成立」 |
| 文本 | 一段固定的字。它不是参数,只用来把积木的说法写通顺 |
| 积木堆 | 一个 C 口,调用的人把自己的积木放进来。见下面一节 |
| 积木堆参数 | 一颗椭圆,调用的人拖进 C 口里用。有积木堆之后才出现,见积木堆参数 |

加过东西之后多出一行「内容」,名字之后的每一格排成一颗小块,按它们在积木上的先后, 前面的小图标就是它是哪一种。拖着小块就能换顺序,积木跟着变。
选中一颗时再「添加」,新的一格插在它后面;没选中就接在末尾。
点一颗(或者直接在积木上点那一格),下面滑出它的设置 —— 字在积木上改,这里放积木上 表达不了的:
- 类型 —— 数字或文本 / 布尔值 / 下拉菜单。换成下拉菜单时再多一张选项表, 「+ 添加选项」加一项,每项右边的 × 删一项。这张选项表跟着这个自制积木走, 它的每一次调用读的都是同一份
- 删除 —— 把这一格从积木上摘掉
固定文字和积木堆没有类型可换。
三种形状#

| 形状 | 外观 | 什么时候用 |
|---|---|---|
| 普通积木 | 上下都能接别的积木 | 一段动作 |
| 返回值积木 | 圆角,能塞进别的积木的圆槽 | 算出一个数或一段文字 |
| 布尔积木 | 六边形,能塞进「如果」的条件位 | 回答成立还是不成立 |
只要这个角色有一块返回值或布尔积木,积木栏的「自制积木」里就会多出一块「返回 ( )」:把它放进定义里,执行到这里就带着这个值返回。
C 型积木:让别人把积木放进来#
点「添加积木堆」,积木当场裂开一个口,变成 C 形。调用它的人把自己的积木放进这个口里, 定义里用「执行 ( )」把那段积木跑一遍。

积木堆是第五种参数,不是第四种形状:口的数量就是积木堆参数的数量,口的位置就是这个 参数在句子里的位置。加了积木堆之后,「返回值」和「布尔」两种形状会变灰,因为这两种形状画不出 C 口; 反过来,选了这两种形状时「积木堆」也是灰的。
定义里那块「执行 ( )」从定义帽子上拖出来,和参数椭圆一样:加了积木堆之后,定义
里的那块原型积木自己就是 C 形的,口里躺着一块「执行 身体」,拖出来是复制一块。

把「执行 ( )」放在定义里的不同位置,就得到不同种类的 C 型积木:
| 想要的积木 | 定义里怎么写 |
|---|---|
| 循环型(对每一项、重试到成功) | 把「执行 ( )」放进一个「重复执行」里 |
| 条件型(只在调试时才跑) | 把「执行 ( )」放进一个「如果⋯⋯那么」里 |
| 包裹型(计时、前后加料) | 前面写准备、「执行 ( )」、后面写收尾 |
| 多分支(试着做⋯⋯出错了就) | 加两个积木堆,在两个位置分别执行 |
积木堆参数:把每一轮的值交给放进来的积木#
「对 [文字] 里的每个 (项目) (序号)」这种积木,放进口里的积木要知道「这一轮是哪一项」。 积木堆参数就是干这个的:积木上的一颗椭圆,调用的人把它拖进口里用,读到的是这一轮的值。

做积木的人。 加了积木堆之后,「添加」那一行多出「积木堆参数」。点一下,积木上口的上面 那一行多一颗椭圆,默认叫「项目」,点它就能改名。它比输入框深一档、字是白的,和普通参数一眼 分得开。一颗椭圆属于它后面最近的那个口(后面没有口时属于前面最近的那个),所以多个积木堆 时,放在哪个口上面那一行,就是给哪个口的。
定义里的「执行 ( )」会自己带上这个口每个参数的一格:「执行 身体 项目 [ ] 序号 [ ]」。 往格子里放什么,这一次执行身体时那颗椭圆就是什么;格子空着就是空字符串。不用学新积木, 写循环时给「执行」填上值就行:

用积木的人。 调用块上的椭圆和扩展 C 型积木上的可拖参数一样:拖一下复制一颗,放进口里 任何地方(包括嵌在里面的积木里)。颜色跟着这块积木走。

上面这段跑出来是 1苹、2果、3派。
| 情形 | 读到的是 |
|---|---|
| 椭圆在口里 | 包着它的最近一层这块积木,这一次「执行」给的值 |
| 同一块积木套在自己的口里 | 最里面那一层的 |
| 这块积木自己的输入格里放了外层的椭圆 | 外层的 |
| 调用处自己有一个同名参数 | 两个互不相干:积木堆参数的椭圆深一档,按参数本身认,不按名字认 |
| 递归、多个脚本同时跑、克隆体 | 各读各的 |
| 拿到口外面 | 空字符串 |
改名、删参数都在「编辑」里做。改名之后调用块上的、拖进口里的椭圆,以及定义里「执行」上的 格子一起换名字;删掉一个参数,「执行」上那一格跟着没了,格子里放的积木留在工作区上,口里 拖进去的那颗椭圆还在,读到空字符串。删掉最后一个积木堆时,积木堆参数一起删掉。
编译模式下,口里的积木编成带参数的一段代码,椭圆就是读这段代码的参数:「对 1 到 10 万里的 每个 (i),把 i 加到变量上」约 0.8 毫秒,内置「重复执行」加变量约 0.6 毫秒。
身体按写它的地方算#
一段积木写在调用处,却在定义里被执行。所有规则都指向同一个答案:它按被写下的地方 解释,不按被执行的地方解释。
| 问题 | 结果 |
|---|---|
| 在哪个角色里跑 | 调用处那个角色,即使这块 C 型积木是模块里的全局积木 |
| 里面的参数椭圆 | 指调用处的参数,不是这块 C 型积木自己的 |
| 里面的「返回」「停止这个脚本」 | 从写它的那块自制积木返回,会穿过中间的 C 型积木 |
| 执行完之后 | 回到定义里「执行 ( )」的下一块 |
| 口是空的 | 「执行 ( )」立刻结束,不报错 |
| 同一个口执行多次 | 可以,每次都是一次全新的执行。循环型积木就靠它 |
| 运行时不刷新屏幕 | C 型积木开了这个开关,身体也在不刷新屏幕的状态里跑 |
| 递归、嵌套 | 都可以。每一句「执行 ( )」找的都是离它最近的那一次调用 |
编译模式下,身体会和调用处一起编译成一段代码直接调用,不经过任何兼容层:两万次身体执行 比内置的「重复执行」多花约 1 毫秒。
颜色、分类、说明#
表单中段是积木的外观,三行:

颜色。 一排色点:第一格是「默认」(跟着主题的自制积木粉),接着是 Scratch 九个 类别的颜色、七种类别里没有的颜色,最后一格彩色的打开拾色器,可以拖、可以输十六进制、 可以用吸管取色。点哪个,上面那块积木当场换色。颜色一直跟着这块积木:工作区里的定义块、 所有调用块、积木栏里那一块都是这个颜色。参数积木也是:定义帽子上那几颗、定义里用着的、 拖出来单独摆在一边的,换颜色时一起换(两块积木有同名参数、颜色又不同时,单独摆着的那颗 认不出是谁的,用默认粉)。「返回 ( )」跟着定义走,拖出定义就退回默认粉,放回去又变回来。 换主题不会把颜色冲掉。
分类。 填一个名字,这块积木在积木栏里就归到这个名字的小标题下面;留空就是「不分类」, 排在最上面。文本框下面列着作品里已经有的分类(含别的角色定义的全局积木的分类),每个带 一颗色点和块数,点一下就填进去。点一个已有的分类会顺带把那个分类的颜色带上,除非这次 已经亲手选过颜色。
分类里已经有别的颜色的积木时,多出一行「也把这个颜色用到「某分类」里的其他 N 块积木上」。 勾上,按「完成」时那几块一起换色(只算定义在这个角色里的);整件事算一步撤销。
说明。 一句话,鼠标停在调用块上时显示。可不填。
积木栏里的效果:

每个分类一条小标题,标题前是这一组积木的颜色(一组里颜色不一样时取最多的那种),右边 有折叠小三角,点标题整组收起或展开,记在本机。分类按名字排,组里的积木也按名字排。 同时有全局积木和本角色积木时,先分「全局 / 本角色」两档,分类是每一档里的小节,见 全局自制积木。
两个开关#
表单最后一行「选项」:
| 开关 | 意思 |
|---|---|
| 运行时不刷新屏幕 | 这段过程一次跑完,中途画面不逐帧更新,速度快很多 |
| 全局积木 | 所有角色都能调用它。只有模块里的积木能打开这个开关,别处是灰的 |
全局积木要做在模块里#
模块是「只放代码的角色」:在「角色」面板工具条最右边点「新建」→「新建模块」,填个名字就有了 (对话框里那句话是「模块是只放代码的角色,只有模块能定义全局积木」)。在模块里做 自制积木时,「全局积木」开关就能打开;打开之后「分类」自动填上模块名,这块积木在每个 角色的积木栏里都归到这个小标题下。

做好之后,别的角色的积木栏里就能直接看到它:

全局积木的颜色、分类和说明也跟着定义走:改一次,所有角色里的调用块和积木栏一起变。 全局积木另有一篇:改定义怎么同步到所有角色、积木栏里的「全局 / 本角色」两档、怎么跳到 定义、删除时会被拦下的情形,以及两处撤销无法恢复的操作,见全局自制积木。
改一块已经做好的积木#
定义块上右键,菜单里有「编辑」:

表单会带着现有的格子、颜色、分类和说明打开,标题变成「编辑积木」。加一格、删一格、 改名字、换类型、拖着换顺序、换颜色、换分类,都可以:

关键的一条:改完之后,工作区里已经摆好的调用块会跟着变形状,原来填在里面的值
不会丢。 一块填了 42 的调用块,在定义里再加一个布尔参数之后仍然是 42,只是
右边多出一个空的六边形槽。全局积木的改动范围是整个作品,
见全局自制积木。
删一块还有人在用的积木#
定义块上右键「删除」。这块积木还有调用块在用时,会问一句:
这块积木还有 3 处在用。要连同这些调用一起删掉吗?
答应了就把那些调用块一起删干净,再删定义;这一整下算一次撤销,Ctrl+Z 全回来。不答应 就什么也不动。
这一问只管这个角色里的调用。全局积木被别的角色用着时删不掉,那条路见 全局自制积木。
细节#
- 名字不能和已有的自制积木重复。重复时点「完成」不会关窗,下面会用红字提示 「已经有一个叫「…」的积木了」。
- 名称和文本里不能有
%;每个输入框都要有名字,而且名字之间不能重复; 下拉菜单至少要有一个选项。不满足时点「完成」同样不关窗,只在下面说明原因。 - 分类名最长 40 个字,说明最长 200 个字。
- 颜色、分类、说明存在积木的定义里,跟着作品文件走;老作品打开来和以前一模一样。
- 调用块上右键还有一条「跳到定义」,会滑到定义处、停在视野正中并闪一下;定义在别的 角色里时先切换过去。它来自内置插件,默认开启,见查找自制积木的定义。
- 按
Esc或点表单外面等于「取消」;拾色器开着时Esc先收拾色器。
和原版 Scratch 的差别#
| 原版 Scratch | Gandi 3.0 | |
|---|---|---|
| 形状 | 只有普通(指令)积木 | 普通 / 返回值 / 布尔 三种 |
| 参数类型 | 数字或文本、布尔值 | 多一种下拉菜单 |
| 调整已有的参数 | 不能换顺序,也不能换类型 | 每一格都能改名、换类型、拖着换顺序、删掉 |
| 颜色 | 一律粉色 | 每块可选颜色,定义里的参数积木跟着走 |
| 积木栏 | 平铺一列 | 按分类分组,每组可折叠 |
| 说明 | 无 | 可写一句悬停说明 |
| 调用范围 | 只有定义它的那个角色 | 模块里的积木可以设成全局,所有角色都能调 |
表单本身也不一样:这里是一张纯 React 的对话框,不是在对话框里再开一个 Blockly 工作区。