插件 | Plugins

Verge3D拼图编辑器提供 了加载您自己自定义拼图的能力, 从而使您可以用您一直想要的功能 来扩展编辑器的功能。

内容

安装插件

插件是包含一堆与插件相关的文件的目录。 插件应该放在Verge3D的 puzzles/plugins 文件夹中, 以便被拼图编辑器识别。一切就绪~ 重新加载编辑器页面后,所有已安装的插件就会出现 在编辑器工具箱的底部,紧随所有标准拼图类目之后。

插件类目
工具箱中的插件类目。

提供给插件开发者:
这里有一个实用的参考插件实例,包含了一些典型的 拼图模块:ExamplePlugin.zip。 您可以将其解压到 puzzles/plugins 文件夹中, 然后在工具箱中查看 "Example Plugin " 的拼图类目。

插件文件概述

一个典型的插件是一个包含init.plug文件的目录,其中包含了插件的 通用设置,还有一堆 *.block 文件,每个文件都定义一个拼图 模块。如果您使用的文本编辑器支持语法高亮, 那么请在开启这两种文件格式时使用HTML模式。

init.plug文件格式

init.plug是一个强制性的插件文件,它用于 指定常规的插件设置。在该文件中,您可以定义 工具箱的条目是什么样子的。您也可以在那里为 拼图添加一些初步的javascript代码。下面这个简单的 示例列出了您将在init.plug文件中看到的内容:

<category name="My Awesome Plugin" color="green"> <label text="My Awesome Plugin v1.0"></label> <block type="myPuzzle"></block> <block type="doSomethingCool"></block> </category> <script> function code() { return `console.log('Powered by My Awesome Plugin!');`; } </script>

Category(类目)

category部分是一个XML树,它定义了插件及其 拼图模块在拼图编辑器工具箱中的显示方式。虽然 它是可选的,但如果 init.plug 文件中没有 category , 那么此插件不会被加载到拼图编辑器中。

您可以通过category设置以下几个选项:

工具箱中的条目名称

通过name属性指定。 <category name="My Awesome Plugin"></category>

工具箱中的条目颜色

通过color属性指定。 <category name="My Awesome Plugin" color="green"></category> 可以用以下格式来定义颜色:

可用的拼图

要使一个拼图模块在插件的工具箱类目中可用, 应该通过 block 元素和其 type 属性来指定。 type属性可以参考以下拼图:

为查找出某个拼图模块的类型,您可以使用拼图上下文 菜单中的 "Print Puzzle XML Tree(打印拼图XML树)" 选项:

该菜单选项可将拼图的XML树打印到 浏览器控制台。您可以在其中找到拼图类型 以及整个XML结构,可用于在设置 默认的 输入和字段值 时参考。

"Print Puzzle XML Tree(打印拼图XML树)" 菜单选项 提供了一个简单的方法,可以一次性将一整组拼图模块添 加到您的插件中。这样便于您创建某种由相连的拼图模块 组成的 "代码片段(snippets)" 并将其放入插件中。这使 得它有点类似于 拼图库

下面的示例解释了如何执行这一操作:

拼图顺序

拼图模块按照您在init.plug文件中 定义的顺序在工具箱中排列。 <category name="My Awesome Plugin" color="green"> <block type="myPuzzle"></block> <block type="doSomethingCool"></block> <!-- <block type="testPuzzle"></block> --> <block type="anotherPuzzle"></block> </category>
注意,被注释掉的拼图模块 "testPuzzle" 并没有显示在 工具箱中。

文本标签

您可以通过label元素将文本标签添加到工具箱类目中:

<category name="My Awesome Plugin" color="green"> <label text="My Awesome Plugin v1.0"></label> <label text="Main Puzzles:"></label> <block type="myPuzzle"></block> <block type="doSomethingCool"></block> <label text="Other:"></label> <block type="anotherPuzzle"></block> </category>

label元素不适合显示多行文本, 即不会换行。

label元素也可以在一定程度上定义样式。 它们支持 web-class 属性,可用于为 label 元素 分配自定义的CSS类。该类的CSS规则可以 在 init.plugscript 部分定义。 下方示例描述了实现方式:

<category name="Example Plugin" color="#a52a2a"> <label text="Example Plugin v1.0 by Soft8Soft" web-class="example-plugin__label"></label> </category> <script> const styleElem = document.createElement('style'); styleElem.innerHTML = ` .example-plugin__label .blocklyFlyoutLabelText { fill: #a52a2a; font-style: italic; font-weight: bold; text-decoration: underline; } `; document.head.appendChild(styleElem); </script>

这是应用自定义CSS规则后的样子:

在为web-class属性规划CSS类名时, 建议考虑限定CSS范围。例如使用拼图插 件名作为专有前缀:在上面的例子中使用 了 "example-plugin__label" 类的 "example-plugin" 部 分作为前缀。这样,意外破坏页面中已经 使用的CSS类的可能性就很小了。

分隔符

分隔符可以用来改变拼图模块之间的距离。 您可以通过sep元素在工具箱类目中添加分隔符。 gap属性指定了间隙的宽度,单位为像素。 不使用sep时,模块之间的默认距离等于24像素。 <category name="My Awesome Plugin" color="green"> <block type="myPuzzle"></block> <sep gap="0"></sep> <block type="doSomethingCool"></block> <sep gap="80"></sep> <block type="anotherPuzzle"></block> </category>

在 "init" 初始化标签中可用的拼图模块

默认情况下,拼图模块 只在main和用户创建的选项卡中可用, 在init(初始化)选项卡中不可用。这是 因为init选项卡所产生的代码是在Verge3D应用 被加载与完全初始化之前执行的。这意味着那些 预期用于3D场景、3D对象、材质的拼图并不适合 在init中使用,如果使用会导致应用崩溃。 但是,对于不作用于3D场景的拼图应该不会有这样的问题。 例如,可以在init中使用预加载资源拼图 或设置用户界面的拼图。

如果需要让拼图模块出现在init标签的工具箱中, 您需要在拼图的block元素中设置allow-init属性 为true(这也适用于labelsep元素)。 <category name="My Awesome Plugin" color="green"> <label text="My Awesome Plugin v1.0" allow-init="true"></label> <label text="Main Puzzles:"></label> <block type="myPuzzle" allow-init="true"></block> <block type="doSomethingCool"></block> <label text="Other:"></label> <block type="anotherPuzzle"></block> </category>
注意,没有allow-initblocklabel元素是 不会显示在工具箱中的。

默认的输入和字段值

如果一个拼图模块有模块输入和/或字段输入, 那么您可以指定它们的占位模块和/或默认值。 模块输入即连接其他拼图模块的插槽,字段输入 即不以拼图模块为形态的UI元素,如选择器、复 选框、文本字段等.这个功能有两个目的:为用户 提供提示,告诉他们可以插入哪些类型的模块到 输入插槽中,同时也可快速调用拼图模块。

比方说,您的拼图模块有一个名为 "myNumber" 的 输入。这里您可以添加一个math_number类型的 占位符模块插入该槽。 <category name="My Awesome Plugin" color="green"> <block type="myPuzzle"> <value name="myNumber"> <block type="math_number"></block> </value> </block> </category> 其外观如下:


插入输入插槽的占位符模块也可以 是一个shadow(影子)模块。shadow模 块与普通模块基本相同,但它们可被您插入 到相应输入插槽中的模块自动替换,当您从 插槽中移除该模块时,它们又会自动出现。 这使得shadow模块比普通的占位符模块 更容易使用。

shadow模块的 定义方式与普通占位符 模块几乎相同,唯一 的区别是block元素 被类似的shadow元素 所取代。 <category name="My Awesome Plugin" color="green"> <block type="myPuzzle"> <value name="myNumber"> <shadow type="math_number"></shadow> </value> </block> </category> 它看起来会是这样的:


拼图模块可以有语句输入,这些输入通常 包裹着一系列的子拼图模块。比方说,您 的拼图模块有一个名为 "myStatement" 的 语句输入,那么您可以在这个输入中添加几个占位符模块,如下所示: <category name="My Awesome Plugin" color="green"> <block type="myPuzzle"> <statement name="myStatement"> <block type="addHTMLElement"> <next> <block type="setHTMLElemAttribute"></block> </next> </block> </statement> </block> </category> 这里使用的statement元素通过name属性 引用 "myStatement" 输入,并在输入中添加 了一些占位符模块。另外,这里还使用了next元素, 用于链接一系列的占位符模块。这个设置的结果如下所示:


如果您的拼图模块有一个名为 "myCheckbox" 的复选框字段, 那么您可以像这样定义它的默认状态(true - 启用, false - 禁用): <category name="My Awesome Plugin" color="green"> <block type="myPuzzle"> <field name="myCheckbox">true</field> </block> </category> 结果如下:


通过使用占位符模块和默认字段值,您可 以定义 复杂的复合拼图, 类似于可以 添加 到 拼图库中的内容:
上图中复杂拼图设置的init.plug的代码 示例如下: <category name="My Awesome Plugin" color="green"> <block type="myPuzzle"> <statement name="STATEMENT_0"> <block type="whenClicked"> <value name="VALUE"> <block type="objectList"> <field name="FIELDNAME">Cube</field> </block> </value> </block> </statement> <statement name="STATEMENT_1"> <block type="loadScene"> <value name="URL"> <block type="text"> <field name="TEXT">my_scene.gltf</field> </block> </value> </block> </statement> <statement name="STATEMENT_2"> <block type="show"> <value name="VALUE"> <block type="objectList"> <field name="FIELDNAME">something</field> </block> </value> </block> </statement> </block> </category>

查看 "Print Puzzle XML Tree(打印拼图XML树)" 上下文菜单选项。它有助于您找出感兴趣的 拼图模块的XML结构(输入和字段的配置)。

工具箱内的子类目

在编辑器的工具箱中,一个 类目可以有子类目,而子类目 也可以有子类目,以此类推... 如果您想把插件拼图组织成一个树状结构, 可以使用此功能。

通过 嵌套category元素来实现: <category name="My Awesome Plugin" color="green"> <block type="myPuzzle"></block> <category name="1" color="red"> <category name="1.1" color="silver"> <block type="anotherPuzzle"></block> </category> </category> <category name="2" color="blue"> <block type="doSomethingCool"></block> </category> </category>
任何category都可以同时包含 categoryblock 元素 (但这不是强制性的)。这样在一个父级类目中可 同时包含拼图模块和子类目。

脚本

<script> 元素是 init.plug 的一个可选的部分。 它可以用来为拼图模块添加一些初始化代码。 有时候,在您的拼图被使用之前,您可能需要 进行繁重的计算和缓存一些数据——这 就是 <script> 的作用。

如果您在 <script> 中定义了一个 code() 函数, 它将被用来生成代码,并在所有拼图之前执行一次。这个 code() 函数 会返回一个包含javascript代码的字符串。 <script> function code() { // this line will be executed before any puzzles return `console.log('Powered by My Awesome Plugin!');`; } </script>

code() 函数返回的初始化代码 只有在该插件的拼图在应用中被实际使用时, 才会被添加到生成的逻辑文件中(被添加到工作区且未被禁用)。

.block文件格式

.block 为扩展名的插件文件是用来定义 单个拼图模块的,特别是定义一个拼图模块的外观以 及它在添加到工作区时应该产生的代码。一个插件可以 没有 .block 文件。如果您想创建一个只有 库存 拼图 模块的工具箱类目(可以包含 更复杂的模块设置), 可使用此方法。

一个 .block 文件的名称被用来指示哪些拼图模块 应该被 包含在插件的工具箱类目 中。

下面是一个 .block 文件的最小化实例。 <template color="green"> <dummy> <label>myPuzzle</label> </dummy> </template> <script> function code(block) { return `console.log('This is my first puzzle!');`; } </script> 这里我们有一个 <template> 元素, 定义了拼图模块的外观。还有一个 <script> 元素, 里面有一个 code() 函数。这个 code() 函数返回 一个字符串,其中包含将生成的代码, 以代替这个拼图。

因此,根据上面的实例,我们期望得到的 简单拼图模块是绿色的,并且有一个 "myPuzzle" 文本标签。


如果将其添加到工作区,它将会在浏览器 控制台中打印出以下信息:

Block模板

拼图模块的外观可以通过两种方式定义: 通过 <template> XML元素和通过 template() 函数。 前者更简单易用。例如,一个典型 拼图的 <template> 可以是这样的:

<template color="green" inline="true" output="Dictionary" tooltip="This is my first puzzle!" help="https://soft8soft.com" > <dummy name="myDummyInput"> <label>enable</label> <checkbox name="myCheckbox">true</checkbox> </dummy> <value name="myValueInput"> <label>input value</label> </value> </template> <script> function code(block) { return `console.log('This is my first puzzle!');`; } </script>

另一种方法是使用 template() 函数。 这是一个您可以在 <script> 元素内定义的函数。 它也可以用来配置拼图的外观, 但这次是通过 Blockly JavaScript API 而 非XML元素与属性配置的。 它会接收 Blockly.BlockSvg 实例型的block参数。

可以通过使用 template() 函数来重写上面实例中的模块, 如下所示:

<script> function template(block) { block.setColor('green'); block.setInputsInline(true); block.setOutput(true, 'Dictionary'); block.setTooltip('This is a test puzzle!'); block.setHelpUrl('https://soft8soft.com'); block.appendDummyInput('myDummyInput') .appendField('enable') .appendField(new Blockly.FieldCheckbox(true), 'myCheckbox'); block.appendValueInput('myValueInput') .appendField('input value'); } function code(block) { return `console.log('This is my first puzzle!');`; } </script>

这种方法更加灵活,但需要对相应的API有所了解。 如果您需要做一些无法通过 <template> 元素 实现的设置,可使用这种方法。 此外,您可以同时 使用 <template>template()

本节提供了 <template>XML) 和 template()(JS)两种变体的示例。

请注意,本节只是简要介绍了如何创建一个 自定义拼图模块。 关于进行常规定制的详细介绍, 请查看谷歌Blockly关于 自定义模块字段 的文档。

Block的颜色

您可以设置拼图模块的颜色,使其外观更加独特。

可以通过 color 属性来设置。 <template color="green"></template>
可以通过 block.setColor 来设置。 <script> function template(block) { block.setColor('green'); } </script>

颜色必须是这里描述的格式之一: 颜色格式

Block的工具提示

您可以添加一个工具提示,当把鼠标 悬停在一个模块上面时就会出现。工具 提示用于向用户提供简单的描述,如拼图 的用途,作用方式的,有哪些使用提示等等。

可以通过 tooltip 属性来设置。 <template tooltip="This is my first puzzle!"></template>
可以通过 block.setTooltip 来设置。 <script> function template(block) { block.setTooltip('This is my first puzzle!'); } </script>

Block的帮助URL

如果 工具提示 不足 以记录拼图描述,您也可以为其添加一个链接, 指向一个有更详尽文档说明的网页。此链接 将被用于拼图上下文菜单中的帮助条目(在拼图上点击右键):

可以通过 help 属性设置: <template help="https://www.soft8soft.com/"></template>
可以通过 block.setHelpUrl 来设置。 <script> function template(block) { block.setHelpUrl('https://www.soft8soft.com/'); } </script>

添加输入插槽

拼图模块可以包含输入插槽来插入其他模块。 它们也可以使用非模块的UI元素, 如复选框或文本字段。 有3种不同类型的输入: value inputstatement inputsdummy inputs

您可以在下图中看到这些类型的 输入的区别。

使用 <value><statement><dummy> 等元素 来添加输入。valuestatement 输入 必须有一个名称(使用它们的 name 属性)。 dummy 输入通常不需要名字, 它们只是 字段 的容器。 <template> <value name="myInput"></value> <statement name="myStatement"></statement> <dummy> <checkbox name="myCheckbox">true</checkbox> </dummy> </template>
使用 block.appendValueInput , block.appendStatementInputblock.appendDummyInput 方法来添加输入。 valuestatement 输入 必须(通过name属性)被命名。 dummy 输入通常不需要名字, 它们只是 字段 的容器。 <script> function template(block) { block.appendValueInput('myValue'); block.appendStatementInput('myStatement'); block.appendDummyInput() .appendField(new Blockly.FieldCheckbox(true), 'myCheckbox'); } </script>

输入的排列

模块状输入可以垂直(默认)或 水平排列。

可以通过 inline 设置为 truefalse 的属性。 为 false 时使用垂直排列, 为 true 时使用水平排列的变体。 <template inline="true"></template>
可以通过 block.setInputsInline 方法设置。该方法可接收 一个为 truefalse 的参数。为 false 时使用垂直排列, 为 true 时使用水平排列的变体。 <script> function template(block) { block.setInputsInline(true); } </script>

添加字段

您可以在拼图中添加诸如文本标签、复选框、下拉列表、文本输入等UI元素。 这些UI元素被称为 "fields(字段)" 。 它们可被添加到任何类型的输入中, 如果您不想为拼图模块额外创建输入插槽, 可一致使用 dummy input 输入。

所有字段都有几个共同点:

让我们来看看如何在拼图中添加各种字段。

字段排列

拼图模块中的字段元素总是属于一个特定的输入插槽。 无论是单个字段还是每个输入的多个字段,它们总是 按照一定的布局进行渲染。有一件事是可以改变的, 那就是字段在输入框中的位置,特别是它们被排列在 哪一边。您可以让它们向左(默认)、 向右和向中间对齐。

要改变某个输入的字段对齐方式, 请使用相应的 输入元素 上的 align 属性。 这个属性的有效值是: Leftcenterright<template> <dummy align="left"></dummy> <value align="center" name="myValueInput"></value> <statement align="right" name="myStatementInput"></statement> </template>
要改变某个输入的字段对齐方式,请使用 setAlign 方法。 它的第一个参数应该是 Blockly.ALIGN_LEFTBlockly.ALIGN_CENTERBlockly.ALIGN_RIGHT<script> function template(block) { block.appendDummyInput() .setAlign(Blockly.ALIGN_LEFT); block.appendValueInput('myValueInput') .setAlign(Blockly.ALIGN_CENTER); block.appendStatementInput('myStatementInput') .setAlign(Blockly.ALIGN_RIGHT); } </script>

模块的连接

拼图模块可以有输入、语句和输出连接。 输入连接是为每个创建的输入插槽自动添加的, 它的作用是插入子模块。 请在这里查看关于创建输入的介绍: 添加输入。 Statement(previous和next)和 output 连接 用于将模块连接到父模块或同级模块。

previousnext 分别在拼图模块的顶部和底部 添加连接,因此拼图模块可以从 下面/上面连接到其他有匹配连接的模块。

output 在拼图模块的左侧添加了一个连接——这使得 该模块可以被插入到父模块的输入插槽中。 输出连接通常用于返回数值的拼图模块, 例如,一些数学计算的结果。

默认情况下,一个拼图模块没有任何连接。 您可以在一个拼图模块上添加任何类型的单个连接。 您也可以在一个拼图模块中添加2个 statementoutput 连接, 但只允许以下组合:previous + nextnext + output

如下演示如何将所有这些连接添加到一个模块中:

可以通过设置 prev 属性为 true 来启用 previous statement连接。 <template prev="true"></template>
通过调用 block.setPreviousStatement , 以 true 作为第一个参数, 可以启用 previous statement连接。 <script> function template(block) { block.setPreviousStatement(true); } </script>
可以通过将 next 属性 设置为 true 来启用 next statement连接。 <template next="true"></template>
可以通过调用 block.setNextStatement , 以 true 作为第一个参数, 来启用 next statement连接。 <script> function template(block) { block.setNextStatement(true); } </script>
可以通过设置 output 属性为空字符串 来启用 output 连接。 <template output=""></template>
可以通过调用 block.setOutput 启用 output 连接, 并将 true 作为第一个参数。 <script> function template(block) { block.setOutput(true); } </script>

输入/输出类型检查

默认情况下,所有具有合适输入/输出的拼图模块都可以相互连接。 但这并不意味着所有的拼图模块都应该是兼容的。 比方说,我们有一个拼图模块返回一个坐标数组, 而另一个拼图模块有一个期望得到动画名称的输入。 如果我们试图将第一个模块插入第二个模块中, 那么事情可能不会按预期执行。 从这些拼图中产生的代码可能是无效的,甚至可能导致页面崩溃。

好在有一种方法可以解决此类情况。 每一个输入和输出都可以被分配一个类型, 只有那些具有匹配类型的模块可以相互连接。

关于输出和其他连接的常用信息,请参见 模块的连接

输出类型可以通过 <template>output 属性来指定。 一个特定的输入可以接受的类型 是由输入的 type 属性定义的。 <!-- the block's output type is 'String' --> <template output="String"> <!-- this input accepts only blocks of type 'Number' --> <value name="myInput" type="Number"></value> </template>
输出类型可以通过调用 block.setOutput 方法 来分配给一个模块, 同时将 true 作为其第一个参数, 将所需类型作为其第二个参数。 通过调用 setCheck 方法 来设置一个特定输入所能接受的类型。 <script> function template(block) { // this input accepts only blocks of type 'Number' block.appendValueInput('myInput') .setCheck('Number'); // the block's output type is 'String' block.setOutput(true, 'String'); } </script>

在上面的例子中,该模块有一个只能接受 "number" 类型 或未指定类型的模块的输入(如果没有通过 setOutput 设置类型)。 该模块也有 "String" 输出类型, 这意味着它只能插入具有 "String" 或 未指定类型的输入(如果没有通过 setCheck 设置类型)。

输入和输出也可以有一个以上的类型。

为了拥有多个输入/输出类型, 请在相应的 typeoutput 属性中 填写多个类型值,并以空格分隔: <!-- this block's output type is 'String' or 'Animation' --> <template output="String Animation"> <!-- this input accepts only blocks of type 'Number' or 'Object3D' --> <value name="myInput" type="Number Object3D"></value> </template>
为了拥有多个输入/输出类型, 在 setCheck 和/或 setOutput 中提供类型阵列。 <script> function template(block) { // this input accepts only blocks of type 'Number' or 'Object3D' block.appendValueInput('myInput') .setCheck(['Number', 'Object3D']); // this block's output type is 'String' or 'Animation' block.setOutput(true, ['String', 'Animation']); } </script>

标准的Verge3D拼图使用了几种特定的输入/输出类型, 您也可以在您的拼图中借用这些类型。

您无需局限于上述类型, 我们鼓励您创造自己的输入/输出类型, 以更好地适应您的拼图。

Code Function

Code() 函数是用来提供Javascript代码的, 如果拼图被添加到工作区,它就会被生成。 一般来说,这是您定义拼图逻辑的地方, 也是您实现大部分拼图功能的地方。

该函数预期返回一个包含js代码的字符串。 它的工作方式类似于 init.plug的代码函数 的工作方式, 只是在这种情况下,代码被添加和使用的次数 与被添加到工作区的拼图模块的数量相同。 这个 code() 函数接收一个 block 参数—— 它与在 template() 中 使用的 Blockly.BlockSvg 是同一个实例,。

一起来看看您能用 code() 函数做些什么。

基本的代码生成

code() 能做的非常简单的事情 就是返回一个带有几行 javascript 代码的字符串, 这些代码将被添加到生成的 visual_logic.js 文件中。
例如,下面的代码打开了标准的浏览器警报对话框。 function code(block) { return `alert('Test');`; } 而在这里,我们仅返回一个为1的值: function code(block) { return `1`; } - 这个例子并没有什么意义,除非此模块有输出连接。 在这种情况下,返回值可以从一个父模块中访问, 该父模块的输入端之一插入了这个模块。

这里是一个更高级的例子: function code(block) { const fun = function() { app.scene.traverse(function(obj) { obj.material = new v3d.MeshBasicMaterial({ color: new v3d.Color(Math.random(), Math.random(), Math.random()) }); }); } return `(${fun})();`; } - 这里所有的对象都得到了一个新的材质,其颜色是随机生成的。

缓解代码臃肿问题

默认情况下,每次在工作区使用拼图时, 拼图的代码都会被复制到生成的visual_logic.js文件中。 如果您只有几行代码,这并不是什么问题。 但是,如果代码很笨重、很复杂,并且分成几个你可能只想声明一次的函数, 那么默认的方法就会变得效率低下, 并导致产生的visual_logic.js文件变得臃肿。

为了解决这个问题, 您可以利用 code() 函数中的一个特殊方法——Plug.provision() 。 我们通过下方的示例来演示如何使用: function code(block) { const fun = Plug.provide('myFunction', function(a, b, c) { console.log(a, b, c); }); return `${fun}(1, 2, 3);`; } 在这里,我们通过 Plug.provide() 定义了一个函数 "myFunction" , 这意味着无论拼图在工作区被使用多少次, "myFunction" 都只会被复制到visual_logic.js中一次。 另外,从 code() 返回的值只有 ${fun}(1, 2, 3); ,这基本上是一个函数调用 "myFunction(1, 2, 3);" , 它将被插入visual_logic.js中, 用于工作区的每个拼图。 而这正是我们的拼图所希望的, 因为 "myFunction" 只需要声明一次, 之后就可以多次调用。

Plug.provide() 中的第一个参数 应该是一个唯一的函数标识符。 返回的变量fun是所提供的函数的名称(它通常与第一个参数传递的值几乎相同, 但也可以不同,因为拼图编辑器需要确保 这个名称是有效的,并且与工作区使用的其他函数/变量没有冲突)。 应该使用该名称(而不是原始的 "myFunction" ) 来调用提供的函数 - 这就是它在 return 语句下的 部分中的完成方式。

访问输入和字段

如果一个拼图模块有 fieldsinput slotstemplate() 函数中定义, 那么您很可能希望它们 影响 code() 函数内生成的内容。 例如, 复选框 可以启用或禁用您的拼图的某个功能。

用于访问 value inputsstatement inputsfields 的API方法有: Blockly.JavaScript.valueToCode , Blockly.JavaScript.statementToCodeblock.getFieldValue

让我们做一个既有输入又有字段的拼图模块。 下面是.block文件的全部内容: <template color="green"> <dummy> <label>myPuzzle</label> </dummy> <value name="myValue"></value> <statement name="myStatement"></statement> <dummy> <checkbox name="myCheckbox">true</checkbox> </dummy> </template> <script> function wrapFn(contents) { return `function() {${contents}}`; } function code(block) { const myInput = Blockly.JavaScript.valueToCode(block, 'myValue', Blockly.JavaScript.ORDER_NONE) || `''`; const myStatement = wrapFn(Blockly.JavaScript.statementToCode(block, 'myStatement')); const myCheckbox = block.getFieldValue('myCheckbox') === 'TRUE'; const fun = Plug.provide('myFunction', function(input, statements, checkbox) { console.log('input value:', input); statements(); // execute puzzles from the myStatement input console.log('checkbox state:', checkbox); }); return `${fun}(${myInput}, ${myStatement}, ${myCheckbox});`; } </script> 在这个例子中,该模块定义了一个名为 "myValue" 的数值输入, 一个语句输入 "myStatement 和一个复选框字段 "myCheckbox" 。 我们通过上面描述的API获得它们的值, 但在将它们传入 "myFunction" 之前,它们经历了一些值得注意的变化。 var myInput = Blockly.JavaScript.valueToCode(block, 'myValue', Blockly.JavaScript.ORDER_NONE) || `''`; - 输入插槽可能会没有插入模块, 所以我们通过在末尾添加*|| `''*部分, 来确保在这种情况下可以得到一个空字符串。 function wrapFn(contents) { return `function() {${contents}}`; } ... var myStatement = wrapFn(Blockly.JavaScript.statementToCode(block, 'myStatement')); - Statement输入插槽通常包含一组Statement。 把它们包在一个函数中是很方便的(见 wrapFn 的作用), 以便把该函数对象作为参数传递,然后把它当作 一个回调使用。 var myCheckbox = block.getFieldValue('myCheckbox') === 'TRUE'; - 在这里,复选框的值只用于与 "TRUE" 进行比较, 产生一个布尔值的结果。

最后,所有的值都可以传递给"myFunction" ,如下所示: return `${fun}(${myInput}, ${myStatement}, ${myCheckbox});`; 现在您能够随心所欲地使用它们了。 const fun = Plug.provide('myFunction', function(input, statements, checkbox) { console.log('input value:', input); statements(); // execute puzzles from the myStatement input console.log('checkbox state:', checkbox); });

插件和图模块的错误

开发或使用插件时,您可能会遇到不同类型的错误, 与某个拼图模块或与整个插件有关。 本节将介绍典型的插件和拼图模块错误,以及如何处理这些问题。

如果在加载插件或初始化拼图模块的过程中出现错误, 那么拼图编辑器会在浏览器控制台中打印出相应的错误信息。 通常情况下, 这些错误会像下面这样:

PluginError(PLUGIN_NAME) ...
BlockError(PLUGIN_NAME/BLOCK_NAME) ...
Puzzle "PLUGIN_NAME/BLOCK_NAME" is not defined properly. Replaced with a dummy block.

- 它们指的是一个特定的插件和 一个特定模块导致的错误。

在插件出错的情况下, 整个插件的类目很可能都不会在工具箱中显示。 如果是拼图模块错误,受影响的拼图模块将被标记为无效,并有一个明显的外观标示:

无效拼图块
在工具箱和工作区中的无效拼图

以下是最常见的插件和拼图模块错误的列表:


BlockError(PLUGIN_NAME/BLOCK_NAME): error parsing .block file - "This page contains the following errors:error on line ..."

这意味着在相应的.block文件中存在着XML错误, 使其无法被解析。 例如,缺少结尾的<script>标签会导致这样的错误。 <script> function template(block) {} function code(block) {}


BlockError(PLUGIN_NAME/BLOCK_NAME): error parsing .block file - "ReferenceError (SyntaxError, TypeError, etc...) ..."

这代表着相应的.block文件的<script>元素中的 代码包含错误信息中指定的 JavaScript错误。


BlockError(PLUGIN_NAME/BLOCK_NAME): validation error - "TypeError: Child block does not have output or previous statement."

这个错误所指的模块有一个子模块插入其中。 但是,这个子模块没有 输出或前一个连接 , 因此不能以这种方式使用。这种情况可能发生在 插件的工具箱 类目 中的拼图块, 因为它们是在init.plug中配置的, 也可能发生在工作区实际使用的块上。


BlockError(PLUGIN_NAME/BLOCK_NAME): validation error - "Error: Connection checks failed. Output/Previous/Next Connection of "PLUGIN_NAME/BLOCK_NAME" block (id="BLOCK_ID") expected TYPE_CHILD, found TYPE_PARENT"

这个错误所指的模块连接到一个父模块, 但是父模块的输入插槽和子模块的输出连接的 类型 不兼容。 这种情况可能发生在插件的工具箱 类目 中的拼图块, 因为它们是在init.plug中配置的, 也可能发生在工作区 实际使用的块上。


BlockError(PLUGIN_NAME/null): validation error - "TypeError: Unknown block type: PLUGIN_NAME/null"

这个错误意味着该插件的init.plug文件引用了一个 没有指定 类型属性 的模块, 这是不允许的。例如,这样做是行不通的: <category name="My Awesome Plugin" color="green"> <block></block> </category>


BlockError(PLUGIN_NAME/BLOCK_NAME): validation error - "TypeError: Unknown block type: PLUGIN_NAME/BLOCK_NAME"

这个错误信息通常出现在一个 "错误解析的 .block 文件" 的错误之后, 它表明由于最初的错误, 提到的拼图模块没有被正确加载和初始化。


BlockError(PLUGIN_NAME/BLOCK_NAME): error calling template() function ...

相应的.block文件要么有一个定义不正确的 "template" 元素, 要么在其 template() 函数中 包含JavaScript错误。


BlockError(PLUGIN_NAME/BLOCK_NAME): error calling code() function ...

相应的.block文件 在其 code() 函数内 含有JavaScript错误。


PluginError(PLUGIN_NAME): error parsing init.plug file - "This page contains the following errors:error on line ..."

这意味着在插件的init.plug文件中存在着XML错误, 使其无法被解析。 例如,缺少结尾的<category>标签会导致这样的错误。 <category name="MyAwesomePlugin" color="green"> <block type="myPuzzle"></block>


PluginError(PLUGIN_NAME): error parsing init.plug file - "ReferenceError (SyntaxError, TypeError, etc...) ..."

这意味着如果相应的init.plug文件的<script>元素中 的代码包含错误信息中 指定的JavaScript错误。


PluginError(PLUGIN_NAME): error calling code() function ...

该插件的init.plug文件 在其 code() 函数内 含有JavaScript错误。


Puzzle "PLUGIN_NAME/BLOCK_NAME" is not defined properly. Replaced with a dummy block.

这条错误信息通常出现在一个BlockError和/或 PluginError错误信息之后, 它表明由于原始错误, 提到的拼图模块没有被正确加载和初始化。 为了仍然能够加载拼图并在一定程度上保持其可操作性 这样的拼图模块(无论是在插件的工具箱类目中还是在工作区中)都会被特殊的dummy模块所取代。 Dummy模块的例子见 这张图片


分享您的插件

当您的插件开发完成时,请随时通过以下方式分享:

在使用拼图时遇到困难?

欢迎您随时在 论坛上提问!您还可以加入中文用户社区QQ群(171678760),在线寻求帮助。