0
0

TurbowarpでP5JSを動かす(OneBall自由落下)

Last updated at Posted at 2024-08-07

前記事:TurbowarpでP5JSを動かす』の続きです

目標:TurbowarpでP5JS、クラスをインポート

Sketchファイルからさらに別のJSファイルをインポート、そのJSファイルからもクラス定義をインポートさせたいと思います。Sketchファイルには詳細の処理を書かずに 他のJSファイルにP5JSの詳細コードを書けるようにすることで、コード修正をしやすくします。Sketchファイルは入口みたいな個別に誘導するものになりますね。

ファイルの関係/IMPORTの階層

カスタム拡張機能内で p5JSのSketchをimportします(絶対パスで)
p5JSのSketchからは 詳細処理を書いた sub.js をimportします(相対パスでOK)
sub.js 内でクラス定義を使うので、sub.js から ball.js を importします(相対パスでOK)

image.png

※ 前提:sub.js , ball.js は、sketch.jsと同一フォルダーに格納しておくとします。

今回やりたいことを見せるためのサンプル動画

ソース実装 ( Ball 一個 )

この記事では Ball 一個分を描画しています。複数Ballを描画するのは次の記事をみてください。

ball.js

コードGITHUB

Ballクラスを書いてあるJSファイルです。

ball.js
/*
 *  Ball クラス
 */
const Ball = class{
    constructor(x, y, r, power, c) {
        this._x = x;
        this._y = y;
        this._r = r;
        this._c = c;
        this._directionX = 1;
        this._power = power;
        this._speed = this._power;
        this._counter = 0;
    }
    get c() {
        return this._c;
    }
    get W(){
        return this._W;
    }
    set W(_W){
        this._W = _W;
    }
    get H() {
        return this._H;
    }
    set H(_H){
        this._H = _H;
    }
    // X方向の動き
    moveX () {
        this._x += this._directionX * 10;
        if( (this.x-this._r/2) < -this.W/2 || this.W/2 < (this.x +this._r/2)) {
            this._directionX *= -1;
        }
    }
    // Y方向の動き
    moveY () {
        this._y += this._speed;
        // 径の大きい円は減速が速いとする
        this._speed +=(this._r) / 25;
        if( (this.y+this._r/2) > this.H/2 ) {
            this._speed = this._power;
        }
    };
    // 動きをまとめる
    move () {
        this.moveX();
        this.moveY();
    }
    get x() {
        return this._x - this.W/2;
    }
    get y() {
        return this._y + this.H/2;
    }
    get r() {
        return this._r;
    }
}
export {Ball};

sub.js

コードGITHUB

sub.js
/**
 * sub.js
 * 同じ階層にあるファイルなので、(./~)の書き方でOK。
 * await を忘れてはだめです。
 */
const {Ball} = await import(`./ball.js?t=${new Date().getTime()}`);

let ball;
const mySetup = (p) => {
    console.log('test setup');
    let c = p.color(
        p.random(0,255),
        p.random(0,255),
        p.random(0,255),
    ); 
    ball = new Ball(0, 0, 20, -20, c);
}
const myDraw = (p) => {

    ball.W = p.canvas.clientWidth;
    ball.H = p.canvas.clientHeight;
    const _c = ball.c;
    p.fill(_c);
    p.noStroke();    
    p.ellipse( ball.x, ball.y, ball.r );
    ball.move();

}

export {mySetup, myDraw};

sketch.js

コードGITHUB

sketch.js
/**
 * Ballクラスをインポート
 * 同じ階層にあるファイルなので、(./~)の書き方でOK。
 * await を忘れてはだめです。
 */
const {mySetup, myDraw} = await import(`./sub.js?_t=${new Date().getTime()}`);

/**
 * P5JS Sketch
 * @param {*} p 
 */
const sketch = (p) => {
    let ball;
    p.setup = () => {
        mySetup(p);
    }
    p.draw = () => {
        myDraw(p);
    }
}

Extension.js

カスタム拡張機能の本体です。
コードGITHUB

Extension.js
/**
 * Turbowarpの『カスタム拡張機能』を使おう【7】
 * TurbowarpでP5JSを動かす
 * SketchのURLを指定できるブロックを追加した
 * 
 */
((Scratch) => {
    /** 拡張機能ID */
    const ExtensionID = 'MyExtension0701P5JS';
    /** 拡張機能表示名 */
    const ExtensionName = 'P5JS練習';

    // 歯車画像URL
    const GEAR_IMAGE_SVG_URI 
        = 'https://amami-harhid.github.io/turbowarpExtensions/assets/gear.svg';

    // P5JS CDN URL
    const P5JSLIB 
        = "https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.js";

    // テスト用JSファイルの場所(HOST+DIRCTORY)
    const TEST_URL 
        = 'http://127.0.0.1:5500/_07_01_extension';
    
    // この拡張機能内(Module内)だけで使う前提のグローバル変数
    window._ExtentionGlobals = {};
    window._ExtentionGlobals.TEST_URL = TEST_URL;

    /**
     * 拡張機能定義
     */
    const ExtensionInfo = {
        id: ExtensionID,
        name: ExtensionName,
        blocks: [
            {
                opcode: "p5jsSketchUrl",
                blockType: Scratch.BlockType.COMMAND,
                text: "[IMG_GEAR]P5JS Sketch⇒[SKETCH_URL]",
                arguments: {
                    IMG_GEAR: {
                        type: Scratch.ArgumentType.IMAGE,       //タイプ
                        dataURI: GEAR_IMAGE_SVG_URI,            //歯車画像のURI
                    },
                    SKETCH_URL: {
                        type: Scratch.ArgumentType.STRING,
                        defaultValue: '',
                    }
                },
            },
            {
                opcode: "p5jsImport",
                blockType: Scratch.BlockType.COMMAND,
                text: "[IMG_GEAR]P5JS IMPORT",
                arguments: {
                    IMG_GEAR: {
                        type: Scratch.ArgumentType.IMAGE,       //タイプ
                        dataURI: GEAR_IMAGE_SVG_URI,            //歯車画像のURI
                    },
                },
            },
            {
                opcode: 'p5JsStart',
                blockType: Scratch.BlockType.COMMAND,
                text: "[IMG_GEAR]p5Jsを開始する",
                arguments: {
                    IMG_GEAR: {
                        type: Scratch.ArgumentType.IMAGE,       //タイプ
                        dataURI: GEAR_IMAGE_SVG_URI,            //歯車画像のURI
                    },
                },
            },
            {
                opcode: "p5JsDraw",
                blockType: Scratch.BlockType.COMMAND,
                text: "[IMG_GEAR]P5JS描画をする",
                arguments: {
                    IMG_GEAR: {
                        type: Scratch.ArgumentType.IMAGE,       //タイプ
                        dataURI: GEAR_IMAGE_SVG_URI,            //歯車画像のURI
                    },
                },
            },
        ],
    }
    /**
     * P5JS拡張用クラス(練習)
     */
    class MyExtension {
        getInfo(){
            return ExtensionInfo;
        }
        p5jsSketchUrl(args, util){
            const sketchUrl = args.SKETCH_URL;
            this.sketchUrl = sketchUrl;
            if(sketchUrl.length == 0) {
                this.sketchUrl = `${TEST_URL}/sketch.js`;
            }
        }
        /**
         * P5JSをインポートする
         * @param {*} args 
         * @param {*} util 
         */
        async p5jsImport( args, util ){
            try{
                // ここで P5JS CDN LIB を読み込む(キャッシュOK)
                await import(P5JSLIB);
                // 【P5JS フックの登録】(beforeSetup)
                p5.prototype.registerMethod('beforeSetup', function(){
                    // フック実行時、thisは p5インスタンスである
                    const p = this; 
                    // Sketchにsetupが登録されているときSketchのsetupを上書きする                    
                    if(p.setup){
                        // 【StageのCanvasをP5jsのCanvasとして利用】
                        const _reuseCanvas = () => {
                            // StageのCanvasを取得する
                            const canvas = util.target.renderer.gl.canvas;
                            const w = canvas.clientWidth;
                            const h = canvas.clientHeight;
                            // StageのCanvasをp5jsのCanvasとして使う
                            p.createCanvas(w, h, p.WEBGL, canvas);
                        }
                        // 【Stageサイズ変化監視】
                        const _resizeCanvas = _reuseCanvas;
                        const _stageSizeObserver =()=>{
                            const canvas = util.target.renderer.gl.canvas;
                            // Stageサイズ変化時に resize処理をする
                            const observer = new MutationObserver(() => {
                                _resizeCanvas();
                            });
                            observer.observe(canvas, {
                                attriblutes: true,
                                attributeFilter: ["style"], 
                            });                    
                        };
                        // 【Sketchのsetupを置換】
                        const _sketchSetup = p.setup;
                        const _wraper = () => {
                            // drawの繰返しを抑止する
                            p.noLoop(); 
                            // StageのCanvasをP5jsのCanvasとして利用する
                            _reuseCanvas(); 
                            // Stageサイズ変化を監視する
                            _stageSizeObserver(); 
                            // 元のsetupを実行する
                            _sketchSetup();                            
                        }
                        p.setup = _wraper;
                    }

                });
            }catch(e){
                const mesagge = 'P5JSの読み込みに失敗したみたいです'
                console.error( mesagge, e );
                alert(mesagge);
            }
        }
        /**
         * p5JS setup を開始する
         * @param {*} args 
         * @param {*} util 
         */
        async p5JsStart( args, util ){
            if(this.sketchUrl == undefined) {
                this.sketchUrl = `${TEST_URL}/sketch.js`;
            }
            try{
                // Sketchを読み込む(キャッシュからの読み込みをしない)
                const _t = new Date().getTime();
                const sketchUrl = `${this.sketchUrl}?_t=${_t}`;
                const {sketch} = await import( sketchUrl );
                const p = new p5(sketch);
                this.p = p;

            }catch(e){
                const mesagge = 'Sketchの読み込みに失敗した、'
                    +'もしくはP5JSインスタンスモード開始に失敗した';
                console.error( mesagge, e );
                alert(mesagge);
            }
        }
        /**
         * Scratch3.x(=Turbowarp)のブロックから呼び出されるdraw処理
         * @param {*} args 
         * @param {*} util 
         */
        async p5JsDraw( args, util ) {
            this.p._draw();
        }
    }

    /** カスタム拡張機能クラスのインスタンスを登録する */
    Scratch.extensions.register(new MyExtension());

})(Scratch);

続き

次の記事:TurbowarpでP5JSを動かす(複数のBall:自由落下)』へ続きます

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