0
0

Turbowarpの『カスタム拡張機能』を使おう【4】

Last updated at Posted at 2024-08-01

本記事について

Turbowarpのカスタム拡張機能を作る方法の説明、その4です。

前記事までで、外部Javascriptファイルを読み込む処理、読み込むファイルをブロックで指定できるようにする、ところまでをやりました。

今回は、テスト用Javascriptのなかで『Scratch』操作をさせてみようと思います。

やりたいことの提示

  • ブロックで入力したSTEP数だけ動かす
    ~あらかじめ設定されている向きの方向に動かす
  • 位置情報を言う(ふきだし)
    ~「〇〇と言う」と「〇〇と考える」をプルダウンで選択可能
    ~「〇〇秒の間だけ」の機能もつける

動画解説

turbowarp004.gif

カスタム拡張機能コード

コードGithub

拡張機能コンスタント

Extension.js
/**
 * Turbowarpの『カスタム拡張機能』を使おう【4】
 * 外部JSファイルにて、Scratch標準ブロックを再現させる
 * 1)指定されたSTEP数だけ動かす
 * 2)現在の位置を吹き出し表示する(言う、考える)
 */
((Scratch) => {
    const ExtensionID = 'MYEXTENSION';
    const ExtensionName = '独自拡張練習';
    // 歯車画像URL
    const GEAR_IMAGE_SVG_URI 
        = 'https://amami-harhid.github.io/turbowarpExtensions/assets/gear.svg';
    // テスト用JSファイルの場所 URL
    const TEST_URL 
        = 'http://127.0.0.1:5500/turbowarpExtensions/_04_extension';

拡張機能定義情報(カラー付き)

    const MyExtensionInfo = {
        id : ExtensionID, 
        name : ExtensionName,
        color1 : '#000000', // 背景を黒に( 文字色は白固定なので背景を白にすると文字が読めない )
        color2 : '#ffffff', // ブロックリストの円周の色( 白 )
        color3 : '#0000ff', // ブロックの周囲の線の色( 青 )

color1, color2, color3 はオプション、省略可能。

        blocks : [

image.png

            {
                opcode: 'loadJSFileSetting',
                blockType: Scratch.BlockType.COMMAND,
                text: '[IMG_GEAR]JSファイルを指定する[JSURL]',
                arguments: {
                    IMG_GEAR: {
                        type: Scratch.ArgumentType.IMAGE,       //タイプ
                        dataURI: GEAR_IMAGE_SVG_URI,            //歯車画像のURI
                    },
                    JSURL: {
                        type: Scratch.ArgumentType.STRING,
                        defaultValue: `${TEST_URL}/sub.js`,
                    },
                },
            },

image.png

            {
                opcode: 'setup',
                blockType: Scratch.BlockType.COMMAND,
                text: '[IMG_GEAR]事前準備',
                arguments: {
                    IMG_GEAR: {
                        type: Scratch.ArgumentType.IMAGE,       //タイプ
                        dataURI: GEAR_IMAGE_SVG_URI,            //歯車画像のURI
                    },
                },
            },

image.png

            {
                opcode : 'moveStep',
                blockType : Scratch.BlockType.COMMAND,
                text : '[GEAR_IMAGE] 動かす [STEPS]',
                arguments: {
                    GEAR_IMAGE : {
                        type: Scratch.ArgumentType.IMAGE,
                        dataURI: GEAR_IMAGE_SVG_URI,
                    },
                    STEPS : {
                        type: Scratch.ArgumentType.NUMBER,
                        defaultValue: 10,
                    }
                },
            },

位置を知らせる(プルダウン付き)

image.png

            {
                opcode : 'sayPosition',
                blockType : Scratch.BlockType.COMMAND,
                text : '[GEAR_IMAGE] 位置を知らせる [TYPE]',
                arguments: {
                    GEAR_IMAGE : {
                        type: Scratch.ArgumentType.IMAGE,
                        dataURI: GEAR_IMAGE_SVG_URI,
                    },
                    TYPE : {
                        type: Scratch.ArgumentType.STRING,
                        menu: 'FukidashiMenu',  // menusのキー
                        defaultValue: 'say',
                    },
                },
            },

〇秒、位置を知らせる(プルダウン付き)

image.png

            {
                opcode : 'sayPositionForSec',
                blockType : Scratch.BlockType.COMMAND,
                text : '[GEAR_IMAGE] [SECS]秒、位置を知らせる [TYPE]',
                arguments: {
                    GEAR_IMAGE : {
                        type: Scratch.ArgumentType.IMAGE,
                        dataURI: GEAR_IMAGE_SVG_URI,
                    },
                    SECS : {
                        type: Scratch.ArgumentType.NUMBER,
                        defaultValue: 2,
                    },
                    TYPE : {
                        type: Scratch.ArgumentType.STRING,
                        menu: 'FukidashiMenu',  // menusのキー
                        defaultValue: 'say',
                    },
                },
            },
        ],

プルダウンメニューの定義

        // メニューの定義
        menus: {
            FukidashiMenu: {
                items: [
                    {'text':'話す','value':'say'},
                    {'text':'考える','value':'think'},
                ],
            }
        }
    }

拡張機能のクラス

sayPositionForSecメソッドにはasyncをつけている。終わるまで待つ必要がある(await)ため。

    class MyExtension {
        getInfo() {
            return MyExtensionInfo;
        }
        /**
         * ロードするJSファイルのURLを設定する
         * @param {*} args 
         * @param {*} util 
         */
        loadJSFileSetting( args, util ){
            this.jsUrl = args.JSURL;
        }
        /**
         * JSファイルをロードする
         * @param {*} args 
         * @param {*} util 
         */
        async setup( args, util ){
            console.log(this.jsUrl)
            try{
                const _t = new Date().getTime();
                const sub = await import(`${this.jsUrl}?_t=${_t}`);
                // 読み込むJSは export {TestJS} をしている前提。
                this.testJS = new sub.TestJS(); 
            }catch(e){
                const message = '読み込みに失敗した、'
                    +'もしくはクラス定義が存在しないみたいです';
                console.error( message, e );
                alert(message);
            }
        }
        moveStep( args, util ) {
            console.log( 'moveStep STEPS=', args.STEPS );
            // sub.js 内のメソッドを実行する
            this.testJS.moveStep(args, util);
        }
        sayPosition( args, util ) {
            console.log( 'sayPosition TYPE=', args.TYPE ); // say or think
            // sub.js 内のメソッドを実行する
            this.testJS.sayPosition(args, util);
        }
        async sayPositionForSec( args, util ) {
            console.log( 'sayPosition TYPE=', args.TYPE ); // say or think
            console.log( 'sayPosition SECS=', args.SECS);   // second
            // sub.js 内のメソッドを実行する
            await this.testJS.sayPositionForSec(args, util);
        }
    }
    Scratch.extensions.register(new MyExtension());

})(Scratch);

外部ファイル

コードGITHUB

角度(°) を radianに直す共通処理を書いてます

const MathUtil = class{
    static degToRad (deg) {
        return deg * Math.PI / 180;
    }
}

クラスの定義( TestJS )

const TestJS = class{

向きの方向へ指定したSTEPS分、位置を移動させる処理

Scratch VM のコードを真似しています。

    moveStep(args, util){

        // 向きの方向へ STEPSの長さ分 位置を変える(移動させる)
        const steps = Scratch.Cast.toNumber(args.STEPS);
        const radians = MathUtil.degToRad(90 - util.target.direction);
        const dx = steps * Math.cos(radians);
        const dy = steps * Math.sin(radians);
        util.target.setXY(util.target.x + dx, util.target.y + dy);

    }

話す処理( 言う:say, 考える: think )

    sayPosition(args, util){
        
        const type = args.TYPE;
        const position = {x:util.target.x, y:util.target.y};
        const message = `x=(${position.x}), y=(${position.y})`;
        util.runtime.emit('SAY', util.target, type, message);
    }
    

〇秒間、話す処理( 言う:say, 考える: think )

〇秒間経過した後に ブランクで言い直しています。

    async sayPositionForSec(args, util){

        const type = args.TYPE;
        const position = {x:util.target.x, y:util.target.y};
        const message = `x=(${position.x}), y=(${position.y})`;
        util.runtime.emit('SAY', util.target, type, message);
        const secs = Scratch.Cast.toNumber(args.SECS);
        await this._sleep(secs*1000); // 秒数だけ待つ(次の処理へ進ませない)
        util.runtime.emit('SAY', util.target, type, ''); // ふきだしバブルを消す
    }

〇ミリ秒停止させる処理 ( Promiseを使っています )

    async _sleep(msec){
        return new Promise(resolve => setTimeout(resolve, msec));
    }
}

最後につける

export {TestJS};

続き

次の記事【5】へ続きます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0