Phaser3 :スプライトに自動生成した物理ボディをつけてみる

前回スプライトからメッシュを自動で生成しましたが、スプライトに物理ボディをつける実装をやっていなかったので、今回紹介したいと思います。

matterで物理ボディ付きのスプライトを作成する

matter.add.spriteのオプションでカスタム物理ボディを追加する

以前の記事で作成したCreateVertsを改造し、作成したvertsを連想配列に保存し、verts作成済のテクスチャデータが指定された場合にそこから呼び出すようにします。

public verts: { [key: string]: Phaser.Math.Vector2[] } = {};
~~
public CreateVerts(x: number, y: number, texture: string) {
    this.points = [];
    this.closepath = [];
    this.border = [];

    //phaserのテクスチャマネージャからsourceimageを取得する
    let source = this.textures.get(texture).getSourceImage();;

    if (this.verts[texture] === undefined) {

        //canvasを作成しテクスチャを描画
        this.canvas = this.textures.createCanvas(texture + "_canvas", source.width, source.height);

        //canvasをクリア
        this.canvas.clear()
        this.canvas.draw(0, 0, source as HTMLImageElement);

        //imagedataを取得
        this.imageData = this.canvas.imageData;
        this.pixels = new Array();
        this.pixelsCopy = new Array();
        //ピクセルを配列に入れる
        for (let i = 0; i < this.canvas.imageData.data.length; i += 4) {
            let p = [this.canvas.imageData.data[i], this.canvas.imageData.data[i + 1], this.canvas.imageData.data[i + 2], this.canvas.imageData.data[i + 3]]
            this.pixels.push(p);
        }

        //ピクセルを配列にいれなおす
        for (let i = 0; i < this.imageData.height; i++) {
            this.pixelsCopy[i] = new Array();
            for (let j = 0; j < this.imageData.width; j++) {
                this.pixelsCopy[i].push(0)
            }
        }

        let xx = 0;
        let yy = 0;

        //アルファ値の取得
        for (let i = 0; i < this.pixels.length; i++) {

            this.pixelsCopy[yy][xx] = this.pixels[i][3]
            xx++;

            if (xx === source.width) {
                xx = 0;
                yy++;
            }
        }

        let origin = 0;
        let tmp = new Phaser.Math.Vector2(0);

        while (this.pixels[origin][3] === 0) {
            origin++;
        }

        let mov = new Phaser.Math.Vector2(0, 0);
        tmp = this.GetBoarder(new Phaser.Math.Vector2(origin, 0), mov);

        this.border.push(new Phaser.Math.Vector2(origin, 0))

        let prevmov = new Phaser.Math.Vector2(mov.x, mov.y);

        while (!(tmp.x === origin && tmp.y === 0)) {
            tmp = this.GetBoarder(tmp, mov);
            if (!(prevmov.x === mov.x && prevmov.y === mov.y)) {
                this.border.push(new Phaser.Math.Vector2(tmp.x, tmp.y));
            }
            prevmov.set(mov.x, mov.y);
        }

        //取得した輪郭からverticesを作る
        let verts = ""
        for (let p of this.border) {
            verts += (p.x + x) + " " + (p.y + y) + " "
        }


        var poly = this.add.polygon(x, y, verts, 0x0000ff, 0.2);
        this.matter.add.sprite(x, y, texture, 0, { shape: { type: 'fromVerts', verts: verts, flagInternal: false } }).setTint(0x05FBFF, 0x1E00FF0);
        this.verts[texture] = this.border;

    } else {
        let verts = ""

        for (let p of this.verts[texture]) {
            verts += (p.x + x) + " " + (p.y + y) + " "
        }

        this.matter.add.sprite(x, y, texture, 0, { shape: { type: 'fromVerts', verts: verts, flagInternal: false } }).setTint(0x05FBFF, 0x1E00FF0);
                
    }

上記処理内のthis.matter.add.spriteで物理ボディ付きのスプライトを作成しています。
第五引数でオプションを指定し、typeにfromvertsを選ぶことで用意した頂点配列を使用することが可能です。
オプションのvertsで使用する頂点配列を指定します。

this.matter.add.sprite(x, y, texture, 0, { shape: { type: 'fromVerts', verts: verts, flagInternal: false } }).setTint(0x05FBFF, 0x1E00FF0);

画面クリックでランダムな位置に物理ボディを作成する処理を追加しました。

update() {
    let rand = ["bar","block1","block2","block3","rect"]
    if (this.input.activePointer.isDown) {
        this.CreateVerts(Phaser.Math.Between(0, this.sys.canvas.width), Phaser.Math.Between(0, this.sys.canvas.height/2), rand[Phaser.Math.FloorTo(Phaser.Math.Between(0,rand.length))])

    }
}

上記の実行結果がこちらです。 f:id:Phaser_3:20190123170804p:plain f:id:Phaser_3:20190123170815g:plain 輪郭取得の精度を上げれば落ちものパズルなどにも使えそうです。