Adding C Blocks
This tutorial builds on the dialog blocks added in the Your First Scratch Mod tutorial series.
Simple C Blocks
Let's add this C block:
We will make this block display a confirm prompt repeatedly. Every time the user says Yes, it will run the code within, if the user says No, it will not display the confirm prompt again.
The scratch-blocks
definition
Blockly.Blocks['dialogs_whileconfirmed'] = {
/**
* @this Blockly.Block
*/
init: function(){
this.jsonInit({
"message0": Blockly.Msg.DIALOGS_WHILECONFIRMED, // while confirmed %1
"message1": "%1",
"args0": [
{
"type": "input_value",
"name": "MESSAGE"
}
],
"args1": [
{
"type": "input_statement",
"name": "SUBSTACK"
}
],
"category": Blockly.Categories.dialogs,
"extensions": ["colours_dialogs", "shape_statement"]
});
}
}
You will notice two differences from a regular block:
message1
: The%1
is where the block(s) inside the C block are inserted. Additional uses of this will be explained in the more branches section.args1
: This contains the block input for the branch. A block input is ainput_statement
.
Don't forget to add the block message DIALOGS_WHILECONFIRMED
, which should be while confirmed %1
.
The scratch-gui
toolbox entry
The block input is left out of the toolbox entry, because there aren't any blocks inside by default. You can copy and modify dialogs_alert
for this block.
You can actually insert blocks into the C block by default
If you really want too, you can insert blocks into the C block by default. I'm not sure if there are any practical uses for this, but here is how you can:
<block type="dialogs_whileconfirmed">
<value name="MESSAGE">
<shadow type="text">
<field name="TEXT">${hello}</field>
</shadow>
</value>
<value name="SUBSTACK">
<block type="dialogs_alert">
<value name="MESSAGE">
<shadow type="text">
<field name="TEXT">${hello}</field>
</shadow>
</value>
</block>
</value>
</block>
The scratch-vm
implementation
As usual, add the block to the getPrimitives
function.
This is the code for the block implementation:
whileConfirmedBlock(args, util){
if(confirm(args.MESSAGE)){
util.startBranch(1, true);
}
}
You might notice two important things about this:
- There is no loop in the block.
- We are using the
util.startBranch
function with the arguments1
andtrue
.
Let's start with the usage of the util.startBranch
function. Here is the function's documentation:
/**
* Start a branch in the current block.
* @param {number} branchNum Which branch to step to (i.e., 1, 2).
* @param {boolean} isLoop Whether this block is a loop.
*/
startBranch (branchNum, isLoop) {
// ...
}
The number 1
tells the Scratch VM to start executing the first branch in the block (in this case, the only branch). The true
tells Scratch VM that this block is a loop. Setting isLoop
to true
doesn't make Scratch VM repeatedly execute the branch. It instead tells Scratch VM to re-execute the C block again after the branch's blocks, which can either do nothing and continue to the next block in the stack or re-execute the branch. This is the reason there is no loop in the function itself.
More branches
Let's now make a random choice C block with three branches. Because this block is random, it will be randomly placed in the dialog blocks category.
This is what the block will look like:
The scratch-blocks
definition
Blockly.Blocks['dialogs_randomchoice'] = {
/**
* @this Blockly.Block
*/
init: function(){
this.jsonInit({
"message0": Blockly.Msg.DIALOGS_RANDOMCHOICE,
"message1": "%1",
"message2": "%1",
"message3": "%1",
"args1": [
{
"type": "input_statement",
"name": "SUBSTACK"
}
],
"args2": [
{
"type": "input_statement",
"name": "SUBSTACK2"
}
],
"args3": [
{
"type": "input_statement",
"name": "SUBSTACK3"
}
],
"category": Blockly.Categories.dialogs,
"extensions": ["colours_dialogs", "shape_statement"]
});
}
};
You will notice that there are now some more message#
and args#
, these are the additional branches. You will also notice there is no args0
, this is because there is no input box in this block, so it doesn't need it.
Again, don't forget to add the block message DIALOGS_RANDOMCHOICE
, which should be random choice
.
The scratch-gui
toolbox entry
Unlike our other blocks, this block has no inputs, so we can just use this simple one line entry:
<block type="dialogs_randomchoice" />
The scratch-vm
implementation
Once again, we must add the block to getPrimitives
so Scratch can find our block's implementation.
This will be the implementation of this block:
randomChoice(args, util){
let randomValue = Math.floor(Math.random() * 3 + 1);
util.startBranch(randomValue, false);
}
JavaScript's Math.random()
generates floating point numbers between 0 and 1, however never exactly one. MDN has examples on how to use Math.random()
to get number in the range you want. In this case, the number is 1, 2 or 3.
randomValue
is the branch we want to run and since we only want this block to run once (it isn't a loop), we use false
instead of true
.
Completed Files
Component | File | Download |
---|---|---|
scratch-blocks | blocks_vertical/dialogs.js | dialogs.js |
scratch-blocks | msg/messages.js | messages.js |
scratch-gui | src/lib/make-toolbox-xml.js | make-toolbox-xml.js |
scratch-vm | src/blocks/tutorialmod_dialogs.js | tutorialmod_dialogs.js |
Scratch commit hashes at the time of this tutorial
scratch-gui: db1933b2aea9f9dbe51e0ad2d750a2550179314a
scratch-vm: d6420c4c9826d360adee118e0e9e255536be7f7c
scratch-blocks: 686df65cc6b5b3df37dc3204f56f443aa18c5085