插件 | 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"> <block type="myPuzzle"></block> <block type="doSomethingCool"></block> <!-- <block type="testPuzzle"></block> --> <block type="anotherPuzzle"></block> </category>

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); }); [anchor:%TOC_DECLEVEL_HACK]

插件和图模块的错误

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

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

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. Input "PARENT_INPUT_NAME" connection on "PLUGIN_NAME/BLOCK_NAME" block (id="BLOCK_ID") expected TYPE_PARENT, found TYPE_CHILD"

这个错误所指的模块有一个子模块插入其中,但是父模块的输入插槽和子模块的输出连接的 类型 不兼容。这种情况可能发生在插件的工具箱 类目 中的拼图块,因为它们是在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),在线寻求帮助。