插件 | Plugins
Verge3D拼图编辑器提供 了加载您自己自定义拼图的能力, 从而使您可以用您一直想要的功能 来扩展编辑器的功能。
内容
- 安装插件
- 插件文件概述
- 插件和图模块的错误
- BlockError(PLUGIN_NAME/BLOCK_NAME): error parsing .block file - "This page contains the following errors:error on line ..."
- BlockError(PLUGIN_NAME/BLOCK_NAME): error parsing .block file - "ReferenceError (SyntaxError, TypeError, etc...) ..."
- BlockError(PLUGIN_NAME/BLOCK_NAME): validation error - "TypeError: Child block does not have output or previous statement."
- 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"
- BlockError(PLUGIN_NAME/null): validation error - "TypeError: Unknown block type: PLUGIN_NAME/null"
- BlockError(PLUGIN_NAME/BLOCK_NAME): validation error - "TypeError: Unknown block type: PLUGIN_NAME/BLOCK_NAME"
- BlockError(PLUGIN_NAME/BLOCK_NAME): error calling template() function ...
- BlockError(PLUGIN_NAME/BLOCK_NAME): error calling code() function ...
- PluginError(PLUGIN_NAME): error parsing init.plug file - "This page contains the following errors:error on line ..."
- PluginError(PLUGIN_NAME): error parsing init.plug file - "ReferenceError (SyntaxError, TypeError, etc...) ..."
- PluginError(PLUGIN_NAME): error calling code() function ...
- Puzzle "PLUGIN_NAME/BLOCK_NAME" is not defined properly. Replaced with a dummy block.
- 分享您的插件
安装插件
插件是包含一堆与插件相关的文件的目录。 插件应该放在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>
可以用以下格式来定义颜色:
- 十六进制三联体,如: #f61 或 #f0562f
- 在0°-360°范围内的色相值,例如: 140 (在HSV颜色 模型的范围内,S和V分别固定为45%和65%)。
- 颜色关键词,如: aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver, teal, white 和 yellow
可用的拼图
要使一个拼图模块在插件的工具箱类目中可用, 应该通过 block 元素和其 type 属性来指定。 type属性可以参考以下拼图:
-
由插件自己定义的拼图——在这种情况下,
type 应该根据您的插件目录中相应的 .block
文件的名称来设置(例如,myPuzzle 表示block文
件 my_awesome_plugin/myPuzzle.block )
<category name="My Awesome Plugin" color="green"> <block type="myPuzzle"></block> </category>
-
库存拼图模块 - type应该等于
库存拼图的类型(例如:math_number)。
这样就可以直接在您的插件的工具箱类目中添加
库存拼图。
<category name="My Awesome Plugin" color="green"> <block type="math_number"></block> </category>
-
由其他插件定义的拼图—— type 应该
是 PLUGIN_DIRECTORY_NAME/PUZZLE_TYPE 的
形式。例如,标准的 电子
商务 插件的拼图可以这样引用:
<category name="My Awesome Plugin" color="green"> <block type="E-Commerce/placeOrder"></block> </category>
为查找出某个拼图模块的类型,您可以使用拼图上下文 菜单中的 "Print Puzzle XML Tree(打印拼图XML树)" 选项:
该菜单选项可将拼图的XML树打印到 浏览器控制台。您可以在其中找到拼图类型 以及整个XML结构,可用于在设置 默认的 输入和字段值 时参考。
"Print Puzzle XML Tree(打印拼图XML树)" 菜单选项 提供了一个简单的方法,可以一次性将一整组拼图模块添 加到您的插件中。这样便于您创建某种由相连的拼图模块 组成的 "代码片段(snippets)" 并将其放入插件中。这使 得它有点类似于 拼图库 。
下面的示例解释了如何执行这一操作:
-
在拼图编辑器中创建一些拼图模块:
-
用鼠标右键点击最上面或最外部的模块,
选择 "Print Puzzle XML Tree(打印拼图XML树)" :
-
进入浏览器控制台,复制打印出来的
内容(下面是在谷歌Chrome浏览器中的截图):
-
把它粘贴到init.plug文件中的 "category" 元素内,
并保存该文件:
- 然后重新加载拼图编辑器,您所复制的模块 就会出现在插件的类目中。
拼图顺序
拼图模块按照您在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.plug 的 script 部分定义。 下方示例描述了实现方式:
<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(这也适用于label和sep元素)。
<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-init的block和label元素是
不会显示在工具箱中的。
默认的输入和字段值
如果一个拼图模块有模块输入和/或字段输入,
那么您可以指定它们的占位模块和/或默认值。
模块输入即连接其他拼图模块的插槽,字段输入
即不以拼图模块为形态的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都可以同时包含 category 和 block 元素
(但这不是强制性的)。这样在一个父级类目中可
同时包含拼图模块和子类目。
脚本
<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的颜色
您可以设置拼图模块的颜色,使其外观更加独特。
<template color="green"></template>
<script> function template(block) { block.setColor('green'); } </script>
颜色必须是这里描述的格式之一: 颜色格式。
Block的工具提示
您可以添加一个工具提示,当把鼠标 悬停在一个模块上面时就会出现。工具 提示用于向用户提供简单的描述,如拼图 的用途,作用方式的,有哪些使用提示等等。
<template tooltip="This is my first puzzle!"></template>
<script> function template(block) { block.setTooltip('This is my first puzzle!'); } </script>
Block的帮助URL
如果 工具提示 不足 以记录拼图描述,您也可以为其添加一个链接, 指向一个有更详尽文档说明的网页。此链接 将被用于拼图上下文菜单中的帮助条目(在拼图上点击右键):
<template help="https://www.soft8soft.com/"></template>
<script> function template(block) { block.setHelpUrl('https://www.soft8soft.com/'); } </script>
添加输入插槽
拼图模块可以包含输入插槽来插入其他模块。 它们也可以使用非模块的UI元素, 如复选框或文本字段。 有3种不同类型的输入: value input , statement inputs 和 dummy inputs 。
- value input - 可用于插入具有 返回值(输出连接)模块的输入插槽, 例如,带有一些数学模块的输入——数学 模块会计算并返回数值,可由父模块 (包含输入插槽的模块)访问。
- statement input - 可用于插入 一组代表一系列连续动作的模块的输入, 例如 if-else 拼图 就有这种类型的输入, 用于在 "if" 或 "else" 条件下将拼图模块分组。
- dummy inputs - 此类输入插槽 只适用于添加非模块式的用户界面元素, 如复选框、文本字段、图片等。 更多信息见 添加字段 。
您可以在下图中看到这些类型的 输入的区别。
<template> <value name="myInput"></value> <statement name="myStatement"></statement> <dummy> <checkbox name="myCheckbox">true</checkbox> </dummy> </template>
<script> function template(block) { block.appendValueInput('myValue'); block.appendStatementInput('myStatement'); block.appendDummyInput() .appendField(new Blockly.FieldCheckbox(true), 'myCheckbox'); } </script>
输入的排列
模块状输入可以垂直(默认)或 水平排列。
<template inline="true"></template>
<script> function template(block) { block.setInputsInline(true); } </script>
添加字段
您可以在拼图中添加诸如文本标签、复选框、下拉列表、文本输入等UI元素。 这些UI元素被称为 "fields(字段)" 。 它们可被添加到任何类型的输入中, 如果您不想为拼图模块额外创建输入插槽, 可一致使用 dummy input 输入。
所有字段都有几个共同点:
- 可以指定一个可选的 name 参数, 这通常用于在 code() 函数中 引用该字段, 以获得该字段的值。
- 能够提供一个可选的默认值。 如前所述,在init.plug的 类目 中 指定的默认值 始终比在拼图模板中定义的默认值 具有更高的优先级。
让我们来看看如何在拼图中添加各种字段。
-
label - 一个不可编辑的文本字段
可以通过 <label> 元素嵌套在 dummy 、 value 或 statement 输入中添加。<template> <dummy> <label>a text label</label> </dummy> </template>
可以通过 appendField 方法 和 Blockly.FieldLabel 添加到 dummy , value 或 statement 输入。<script> function template(block) { block.appendDummyInput() .appendField(new Blockly.FieldLabel('a text label')); } </script>
-
text - 一个可编辑的文本字段
可以通过 <text> 元素嵌套在 dummy , value 或 statement 输入中添加。<template> <dummy> <text name="myText">default text</text> </dummy> </template>
可以通过 appendField 方法和 Blockly.FieldTextInput 添加到 dummy , value 或 statement 输入。<script> function template(block) { block.appendDummyInput() .appendField(new Blockly.FieldTextInput('default text'), 'myText'); } </script>
-
multiline - 一个可编辑的多行文本字段
可以通过 <multiline> 元素嵌套在 dummy , value 或 statement 输入中添加。<template> <dummy> <multiline name="myTextMultiline">This is a \n multiline \n text</multiline> </dummy> </template>
可以通过 appendField 方法和 Blockly.FieldMultilineInput 添加到 dummy , value 或 statement 输入。<script> function template(block) { block.appendDummyInput() .appendField(new Blockly.FieldMultilineInput('This is a \n multiline \n text'), 'myTextMultiline'); } </script>
-
number - 一个可编辑的数值字段
可以通过 <number> 元素嵌套在 dummy , value 或 statement 输入中添加。<template> <dummy> <number name="myNumber">3</number> </dummy> </template>
可以通过 appendField 方法和 Blockly.FieldNumber 添加到 dummy , value 或 statement 输入。<script> function template(block) { block.appendDummyInput() .appendField(new Blockly.FieldNumber(3), 'myNumber'); } </script>
-
angle - 一个可编辑的字段,用于表示角度的数值(单位:度)。
可以通过 <angle> 元素嵌套在 dummy , value 或 statement 输入中添加。<template> <dummy> <angle name="myAngle">15</angle> </dummy> </template>
可以通过 appendField 方法和 Blockly.FieldAngle 添加到 dummy , value 或 statement 输入。<script> function template(block) { block.appendDummyInput() .appendField(new Blockly.FieldAngle(15), 'myAngle'); } </script>
-
checkbox - 一个典型的复选框字段
可以通过 <checkbox> 元素嵌套在 dummy, value 或 statement 输入中添加。 默认的复选框状态 应该被设置为 true 或 false 。<template> <dummy> <checkbox name="myCheckbox">true</checkbox> </dummy> </template>
可以通过 appendField 方法和 Blockly.FieldCheckbox 添加到一个 dummy , value 或 statement 输入。 默认的复选框状态作为参数 传递给 Blockly.FieldCheckbox ,可以是 true 或 false 。<script> function template(block) { block.appendDummyInput() .appendField(new Blockly.FieldCheckbox(true), 'myCheckbox'); } </script>
-
dropdown - 一个可供选择的值的列表
可以通过 <dropdown> 元素嵌套在 dummy, value 或 statement 输入中添加。
为了创建选项列表,在 <dropdown> 内 添加 <option> 元素。 所有 <option> 元素的 name 属性 必须设置为一个唯一的标识符 (更适合在代码中使用的东西, 例如COLOR_GREEN)。 在开始和结束的 <option> 标签之间 包含的文本被用作该选项的 下拉部件中显示的标签。
要设置默认选择, 可以将选项的 default 属性设置为 true 。
<template> <dummy> <dropdown name="myDropdown"> <option name="COLOR_GREEN">green</option> <option name="COLOR_YELLOW" default="true">yellow</option> <option name="COLOR_RED">red</option> </dropdown> </dummy> </template>
可以通过 appendField 方法和 Blockly.FieldDropdown 添加到 dummy 、 value 或 statement 输入。
Blockly.FieldDropdown 的第一个参数 可以是一个可用选项的数组。每个选项依次 也是一个包含两个元素的数组:一个用于在 用户界面中显示文本(表示选项内容的友好提示文本) 和一个唯一标识符 (更适合在代码中使用的东西,例如COLOR_GREEN)。
可以通过调用 setValue 方法 来设置默认选择的选项。 要指定哪个是默认的, 只需将选项的唯一标识符 作为参数传入 setValue 。
<script> function template(block) { let field = new Blockly.FieldDropdown([ // [ displayed text, option name ] ['green', 'COLOR_GREEN'], ['yellow', 'COLOR_YELLOW'], ['red', 'COLOR_RED'], ]); // setting default option field.setValue('COLOR_YELLOW'); block.appendDummyInput() .appendField(field, 'myDropdown'); } </script>
-
color picker - 一个颜色选择器部件
可以通过 <color> 元素嵌套在 dummy, value 或 statement 输入中添加。<template> <dummy> <color name="myColor">#350af5</color> </dummy> </template>
可以通过 appendField 方法 和 Blockly.FieldColor 添加到 dummy , value 或 statement 输入。<script> function template(block) { block.appendDummyInput() .appendField(new Blockly.FieldColor('#350af5'), 'myColor'); } </script>
字段排列
拼图模块中的字段元素总是属于一个特定的输入插槽。 无论是单个字段还是每个输入的多个字段,它们总是 按照一定的布局进行渲染。有一件事是可以改变的, 那就是字段在输入框中的位置,特别是它们被排列在 哪一边。您可以让它们向左(默认)、 向右和向中间对齐。
<template> <dummy align="left"></dummy> <value align="center" name="myValueInput"></value> <statement align="right" name="myStatementInput"></statement> </template>
<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 连接 用于将模块连接到父模块或同级模块。
previous 和 next 分别在拼图模块的顶部和底部 添加连接,因此拼图模块可以从 下面/上面连接到其他有匹配连接的模块。
output 在拼图模块的左侧添加了一个连接——这使得 该模块可以被插入到父模块的输入插槽中。 输出连接通常用于返回数值的拼图模块, 例如,一些数学计算的结果。
默认情况下,一个拼图模块没有任何连接。 您可以在一个拼图模块上添加任何类型的单个连接。 您也可以在一个拼图模块中添加2个 statement 或 output 连接, 但只允许以下组合:previous + next 或 next + output。
如下演示如何将所有这些连接添加到一个模块中:
<template prev="true"></template>
<script> function template(block) { block.setPreviousStatement(true); } </script>
<template next="true"></template>
<script> function template(block) { block.setNextStatement(true); } </script>
<template output=""></template>
<script> function template(block) { block.setOutput(true); } </script>
输入/输出类型检查
默认情况下,所有具有合适输入/输出的拼图模块都可以相互连接。 但这并不意味着所有的拼图模块都应该是兼容的。 比方说,我们有一个拼图模块返回一个坐标数组, 而另一个拼图模块有一个期望得到动画名称的输入。 如果我们试图将第一个模块插入第二个模块中, 那么事情可能不会按预期执行。 从这些拼图中产生的代码可能是无效的,甚至可能导致页面崩溃。
好在有一种方法可以解决此类情况。 每一个输入和输出都可以被分配一个类型, 只有那些具有匹配类型的模块可以相互连接。
关于输出和其他连接的常用信息,请参见 模块的连接 。
<!-- 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>
<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 设置类型)。
输入和输出也可以有一个以上的类型。
<!-- 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>
<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拼图使用了几种特定的输入/输出类型, 您也可以在您的拼图中借用这些类型。
- 对于Javascript类型和内置对象, 有以下几种:Number, String, Boolean, Dictionary (针对js对象), Array, Promise, Procedure (对于在拼图中定义的过程)
- 对于场景实体:Object3D, Material, Animation 。
- 其他类型:Canvas, Sound, Video
您无需局限于上述类型, 我们鼓励您创造自己的输入/输出类型, 以更好地适应您的拼图。
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 语句下的
部分中的完成方式。
访问输入和字段
如果一个拼图模块有 fields
或 input slots
在 template() 函数中定义,
那么您很可能希望它们
影响 code() 函数内生成的内容。
例如, 复选框
可以启用或禁用您的拼图的某个功能。
用于访问 value inputs 、
statement inputs
和 fields 的API方法有:
Blockly.JavaScript.valueToCode ,
Blockly.JavaScript.statementToCode 和
block.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 template() function - "TypeError: Found invalid FieldDropdown options."
这个特殊的错误意味着拼图中的 一个下拉字段有一个未命名的选项。 参见 dropdown field 以了解更多信息。
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模块的例子见 这张图片 。
分享您的插件
当您的插件开发完成时,请随时通过以下方式分享:
- 在 Verge3D论坛上发布链接。
- 写一篇 文章 或在 常用链接 章节中被提及。
- 在社交媒体上分享是使用 #verge3d 标签。
- 在Reddit分享。这里有温和的Verge3D版块,您永远都不会被禁止: r/RealVerge3D, r/3dcommerce, 及 3dconfigurators。
- 在 Gumroad 或类似的资源网站上发布。
在使用拼图时遇到困难?
欢迎您随时在 论坛上提问!您还可以加入中文用户社区QQ群(171678760),在线寻求帮助。