明:
1. blockly代碼下載,基礎代碼建議先閱讀可視化編程工具blockly——工作區、可視化編程工具blockly——可調整大小的工作區兩篇文章;
2. blockly工具箱支持xml和json兩種方式定義,本文使用xml演示,json方式創建工具箱可參考google官網文檔https://developers.google.cn/blockly/guides/configure/web/toolbox。
工具箱就是可供用戶使用的代碼塊,默認顯示在工作區的左側,如果代碼塊較多的話最好能分類組織,工具箱的代碼通過在index.html中id為toolbox的xml代碼進行定義,blcokly會解析xml并注入到頁面圖形化的工具箱代碼DOM,如下圖的工具箱包含了控制、邏輯兩個類別。
對應的xml定義代碼如下,其中category定義了一個分類,block定義了代碼塊,type指定的類別都是blockly默認提供的,定義代碼在./blockly/blocks目錄下
<xml id="toolbox" style="display: none">
<category name="控制">
<block type="controls_if"></block>
<block type="controls_whileUntil"></block>
<block type="controls_for">
</category>
<category name="邏輯">
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_boolean"></block>
</category>
</xml>
工具箱還可以多層嵌套定義,如下xml定義的工具箱效果如下:
<xml id="toolbox" style="display: none">
<category name="核心">
<category name="控制">
<block type="controls_if"></block>
<block type="controls_whileUntil"></block>
</category>
<category name="邏輯">
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_boolean"></block>
</category>
</category>
<category name="數學">
<block type="math_constant"></block>
<category name="算術">
<block type="math_arithmetic"></block>
<block type="math_number"></block>
</category>
<category name="三角">
<block type="math_single"></block>
<block type="math_trig"></block>
</category>
</category>
</xml>
工具箱分組類別還可以指定顏色,通過colour屬性進行設定,colour值的范圍是0~360,如下xml代碼生成的工具箱效果如下:
<xml id="toolbox" style="display: none">
<category name="邏輯" colour="20">
<block type="controls_if"></block>
<block type="controls_whileUntil"></block>
<block type="controls_for">
</category>
<category name="控制" colour="200">
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_boolean"></block>
</category>
</xml>
blockly提供的工具箱中有兩類有著特殊的行為,分別是變量和函數,它們的xml定義中擁有custom屬性,分別為VARIABLE和PROCEDURE,代碼及顯示效果如下:
<xml id="toolbox" style="display: none">
<category name="變量" custom="VARIABLE"></category>
<category name="函數" custom="PROCEDURE"></category>
</xml>
其中變量分組中點擊創建變量并按照提示輸入變量名稱后會生成如下三個代碼塊:
函數分組中包含三個代碼塊,前兩個為方法定義,一個方法帶有返回值,第三個為條件返回的代碼塊,通過這三個代碼塊我們可以編寫新的方法來生成新的代碼塊,如下定義一個inc方法會生成一個新的代碼塊inc,該方法將輸入的x變量增加1并返回,新生成的代碼塊又可以作為代碼塊進行使用:
有些代碼塊希望擁有缺省值,如下代碼生成工具箱效果如下:
<xml id="toolbox" style="display: none">
<block type="logic_boolean"></block>
<block type="math_number">
<field name="NUM">42</field>
</block>
<block type="math_arithmetic">
<field name="OP">ADD</field>
<value name="A">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="B">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
</xml>
其中:
迷宮游戲總共有10個關卡,隨著關卡等級的提高,積木會變多,難度也有相應的增加。游戲目的:了解方向積木、循環積木、以及判斷積木的使用;游戲等級:1-10級;運行程序:單機可以執行積木腳本,積木腳本將控制企鵝運動,直到腳本執行完成或找到地圖圖標標志;方向積木:向左轉、向右轉;循環積木:重復執行直到,for或while循環;判斷積木:如果前方可以通行,if or ifelse;
旋轉方向積木
Blockly.Blocks['maze_turn']={
init: function() {
//控制積木左轉、右轉
var DIRECTIONS=[[BlocklyGames.getMsg('Maze_turnLeft'), 'turnLeft'],
[BlocklyGames.getMsg('Maze_turnRight'), 'turnRight']];
// Append arrows to direction messages.
DIRECTIONS[0][0] +=Maze.Blocks.LEFT_TURN;
DIRECTIONS[1][0] +=Maze.Blocks.RIGHT_TURN;
this.setColour(Maze.Blocks.MOVEMENT_HUE);
//添加元素下拉列表
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown(DIRECTIONS), 'DIR');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(BlocklyGames.getMsg('Maze_turnTooltip'));
}
};
Blockly.JavaScript['maze_turn']=function(block) {
// 獲得下拉列表值
var dir=block.getFieldValue('DIR');
return dir + '(\'block_id_' + block.id + '\');\n';
};
循環積木
Blockly.Blocks['maze_forever']={
init: function() {
this.setColour(Maze.Blocks.LOOPS_HUE);
//添加圖標
this.appendDummyInput()
.appendField(BlocklyGames.getMsg('Maze_repeatUntil'))
.appendField(new Blockly.FieldImage(Maze.SKIN.marker, 12, 16));
//添加執行語句
this.appendStatementInput('DO')
.appendField(BlocklyGames.getMsg('Maze_doCode'));
this.setPreviousStatement(true);
this.setTooltip(BlocklyGames.getMsg('Maze_whileTooltip'));
}
};
Blockly.JavaScript['maze_forever']=function(block) {
// Generate JavaScript for repeat loop.
var branch=Blockly.JavaScript.statementToCode(block, 'DO');
if (Blockly.JavaScript.INFINITE_LOOP_TRAP) {
branch=Blockly.JavaScript.INFINITE_LOOP_TRAP.replace(/%1/g,
'\'block_id_' + block.id + '\'') + branch;
}
//循環執行branch直到碰到地圖圖標
return 'while (notDone()) {\n' + branch + '}\n';
};
判斷積木
Blockly.Blocks['maze_if']={
//判斷積木是否可以前進、左轉、右轉
init: function() {
var DIRECTIONS=[[BlocklyGames.getMsg('Maze_pathAhead'), 'isPathForward'],
[BlocklyGames.getMsg('Maze_pathLeft'), 'isPathLeft'],
[BlocklyGames.getMsg('Maze_pathRight'), 'isPathRight']];
// Append arrows to direction messages.
DIRECTIONS[1][0] +=Maze.Blocks.LEFT_TURN;
DIRECTIONS[2][0] +=Maze.Blocks.RIGHT_TURN;
this.setColour(Maze.Blocks.LOGIC_HUE);
//添加元素下拉列表
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown(DIRECTIONS), 'DIR');
//判斷執行代碼
this.appendStatementInput('DO')
.appendField(BlocklyGames.getMsg('Maze_doCode'));
this.setTooltip(BlocklyGames.getMsg('Maze_ifTooltip'));
this.setPreviousStatement(true);
this.setNextStatement(true);
}
};
Blockly.JavaScript['maze_if']=function(block) {
// 獲取下拉列表if中的條件代碼
var argument=block.getFieldValue('DIR') +
'(\'block_id_' + block.id + '\')';
var branch=Blockly.JavaScript.statementToCode(block, 'DO');
var code='if (' + argument + ') {\n' + branch + '}\n';
return code;
};
企鵝迷宮對象:Maze.map,隨著等級的變化map中的路線圖也會變化地圖圖標位置:Maze.finish[y, x],用于判斷企鵝是否到達finsih點企鵝當前位置:[pegmanY, pegmanX]企鵝方向:NORTH: 0:[pegmanY-1, pegmanX],EAST: 1:[pegmanY, pegmanX+1],SOUTH: 2:[pegmanY+1, pegmanX],WEST: 3:[pegmanY-1, pegmanX+1]
// 添加地圖圖標
var finishMarker=Blockly.utils.dom.createSvgElement('image', {
'id': 'finish',
'height': 34,
'width': 20
}, svg);
finishMarker.setAttributeNS(Blockly.utils.dom.XLINK_NS, 'xlink:href',
Maze.SKIN.marker);
// 添加企鵝圖標
var pegmanIcon=Blockly.utils.dom.createSvgElement('image', {
'id': 'pegman',
'height': Maze.PEGMAN_HEIGHT,
'width': Maze.PEGMAN_WIDTH * 21, // 49 * 21=1029
'clip-path': 'url(#pegmanClipPath)'
}, svg);
pegmanIcon.setAttributeNS(Blockly.utils.dom.XLINK_NS, 'xlink:href',
Maze.SKIN.sprite);
企鵝移動企鵝移動對象為[command, id],comand為東南西北四個方向,id為積木的id。
Maze.move=function(direction, id) {
//判斷企鵝某個方向是否可以移動
if (!Maze.isPath(direction, null)) {
Maze.log.push(['fail_' + (direction ? 'backward' : 'forward'), id]);
throw false;
}
var effectiveDirection=Maze.pegmanD + direction;
var command;
//構造企鵝移動方向對象
switch (Maze.constrainDirection4(effectiveDirection)) {
case Maze.DirectionType.NORTH:
Maze.pegmanY--;
command='north';
break;
case Maze.DirectionType.EAST:
Maze.pegmanX++;
command='east';
break;
case Maze.DirectionType.SOUTH:
Maze.pegmanY++;
command='south';
break;
case Maze.DirectionType.WEST:
Maze.pegmanX--;
command='west';
break;
}
Maze.log.push([command, id]);
};
企鵝轉動方向
Maze.turn=function(direction, id) {
if (direction) {
// Right turn (clockwise).
Maze.pegmanD++;
Maze.log.push(['right', id]);
} else {
// Left turn (counterclockwise).
Maze.pegmanD--;
Maze.log.push(['left', id]);
}
Maze.pegmanD=Maze.constrainDirection4(Maze.pegmanD);
};
企鵝某個方向是否可移動
Maze.isPath=function(direction, id) {
var effectiveDirection=Maze.pegmanD + direction;
var square;
var command;
switch (Maze.constrainDirection4(effectiveDirection)) {
case Maze.DirectionType.NORTH:
square=Maze.map[Maze.pegmanY - 1] &&
Maze.map[Maze.pegmanY - 1][Maze.pegmanX];
command='look_north';
break;
case Maze.DirectionType.EAST:
square=Maze.map[Maze.pegmanY][Maze.pegmanX + 1];
command='look_east';
break;
case Maze.DirectionType.SOUTH:
square=Maze.map[Maze.pegmanY + 1] &&
Maze.map[Maze.pegmanY + 1][Maze.pegmanX];
command='look_south';
break;
case Maze.DirectionType.WEST:
square=Maze.map[Maze.pegmanY][Maze.pegmanX - 1];
command='look_west';
break;
}
if (id) {
Maze.log.push([command, id]);
}
return square !==Maze.SquareType.WALL && square !==undefined;
};
Maze.initInterpreter=function(interpreter, scope) {
var wrapper;
moveForward=function(id) {
Maze.move(0, id);//前進
};
moveBackward=function(id) {
Maze.move(2, id);//后退
};
turnLeft=function(id) {
Maze.turn(0, id);//左轉
};
turnRight=function(id) {
Maze.turn(1, id);//右轉
};
isPathForward=function(id) {
return Maze.isPath(0, id);//是否可前進
};
isPathRight=function(id) {
return Maze.isPath(1, id);
};
isPathBackward=function(id) {
return Maze.isPath(2, id);
};
isPathLeft=function(id) {
return Maze.isPath(3, id);
};
notDone=function() {//游戲是否結束
return Maze.notDone();
};
};
迷宮游戲某個等級,腳本區只能使用固定數量的積木,此函數提示腳本區剩余積木數量。
lockly Games 是一系列編程教育小游戲。搜索“少兒編程教程網”就可以找到“Blockly游戲”(https://blockly-games.kidscoding8.com/blockly-games/zh-hans/index.html?lang=zh-hans)。
“鳥”這個關卡在迷宮關卡的編程知識基礎上學習運用關系表達式來控制鳥的飛行方向,讓鳥合理規劃線路吃到蟲子后回到自己的巢。
前6關是基本塊的訓練。通過對角度、“沒有蠕蟲”塊、XY坐標位置塊、關系表達式塊的訓練掌握怎樣用條件判斷和邏輯控制鳥的復雜飛行。
每次過關后還會將你編寫的圖形化代碼轉換為對應的JavaScript代碼,讓你對JavaScript有一個初步感性的認識。
從第7關開始路線開始變得復雜了。在處理多個判斷條件時需要用到不同的關系表達式——如果if、否則如果else if、否則else。
如果if(條件1):
如果條件1為真,執行這里(條件為真才執行);
否則如果else if(條件2):
否則,當條件2為真執行這里(當條件1不為真,條件2為真執行這里);
否則else:
條件1,條件2都不為真,執行這里。
第7關代碼
第8關出現了新的積木塊“和”,可以連接多個需要同時滿足的條件。
第8關路線
第8關代碼
第9關路線
第9關代碼
作為最后一關,難度自然比較高,判斷條件也更加復雜,需要用兩個“和”方塊完成3個條件的判斷。
第10關路線
第10關代碼
完成“鳥”關卡后,相信你對如果(if)、否則如果(else if)、否則(else)這幾個判斷已經有了一個更深刻的認識。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。