Blocklyのカスタムブロックを作ってみる

BlocklyではBlockly Developer Toolsでカスタムブロックを作る事ができます。

今回は様々なプログラミング言語で実装されているsleep関数(数秒待つ)のカスタムブロックを作成してみましょう。


最初に



一秒待つという簡単な処理のブロックを作成してみます。



Blockly Developer Toolsにアクセスしてみます。

画面左にすでにブロックが組み立てられていますが、説明は無しにして手順のみを書いていきます。



最初に左メニュのInputからdummy inputブロックを右側のブロックのinputsに移動します。



続いて、左メニュのFieldからtext[]のブロックを先程のdummy inputのfieldsに移動します。



nameをsleepに変え、先程挿入したtext[]ブロックには1秒待つという文字列を入力します。

これで1秒待つのブロックができました。




作成したカスタムブロックをBlocklyでブロックを日本語表記にしてみるまでで作成したコードに挿入してみます。


下記のJavaScriptのファイルを作成します。

~/workspace/blockly/custom.js

続いて、~/workspace/blockly/index.htmlのheadタグ内の

<script src="https://cdnjs.cloudflare.com/ajax/libs/blockly/10.4.1/blockly.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blockly/10.4.1/blocks_compressed.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blockly/10.4.1/msg/ja.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blockly/10.4.1/blockly.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blockly/10.4.1/blocks_compressed.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blockly/10.4.1/msg/ja.min.js"></script>
<script src="/custom.js"></script>

に変更します。



Blockly Developer Toolsの右側のBlock DefinitionとGenerator stubの右側にありますセレクトボックスをJavaScriptにして、各々の生成されたコードを

~/workspace/blockly/custom.js

にコピーペーストします。

※本来であれば、Block DefinitionとGenerator stubのコードは別ファイルで管理した方が良いですが、今回は一つのファイルで管理します。


一応、~/workspace/blockly/custom.jsのコードを載せておきます。


Blockly.Blocks['sleep'] = {
	init: function() {
		this.appendDummyInput()
		  .appendField("一秒待つ");
		this.setColour(230);
		this.setTooltip("");
		this.setHelpUrl("");
	}
};

javascript.javascriptGenerator.forBlock['sleep'] = function(block, generator) {
	// TODO: Assemble javascript into code variable.
	var code = '...\n';
	return code;
};



次にBlocklyを試してみようで作成しました

const toolbox = {
	"kind": "flyoutToolbox",
	"contents": [
		{
			"kind": "block",
			"type": "text"
		},
		{
			"kind": "block",
			"type": "text_print"
		},
	]
}

のどのブロックを設置するか?のコードに

const toolbox = {
	"kind": "flyoutToolbox",
	"contents": [
		{
			"kind": "block",
			"type": "sleep"
		},
		{
			"kind": "block",
			"type": "text"
		},
		{
			"kind": "block",
			"type": "text_print"
		},
	]
}

のように今回作成したブロックを指定します。

※記事の文量の都合で、他のブロックの記述は省略しています。


サーバを起動して、ページを表示してみると、



一秒待つブロックが追加されました。

現時点でのブロックでは、



if文やfor文のようなネスト構造を持つブロックに組み込むことができないので、Blockly Developer Toolsでカスタムブロックを改修します。



赤枠で囲った箇所を↕ top+bottom connectionsにして、Block DefinitionとGenerator stubで出力された値を

~/workspace/blockly/custom.js

に上書きします。


Blockly.Blocks['sleep'] = {
	init: function() {
		this.appendDummyInput()
		  .appendField("一秒待つ");
		this.setPreviousStatement(true, null);
		this.setNextStatement(true, null);
		this.setColour(230);
		this.setTooltip("");
		this.setHelpUrl("");
	}
};

javascript.javascriptGenerator.forBlock['slee'] = function(block, generator) {
	// TODO: Assemble javascript into code variable.
	var code = '...\n';
	return code;
};

再びサーバを起動して表示を確認してみると、



一秒待つブロックの上下に突起が出来、



if文やfor文のネスト内で1秒待つが使用できるようになりました。




最後に今回作成しました一秒待つブロックにJavaScriptのコードを追加します。


~/workspace/blockly/custom.js

javascript.javascriptGenerator.forBlock['sleep'] = function(block, generator) {
	// TODO: Assemble javascript into code variable.
	var code = '...\n';
	return code;
};

javascript.javascriptGenerator.forBlock['sleep'] = function(block, generator) {
	// TODO: Assemble javascript into code variable.
	var code = '(function(ms) {var start = Date.now();while(Date.now() - start < ms){}})(1000);\n';
	return code;
};

にします。


これで1秒待つブロックができました。




続いて、



秒数指定できるブロックを作成してみます。


Blockly Developer Toolsで下記のようにブロックを改修します。




新しく追加しました上のブロックでは、数字のブロックを差し込める箇所を追加することができるようになり、SECが引数になります。

引数はBlocklyで初期値付きのブロックを設置するで触れました初期値設定やJavaScriptのコードの作成時に使用します。




~/workspace/blockly/custom.js

を下記のように変更します。


Blockly.Blocks['sleep'] = {
	init: function() {
		this.appendDummyInput()
		  .appendField("一秒待つ");
		this.setPreviousStatement(true, null);
		this.setNextStatement(true, null);
		this.setColour(230);
		this.setTooltip("");
		this.setHelpUrl("");
	}
};

Blockly.Blocks['sleep'] = {
	init: function() {
		this.appendValueInput("SEC")
		  .setCheck("Number")
		  .appendField(new Blockly.FieldLabelSerializable(""), "SEC");
		this.appendDummyInput()
		  .appendField("秒待つ");
		this.setPreviousStatement(true, null);
		this.setNextStatement(true, null);
		this.setColour(230);
		this.setTooltip("");
		this.setHelpUrl("");
	}
};

に変更し、


javascript.javascriptGenerator.forBlock['sleep'] = function(block, generator) {
	// TODO: Assemble javascript into code variable.
	var code = '(function(ms) {var start = Date.now();while(Date.now() - start < ms){}})(1000);\n';
	return code;
};

javascript.javascriptGenerator.forBlock['sleep'] = function(block, generator) {
	// TODO: Assemble javascript into code variable.
	var value_sec = generator.valueToCode(block, 'SEC', javascript.Order.ATOMIC);
	var code = '(function(ms) {var start = Date.now();while(Date.now() - start < ms){}})('+(parseInt(value_sec)*1000)+');\n';
	return code;
};

に変更します。




最後に



初期値でmath_number付きのブロックを作成してみます。

どのブロックを設置するか?のJavaScriptのコードで


const toolbox = {
	"kind": "flyoutToolbox",
	"contents": [
		{
			"kind": "block",
			"type": "sleep"
		},
		// 他のブロックの記述は省略
	]
}

const toolbox = {
	"kind": "flyoutToolbox",
	"contents": [
		{
			"kind": "block",
			"type": "sleep",
			"inputs": {
				"SEC": {
					"block": {
						"type": "math_number",
						"fields": {
							"NUM": 1
						}
					}
				}
			}
		},
		// 他のブロックの記述は省略
	]
}

に変更します。

マインクラフト用ビジュアルエディタを開発しています。
詳しくはinunosinsi/mcws_blockly - githubをご覧ください。