提供: Japanese Scratch-Wiki

< Scratch 3.0の拡張機能を作ってみよう

このきじは ひらがなのページがありません。ごめんなさい。
SandCastleIcon.png このページには、ScratchのWebサイトまたはWikipedia,Scratch Wiki以外へのリンクがあります。 他のサイトの安全を保証することはできないため、インターネットを使用する際は常に安全を忘れないようにしてください。

拡張機能のコードを書いてみよう。その前に、またお絵かきだ。

お絵かき

Scratchのペイントエディターを使って、ベクターモードにして、40x40の正方形を描く。この枠に絵を入れる。これはブロックのアイコンに使われる。描けて、正方形からはみ出ていないことを確認したら、グループ化してsvg形式で保存する。

保存した後は、ここなどでbase64形式に変換する。これは後で使う。

拡張機能の追加

scratch-vm\src\extensionsに、新しくフォルダを作る。ここでは「scratch3_newblocks」にする。その中に、index.jsを作る。まずは、以下をコピペしてみると良い。

 1 const ArgumentType = require('../../extension-support/argument-type');
 2 const BlockType = require('../../extension-support/block-type');
 3 const Cast = require('../../util/cast');
 4 const log = require('../../util/log');
 5 
 6 /**
 7  * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 8  * @type {string}
 9  */
10 // eslint-disable-next-line max-len
11 const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZyBpZD0iSUQwLjA4NjgyNDQzOTAwMDMzODMyIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjQ5MTU0NjY2MDY2MTY5NzQsIDAsIDAsIDAuNDkxNTQ2NjYwNjYxNjk3NCwgLTY0LjUsIC03Ny4yNSkiPjxwYXRoIGlkPSJJRDAuNTcyMTQ2MjMwMzc3MjU2OSIgZmlsbD0iI0ZGOTQwMCIgc3Ryb2tlPSJub25lIiBkPSJNIDE4OCAxNDEgTCAyNTAgMTQxIEwgMjUwIDIwMyBMIDE4OCAyMDMgTCAxODggMTQxIFogIiB0cmFuc2Zvcm09Im1hdHJpeCgxLjI4NzkwMzMwODg2ODQwODIsIDAsIDAsIDEuMjg3OTAzMzA4ODY4NDA4MiwgLTExMC45LCAtMjQuNCkiLz48cGF0aCBpZD0iSUQwLjYzODMzNjEzNTA3NDQ5NjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTk2IDIwNCBDIDE5NiAyMDQgMTkyLjcwNiAxOTAuMDU4IDE5MyAxODMgQyAxOTMuMDc0IDE4MS4yMzYgMTk1Ljg4NiAxNzguNDU4IDE5NyAxODAgQyAyMDEuNDU1IDE4Ni4xNjggMjAzLjQ0MyAyMDMuNzU0IDIwNiAyMDEgQyAyMDkuMjExIDE5Ny41NDIgMjEwIDE2NiAyMTAgMTY2ICIgdHJhbnNmb3JtPSJtYXRyaXgoMSwgMCwgMCwgMSwgLTU3LCAxNS44KSIvPjxwYXRoIGlkPSJJRDAuNzU4NzMwMzU2NTgxNTA5MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgZD0iTSAyMTUgMTY5IEMgMjE1IDE2OSAyMTguMzY3IDE2OS41MzQgMjIwIDE3MCBDIDIyMC43MTYgMTcwLjIwNSAyMjEuMjc4IDE3MC44MTkgMjIyIDE3MSBDIDIyMi42NDYgMTcxLjE2MiAyMjMuMzY4IDE3MC43ODkgMjI0IDE3MSBDIDIyNC40NDcgMTcxLjE0OSAyMjUgMTcyIDIyNSAxNzIgIiB0cmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAxLCAtNTcsIDE1LjgpIi8+PHBhdGggaWQ9IklEMC4yNDM2NzMwNzMxMjc4NjU4IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBkPSJNIDIyNyAxNTQgQyAyMjcgMTU0IDIxOC41NTUgMTQ3Ljg5MCAyMTcgMTUxIEMgMjEyLjM0NSAxNjAuMzEwIDIxMS4yODkgMTcxLjczMyAyMTMgMTgyIEMgMjEzLjYxMiAxODUuNjcyIDIyMyAxODcgMjIzIDE4NyAiIHRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIDEsIC01NywgMTUuOCkiLz48cGF0aCBpZD0iSUQwLjc5MzkzOTQ4MTk1NTAyMTYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTc1IDIwMC41MDAgQyAxNzUgMjAwLjUwMCAxNjkuODA1IDIyMS45MTMgMTcxIDIyMi43NTAgQyAxNzIuMTk1IDIyMy41ODcgMTc4Ljc5NSAyMDUuMjk1IDE4Mi41MDAgMjA1Ljc1MCBDIDE4NS45MjAgMjA2LjE3MCAxODEuODU5IDIyNC41MDAgMTg1LjI1MCAyMjQuNTAwIEMgMTg5LjIxMyAyMjQuNTAwIDE5Ny4yNTAgMjA1Ljc1MCAxOTcuMjUwIDIwNS43NTAgIi8+PC9nPjwvc3ZnPg==';
12 
13 /**
14  * Icon svg to be displayed in the category menu, encoded as a data URI.
15  * @type {string}
16  */
17 // eslint-disable-next-line max-len
18 const menuIconURI = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZyBpZD0iSUQwLjA4NjgyNDQzOTAwMDMzODMyIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjQ5MTU0NjY2MDY2MTY5NzQsIDAsIDAsIDAuNDkxNTQ2NjYwNjYxNjk3NCwgLTY0LjUsIC03Ny4yNSkiPjxwYXRoIGlkPSJJRDAuNTcyMTQ2MjMwMzc3MjU2OSIgZmlsbD0iI0ZGOTQwMCIgc3Ryb2tlPSJub25lIiBkPSJNIDE4OCAxNDEgTCAyNTAgMTQxIEwgMjUwIDIwMyBMIDE4OCAyMDMgTCAxODggMTQxIFogIiB0cmFuc2Zvcm09Im1hdHJpeCgxLjI4NzkwMzMwODg2ODQwODIsIDAsIDAsIDEuMjg3OTAzMzA4ODY4NDA4MiwgLTExMC45LCAtMjQuNCkiLz48cGF0aCBpZD0iSUQwLjYzODMzNjEzNTA3NDQ5NjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTk2IDIwNCBDIDE5NiAyMDQgMTkyLjcwNiAxOTAuMDU4IDE5MyAxODMgQyAxOTMuMDc0IDE4MS4yMzYgMTk1Ljg4NiAxNzguNDU4IDE5NyAxODAgQyAyMDEuNDU1IDE4Ni4xNjggMjAzLjQ0MyAyMDMuNzU0IDIwNiAyMDEgQyAyMDkuMjExIDE5Ny41NDIgMjEwIDE2NiAyMTAgMTY2ICIgdHJhbnNmb3JtPSJtYXRyaXgoMSwgMCwgMCwgMSwgLTU3LCAxNS44KSIvPjxwYXRoIGlkPSJJRDAuNzU4NzMwMzU2NTgxNTA5MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgZD0iTSAyMTUgMTY5IEMgMjE1IDE2OSAyMTguMzY3IDE2OS41MzQgMjIwIDE3MCBDIDIyMC43MTYgMTcwLjIwNSAyMjEuMjc4IDE3MC44MTkgMjIyIDE3MSBDIDIyMi42NDYgMTcxLjE2MiAyMjMuMzY4IDE3MC43ODkgMjI0IDE3MSBDIDIyNC40NDcgMTcxLjE0OSAyMjUgMTcyIDIyNSAxNzIgIiB0cmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAxLCAtNTcsIDE1LjgpIi8+PHBhdGggaWQ9IklEMC4yNDM2NzMwNzMxMjc4NjU4IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBkPSJNIDIyNyAxNTQgQyAyMjcgMTU0IDIxOC41NTUgMTQ3Ljg5MCAyMTcgMTUxIEMgMjEyLjM0NSAxNjAuMzEwIDIxMS4yODkgMTcxLjczMyAyMTMgMTgyIEMgMjEzLjYxMiAxODUuNjcyIDIyMyAxODcgMjIzIDE4NyAiIHRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIDEsIC01NywgMTUuOCkiLz48cGF0aCBpZD0iSUQwLjc5MzkzOTQ4MTk1NTAyMTYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTc1IDIwMC41MDAgQyAxNzUgMjAwLjUwMCAxNjkuODA1IDIyMS45MTMgMTcxIDIyMi43NTAgQyAxNzIuMTk1IDIyMy41ODcgMTc4Ljc5NSAyMDUuMjk1IDE4Mi41MDAgMjA1Ljc1MCBDIDE4NS45MjAgMjA2LjE3MCAxODEuODU5IDIyNC41MDAgMTg1LjI1MCAyMjQuNTAwIEMgMTg5LjIxMyAyMjQuNTAwIDE5Ny4yNTAgMjA1Ljc1MCAxOTcuMjUwIDIwNS43NTAgIi8+PC9nPjwvc3ZnPg==';
19 
20 
21 /**
22  * Class for the new blocks in Scratch 3.0
23  * @param {Runtime} runtime - the runtime instantiating this block package.
24  * @constructor
25  */
26 class Scratch3NewBlocks {
27     constructor (runtime) {
28         /**
29          * The runtime instantiating this block package.
30          * @type {Runtime}
31          */
32         this.runtime = runtime;
33 
34         //this._onTargetCreated = this._onTargetCreated.bind(this);
35         //this.runtime.on('targetWasCreated', this._onTargetCreated);
36     }
37 
38 
39     /**
40      * @returns {object} metadata for this extension and its blocks.
41      */
42     getInfo () {
43         return {
44             id: 'newblocks',
45             name: 'New Blocks',
46             menuIconURI: menuIconURI,
47             blockIconURI: blockIconURI,
48             blocks: [
49                 {
50                     opcode: 'writeLog',
51                     blockType: BlockType.COMMAND,
52                     text: 'log [TEXT]',
53                     arguments: {
54                         TEXT: {
55                             type: ArgumentType.STRING,
56                             defaultValue: "hello"
57                         }
58                     }
59                 },
60                 {
61                     opcode: 'getBrowser',
62                     text: 'browser',
63                     blockType: BlockType.REPORTER
64                 }
65             ],
66             menus: {
67             }
68         };
69     }
70 
71     /**
72      * Write log.
73      * @param {object} args - the block arguments.
74      * @property {number} TEXT - the text.
75      */
76     writeLog (args) {
77         const text = Cast.toString(args.TEXT);
78         log.log(text);
79     }
80 
81     /**
82      * Get the browser.
83      * @return {number} - the user agent.
84      */
85     getBrowser () {
86         return navigator.userAgent;
87     }
88 }
89 
90 module.exports = Scratch3NewBlocks;

お絵かきの結果(base64)は11行目、18行目の''内を置き換えておこう。

解説

blockIconURIには、ブロックの横の画像、menuIconURIには、ブロックカテゴリでの画像が、base64エンコードされたdata urlの形で格納される。実装などはclass Scratch3NewBlocksの中にある。

getInfo ()での引数は以下のとおり:

  • id: GUI側の設定で指定したextensionId。
  • name: 拡張機能の名前。
  • blocks: ブロックの配列。入れた順に表示される。
    • opcode: 保存時にJSONに書き込まれるオペコード。既存のものとかぶらないように注意が必要。
    • blockType: ブロックの種類。
    • text: ブロックの名前。[と]の間に英数字を入れると引数になる。
    • arguments: 引数。
      • キー(ここでの例:TEXT)は、[ と ]に入れた文字列。
      • type: 引数の種類。
        • ArgumentType.BOOLEAN(真偽ブロック)
        • ArgumentType.NUMBER(数値入力)
        • ArgumentType.STRING(文字列入力)
      • defaultValue: 初期値。
  • menus: メニュー。今は空。

その後に、以下のような形で定義を書く。

writeLog (args) {
    const text = Cast.toString(args.TEXT);
    log.log(text);
}
getBrowser () {
    return navigator.userAgent;
}

オペコード ()から始まる。引数がある場合はargs引数のプロパティとして、args.TEXTのように表記すると取得できる。Castはキャストの規則の実装。toStringは文字列化だが、ここをtoNumberにすると数値化できる。

値/真偽ブロックの場合はreturnで値を返却し、それ以外はreturnは不要。

最終段階: VMの拡張機能リストに追加

src\extension-support\extension-manager.jsを開き、以下の行を20行目付近に追加する。

newblocks: () => require('../extensions/scratch3_newblocks'),

前の項目の最後にカンマがなければ、つける。

これで終了した。

テスト

scratch-guiに戻り、以下のコマンドを実行する。

$ npm start

その後、localhost:8601にアクセスする。しばらく待つとScratchのエディターが現れる。(もし白い画面がずっと続いていたら、コードにミスがあってビルドできていない。)成功すれば、拡張機能ライブラリーにNew Blocksが追加される。

コマンドの画面を閉じるか、CtrlCで停止できる。

以下のブロックが追加された。

  • log [hello]::pen - ログを出力するブロック。ログはF12のコンソールに出力される。
  • (browser::pen) - ユーザーエージェントを返すブロック。