|
|
@@ -0,0 +1,401 @@
|
|
|
+import * as Blockly from "blockly";
|
|
|
+import 'blockly/msg/zh-hans';
|
|
|
+import { javascriptGenerator } from "blockly/javascript";
|
|
|
+
|
|
|
+/**
|
|
|
+ * 定义所有可用的自定义积木配置
|
|
|
+ */
|
|
|
+const availableBlocks = {
|
|
|
+ // 通用积木 - 始终可用
|
|
|
+ move_forward: {
|
|
|
+ jsonConfig: {
|
|
|
+ "type": "move_forward",
|
|
|
+ "message0": "向前移动",
|
|
|
+ "previousStatement": null,
|
|
|
+ "nextStatement": null,
|
|
|
+ "colour": 230,
|
|
|
+ "tooltip": "控制角色向前移动一格",
|
|
|
+ "helpUrl": "",
|
|
|
+ "icon": {
|
|
|
+ "src": "▶",
|
|
|
+ "width": 16,
|
|
|
+ "height": 16,
|
|
|
+ "alt": "向前移动"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isGeneral: true // 标记为通用积木
|
|
|
+ },
|
|
|
+ turn_left: {
|
|
|
+ jsonConfig: {
|
|
|
+ "type": "turn_left",
|
|
|
+ "message0": "向左转",
|
|
|
+ "previousStatement": null,
|
|
|
+ "nextStatement": null,
|
|
|
+ "colour": 230,
|
|
|
+ "tooltip": "控制角色向左转",
|
|
|
+ "helpUrl": "",
|
|
|
+ "icon": {
|
|
|
+ "src": "◀",
|
|
|
+ "width": 16,
|
|
|
+ "height": 16,
|
|
|
+ "alt": "向左转"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isGeneral: true
|
|
|
+ },
|
|
|
+ turn_right: {
|
|
|
+ jsonConfig: {
|
|
|
+ "type": "turn_right",
|
|
|
+ "message0": "向右转",
|
|
|
+ "previousStatement": null,
|
|
|
+ "nextStatement": null,
|
|
|
+ "colour": 230,
|
|
|
+ "tooltip": "控制角色向右转",
|
|
|
+ "helpUrl": "",
|
|
|
+ "icon": {
|
|
|
+ "src": "▶",
|
|
|
+ "width": 16,
|
|
|
+ "height": 16,
|
|
|
+ "alt": "向右转"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isGeneral: true
|
|
|
+ },
|
|
|
+ turn_around: {
|
|
|
+ jsonConfig: {
|
|
|
+ "type": "turn_around",
|
|
|
+ "message0": "向后转",
|
|
|
+ "previousStatement": null,
|
|
|
+ "nextStatement": null,
|
|
|
+ "colour": 230,
|
|
|
+ "tooltip": "控制角色向后转",
|
|
|
+ "helpUrl": "",
|
|
|
+ "icon": {
|
|
|
+ "src": "◀▶",
|
|
|
+ "width": 16,
|
|
|
+ "height": 16,
|
|
|
+ "alt": "向后转"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isGeneral: true
|
|
|
+ },
|
|
|
+ pickup_item: {
|
|
|
+ jsonConfig: {
|
|
|
+ "type": "pickup_item",
|
|
|
+ "message0": "拾取物品",
|
|
|
+ "previousStatement": null,
|
|
|
+ "nextStatement": null,
|
|
|
+ "colour": 30,
|
|
|
+ "tooltip": "尝试拾取当前位置的物品",
|
|
|
+ "helpUrl": "",
|
|
|
+ "icon": {
|
|
|
+ "src": "👆",
|
|
|
+ "width": 16,
|
|
|
+ "height": 16,
|
|
|
+ "alt": "拾取物品"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isGeneral: true
|
|
|
+ },
|
|
|
+ use_item: {
|
|
|
+ jsonConfig: {
|
|
|
+ "type": "use_item",
|
|
|
+ "message0": "使用物品",
|
|
|
+ "previousStatement": null,
|
|
|
+ "nextStatement": null,
|
|
|
+ "colour": 30,
|
|
|
+ "tooltip": "在当前位置使用物品",
|
|
|
+ "helpUrl": "",
|
|
|
+ "icon": {
|
|
|
+ "src": "👇",
|
|
|
+ "width": 16,
|
|
|
+ "height": 16,
|
|
|
+ "alt": "使用物品"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isGeneral: true
|
|
|
+ },
|
|
|
+ // 特殊积木 - 可根据配置动态加载
|
|
|
+ pause: {
|
|
|
+ jsonConfig: {
|
|
|
+ "type": "pause",
|
|
|
+ "message0": "暂停 %1 秒",
|
|
|
+ "args0": [
|
|
|
+ {
|
|
|
+ "type": "field_number",
|
|
|
+ "name": "SECONDS",
|
|
|
+ "value": 1,
|
|
|
+ "min": 1,
|
|
|
+ "max": 10,
|
|
|
+ "precision": 1
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "previousStatement": null,
|
|
|
+ "nextStatement": null,
|
|
|
+ "colour": 0,
|
|
|
+ "tooltip": "让角色在原地停留指定的秒数",
|
|
|
+ "helpUrl": "",
|
|
|
+ "icon": {
|
|
|
+ "src": "⏸",
|
|
|
+ "width": 16,
|
|
|
+ "height": 16,
|
|
|
+ "alt": "暂停"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isGeneral: false
|
|
|
+ },
|
|
|
+ play_sound: {
|
|
|
+ jsonConfig: {
|
|
|
+ "type": "play_sound",
|
|
|
+ "message0": "声音",
|
|
|
+ "previousStatement": null,
|
|
|
+ "nextStatement": null,
|
|
|
+ "colour": 160,
|
|
|
+ "tooltip": "自动触发特效提示音,并根据当前方格类型执行相应操作",
|
|
|
+ "helpUrl": "",
|
|
|
+ "icon": {
|
|
|
+ "src": "🔊",
|
|
|
+ "width": 16,
|
|
|
+ "height": 16,
|
|
|
+ "alt": "声音"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isGeneral: false
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 定义所有可用的JavaScript生成器配置
|
|
|
+ */
|
|
|
+const availableGenerators = {
|
|
|
+ // 通用生成器 - 始终可用
|
|
|
+ move_forward: function(block) {
|
|
|
+ return 'await moveForward();\n';
|
|
|
+ },
|
|
|
+ turn_left: function(block) {
|
|
|
+ return 'await turnLeft();\n';
|
|
|
+ },
|
|
|
+ turn_right: function(block) {
|
|
|
+ return 'await turnRight();\n';
|
|
|
+ },
|
|
|
+ turn_around: function(block) {
|
|
|
+ return 'await turnAround();\n';
|
|
|
+ },
|
|
|
+ pickup_item: function(block) {
|
|
|
+ return 'await pickupItem();\n';
|
|
|
+ },
|
|
|
+ use_item: function(block) {
|
|
|
+ return 'await useItem();\n';
|
|
|
+ },
|
|
|
+ // 特殊生成器 - 可根据配置动态加载
|
|
|
+ pause: function(block) {
|
|
|
+ const seconds = block.getFieldValue('SECONDS');
|
|
|
+ return `await pause(${seconds});\n`;
|
|
|
+ },
|
|
|
+ play_sound: function(block) {
|
|
|
+ return 'await playSound();\n';
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 注册自定义Blockly积木
|
|
|
+ * @param {Array<string>} blocklySpecialBlocks - 特殊积木方法标识集合
|
|
|
+ */
|
|
|
+export function registerCustomBlocks(blocklySpecialBlocks = []) {
|
|
|
+ // 遍历所有可用积木
|
|
|
+ for (const [blockName, blockConfig] of Object.entries(availableBlocks)) {
|
|
|
+ // 如果是通用积木,或者是允许的特殊积木,则注册
|
|
|
+ if (blockConfig.isGeneral || blocklySpecialBlocks.includes(blockName)) {
|
|
|
+ Blockly.Blocks[blockName] = {
|
|
|
+ init: function() {
|
|
|
+ this.jsonInit(blockConfig.jsonConfig);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 设置Blockly中文本地化
|
|
|
+ */
|
|
|
+export function setupBlocklyChineseLocale() {
|
|
|
+ // 覆盖默认的英文文本为中文
|
|
|
+ if (!Blockly.Msg) Blockly.Msg = {};
|
|
|
+
|
|
|
+ // 逻辑积木中文配置
|
|
|
+ Blockly.Msg.CONTROLS_IF_MSG_IF = "如果";
|
|
|
+ Blockly.Msg.CONTROLS_IF_MSG_ELSEIF = "否则如果";
|
|
|
+ Blockly.Msg.CONTROLS_IF_MSG_ELSE = "否则";
|
|
|
+ Blockly.Msg.CONTROLS_IF_MSG_DO = "执行"; // 添加这行将'do'转换为中文
|
|
|
+ Blockly.Msg.CONTROLS_IF_TOOLTIP_1 = "如果条件为真,则执行相应的代码。";
|
|
|
+ Blockly.Msg.CONTROLS_IF_TOOLTIP_2 = "如果条件为真,则执行相应的代码;否则执行其他代码。";
|
|
|
+ Blockly.Msg.CONTROLS_IF_TOOLTIP_3 = "如果条件为真,则执行相应的代码;否则检查其他条件。";
|
|
|
+
|
|
|
+ // 添加设置按钮相关的中文配置
|
|
|
+ Blockly.Msg.CONTROLS_IF_ELSEIF_TOOLTIP = "否则如果条件为真,则执行相应的代码。";
|
|
|
+ Blockly.Msg.CONTROLS_IF_ELSE_TOOLTIP = "否则执行相应的代码。";
|
|
|
+
|
|
|
+ // 打开/关闭块的提示文本
|
|
|
+ Blockly.Msg.CONTROLS_IF_TOOLTIP_IF = "添加或删除条件。";
|
|
|
+ Blockly.Msg.CONTROLS_IF_TOOLTIP_ELSE = "添加或删除否则块。";
|
|
|
+
|
|
|
+
|
|
|
+ // 比较运算符中文配置 - 添加显示文本
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_EQ = "等于";
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_NEQ = "不等于";
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_LT = "小于";
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_LTE = "小于等于";
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_GT = "大于";
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_GTE = "大于等于";
|
|
|
+
|
|
|
+ // 比较运算符提示文本(已存在)
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_TOOLTIP_EQ = "比较两个值是否相等。";
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_TOOLTIP_NEQ = "比较两个值是否不相等。";
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_TOOLTIP_LT = "比较第一个值是否小于第二个值。";
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_TOOLTIP_LTE = "比较第一个值是否小于或等于第二个值。";
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GT = "比较第一个值是否大于第二个值。";
|
|
|
+ Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GTE = "比较第一个值是否大于或等于第二个值。";
|
|
|
+
|
|
|
+ // 逻辑运算中文配置 - 添加显示文本
|
|
|
+ Blockly.Msg.LOGIC_OPERATION_AND = "且";
|
|
|
+ Blockly.Msg.LOGIC_OPERATION_OR = "或";
|
|
|
+
|
|
|
+ // 逻辑运算提示文本(已存在)
|
|
|
+ Blockly.Msg.LOGIC_OPERATION_TOOLTIP_AND = "如果两个条件都为真,则结果为真。";
|
|
|
+ Blockly.Msg.LOGIC_OPERATION_TOOLTIP_OR = "如果任一条件为真,则结果为真。";
|
|
|
+
|
|
|
+ // 布尔值配置
|
|
|
+ Blockly.Msg.LOGIC_BOOLEAN_TRUE = "真";
|
|
|
+ Blockly.Msg.LOGIC_BOOLEAN_FALSE = "假";
|
|
|
+ Blockly.Msg.LOGIC_BOOLEAN_TOOLTIP = "返回一个布尔值:真或假。";
|
|
|
+
|
|
|
+ // 循环积木中文配置(保持不变)
|
|
|
+ Blockly.Msg.CONTROLS_REPEAT_TITLE = "重复%1次";
|
|
|
+ Blockly.Msg.CONTROLS_REPEAT_TOOLTIP = "重复执行内部代码指定次数。";
|
|
|
+
|
|
|
+ // 数学积木中文配置(保持不变)
|
|
|
+ Blockly.Msg.MATH_NUMBER_TOOLTIP = "一个数字。在编辑器中双击以更改。";
|
|
|
+ Blockly.Msg.MATH_ADDITION_SYMBOL = "加";
|
|
|
+ Blockly.Msg.MATH_SUBTRACTION_SYMBOL = "减";
|
|
|
+ Blockly.Msg.MATH_MULTIPLICATION_SYMBOL = "乘";
|
|
|
+ Blockly.Msg.MATH_DIVISION_SYMBOL = "除";
|
|
|
+ Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_ADD = "返回两个数的和。";
|
|
|
+ Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_SUBTRACT = "返回第一个数减去第二个数的差。";
|
|
|
+ Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MULTIPLY = "返回两个数的积。";
|
|
|
+ Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_DIVIDE = "返回第一个数除以第二个数的商。";
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 注册JavaScript生成器
|
|
|
+ * @param {Array<string>} blocklySpecialBlocks - 特殊积木方法标识集合
|
|
|
+ */
|
|
|
+export function registerJavaScriptGenerators(blocklySpecialBlocks = []) {
|
|
|
+ // 遍历所有可用生成器
|
|
|
+ for (const [blockName, generatorFunction] of Object.entries(availableGenerators)) {
|
|
|
+ // 检查对应的积木是否为通用积木或允许的特殊积木
|
|
|
+ const blockConfig = availableBlocks[blockName];
|
|
|
+ if (blockConfig && (blockConfig.isGeneral || blocklySpecialBlocks.includes(blockName))) {
|
|
|
+ javascriptGenerator.forBlock[blockName] = generatorFunction;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 为重复循环块注册自定义生成器,确保支持异步操作(始终可用)
|
|
|
+ javascriptGenerator.forBlock['controls_repeat_ext'] = function(block) {
|
|
|
+ const repeats = javascriptGenerator.valueToCode(block, 'TIMES', javascriptGenerator.ORDER_ATOMIC) || '0';
|
|
|
+
|
|
|
+ // 确保获取到的是数字类型,如果是字符串需要转换
|
|
|
+ const safeRepeats = `(function() {
|
|
|
+ const num = Number(${repeats});
|
|
|
+ return isNaN(num) ? 0 : Math.max(0, Math.floor(num));
|
|
|
+ })()`;
|
|
|
+
|
|
|
+ // 获取循环体代码
|
|
|
+ const branch = javascriptGenerator.statementToCode(block, 'DO');
|
|
|
+
|
|
|
+ // 生成支持异步的循环代码
|
|
|
+ let code = `for (let i = 0; i < ${safeRepeats}; i++) {\n`;
|
|
|
+ code += javascriptGenerator.prefixLines(branch, javascriptGenerator.INDENT);
|
|
|
+ code += '}\n';
|
|
|
+
|
|
|
+ return code;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 为text_print块添加生成器,用于调试(始终可用)
|
|
|
+ javascriptGenerator.forBlock['text_print'] = function(block) {
|
|
|
+ const msg = javascriptGenerator.valueToCode(block, 'TEXT', javascriptGenerator.ORDER_NONE) || '';
|
|
|
+ return msg;
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 初始化Blockly工作区
|
|
|
+ * @param {Object} options - 初始化选项
|
|
|
+ * @returns {Blockly.Workspace} - 初始化后的Blockly工作区
|
|
|
+ */
|
|
|
+export function initBlockly(options = {}) {
|
|
|
+ // 应用中文配置
|
|
|
+ setupBlocklyChineseLocale();
|
|
|
+
|
|
|
+ const defaultOptions = {
|
|
|
+ toolbox: document.getElementById('toolbox'),
|
|
|
+ collapse: false,
|
|
|
+ comments: true,
|
|
|
+ disable: false, // 设为false以允许编辑
|
|
|
+ maxBlocks: Infinity,
|
|
|
+ trashcan: true,
|
|
|
+ horizontalLayout: false,
|
|
|
+ toolboxPosition: 'start',
|
|
|
+ toolboxCollapse: false, // 设置工具箱默认展开
|
|
|
+ toolboxAlwaysExpanded: true, // 确保点击类别后保持展开状态
|
|
|
+ css: true,
|
|
|
+ media: 'https://unpkg.com/blockly/media/',
|
|
|
+ rtl: false,
|
|
|
+ scrollbars: true,
|
|
|
+ sounds: false, // 禁用声音以提高性能
|
|
|
+ oneBasedIndex: true,
|
|
|
+ grid: {
|
|
|
+ spacing: 20,
|
|
|
+ length: 3,
|
|
|
+ colour: "#ccc",
|
|
|
+ snap: true
|
|
|
+ },
|
|
|
+ zoom: {
|
|
|
+ controls: true,
|
|
|
+ wheel: true,
|
|
|
+ startScale: 1.0,
|
|
|
+ maxScale: 3,
|
|
|
+ minScale: 0.3,
|
|
|
+ scaleSpeed: 1.2
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 合并默认选项和用户提供的选项
|
|
|
+ const finalOptions = { ...defaultOptions, ...options };
|
|
|
+
|
|
|
+ return Blockly.inject('blocklyDiv', finalOptions);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取所有可用的积木配置
|
|
|
+ * @returns {Object} 所有可用积木的配置对象
|
|
|
+ */
|
|
|
+export function getAllBlocksConfig() {
|
|
|
+ return {...availableBlocks};
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取所有可用的生成器配置
|
|
|
+ * @returns {Object} 所有可用生成器的配置对象
|
|
|
+ */
|
|
|
+export function getAllGeneratorsConfig() {
|
|
|
+ return {...availableGenerators};
|
|
|
+}
|
|
|
+
|
|
|
+export default {
|
|
|
+ registerCustomBlocks,
|
|
|
+ registerJavaScriptGenerators,
|
|
|
+ setupBlocklyChineseLocale,
|
|
|
+ initBlockly,
|
|
|
+ getAllBlocksConfig,
|
|
|
+ getAllGeneratorsConfig
|
|
|
+};
|