Phaser 3 : 凹面の当たり判定を自動で生成してみる② : テクスチャのアルファ値を取得

前回に引き続き、凹面メッシュの自動生成に挑戦します。 gpnotes.hatenablog.jp


ドロネー図に入る前に、テクスチャのアルファ値を取得できなければ自動生成ができないので、今回はテクスチャからピクセル情報を抽出してみたいと思います。

テクスチャのデータを取得する

こちらのページにピクセルの情報の取得方法が載っていました。

Phaser 3 texture getPixels (plural) - Phaser 3 - HTML5 Game Devs Forum

紹介されている方法はPhaserのテクスチャデータを取得してcanvasに描画、作成したcanvasからピクセルを取得するという手順のようです。

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

//canvasを作成しテクスチャを描画
let canvas = this.textures.createCanvas('canvasName', source.width, source.height);
canvas.draw(0, 0, source as HTMLImageElement);

//imagedataを取得
let imageData = canvas.imageData;

imageData.data内にピクセル情報が r,g,b,aの順に入っています。
元サンプルでは取り出す際にrgbaに当てはめていますが、ここでは後で扱いやすいようにピクセル用の配列を作ります。

let x = 0;
let y = 0;
let color = new Phaser.Display.Color();

let pixels: number[][] = new Array();

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

あとは取得したピクセルの位置にgraphicsで点を作成していきます。
colorオブジェクトを作成しcolor.setToを使うことで色をセットできます。

for (let i = 0; i < pixels.length; i ++) {

    let r = pixels[i][0];
    let g = pixels[i][1];
    let b = pixels[i][2];
    let a = pixels[i][3];

    let posX = this.sys.canvas.width / 2;
    let posY = this.sys.canvas.height / 2;

    if (a > 0) {

        let dx = posX + x ;
        let dy = posY + y ;

        color.setTo(r, g, b, a);

        let pic = this.add.graphics();
        pic.x = dx;
        pic.y = dy;
        pic.fillStyle(color.color, 1);
        pic.fillPointShape(new Phaser.Math.Vector2(0, 0), 1);


    }

    x++;

    if (x === source.width) {
        x = 0;
        y++;
    }
}

実行結果がこちらです。 f:id:Phaser_3:20181221154707p:plain 単に画像を出力しているだけに見えるかもしれませんが、実際にはpointの集合なのでpoint単位で色を変えることもできます。 f:id:Phaser_3:20181221154920p:plain


今回のピクセル取得のソースはPhaser 3 Examplesにあるものと同じようで、こちらのアニメーションもためしに実装してみます。

for (let i = 0; i < pixels.length; i ++) {

    let r = pixels[i][0];
    let g = pixels[i][1];
    let b = pixels[i][2];
    let a = pixels[i][3];

    let posX = this.sys.canvas.width / 2;
    let posY = this.sys.canvas.height / 2;

    if (a > 0) {

        let startX = Phaser.Math.Between(posX , posX+imageData.width/2+imageData.width/2);
        let startY = Phaser.Math.Between(posY + imageData.height / 2 - imageData.height, posY  +imageData.height/2+imageData.height-200);

        let dx = posX + x ;
        let dy = posY + y ;

        color.setTo(r, g, b, a);

        let pic = this.add.graphics();
        pic.x = startX;
        pic.y = startY;
        pic.fillStyle(color.color, 1);
        pic.fillPointShape(new Phaser.Math.Vector2(0, 0), 1);
        pic.setScale(0.1);

        this.tweens.add({

            targets: pic,
            duration: 2000,
            x: dx,
            y: dy,
            scaleX: 1,
            scaleY: 1,
            angle: 360,
            delay: y* 15,
            yoyo: true,
            repeat: -1,
            repeatDelay: 600,
            hold: 600

        });

    }

    x++;

    if (x === source.width) {
        x = 0;
        y++;
    }
}
}

ブロックが粉砕されるようなエフェクトができました。 f:id:Phaser_3:20181221155525g:plain graphicsの集合で作ってしまったので実用的なパフォーマンスではないと思いますが、描画方法を工夫すれば実際のゲームでも使えそうです。
今回のソースはこちらです。

/// <reference path="../app.ts" />

namespace MyGame {

    interface XY {
        x: number;
        y: number;
    }

    export class MyScene1 extends Phaser.Scene {

        //グラフィックオブジェクトを用意
        public player: Phaser.GameObjects.Image;
        public beamGroup: Phaser.GameObjects.Group;

        public graphics: Phaser.GameObjects.Graphics;
        public points: Phaser.Math.Vector2[] = [];
        public closepath: Phaser.Math.Vector2[] = [];

        constructor() {
            super({ key: 'MyScene1', active: false });
        }

        preload() {
            this.load.image("bar", "assets/images/bar.png");
            this.load.image("block", "assets/images/block.png");
        }

        create() {

            this.cameras.main.setBackgroundColor(0xffffff);

            this.graphics = this.add.graphics();
            this.graphics.fillStyle(0x000000);

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

            //canvasを作成しテクスチャを描画
            let canvas = this.textures.createCanvas('canvasName', source.width, source.height);
            canvas.draw(0, 0, source as HTMLImageElement);

            //imagedataを取得
            let imageData = canvas.imageData;

            let x = 0;
            let y = 0;
            let color = new Phaser.Display.Color();

            let pixels: number[][] = new Array();

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

            for (let i = 0; i < pixels.length; i ++) {

                let r = pixels[i][0];
                let g = pixels[i][1];
                let b = pixels[i][2];
                let a = pixels[i][3];

                let posX = this.sys.canvas.width / 2;
                let posY = this.sys.canvas.height / 2;

                if (a > 0) {

                    let startX = Phaser.Math.Between(posX , posX+imageData.width/2+imageData.width/2);
                    let startY = Phaser.Math.Between(posY + imageData.height / 2 - imageData.height, posY  +imageData.height/2+imageData.height-200);

                    let dx = posX + x ;
                    let dy = posY + y ;

                    color.setTo(r, g, b, a);

                    let pic = this.add.graphics();
                    pic.x = startX;
                    pic.y = startY;
                    pic.fillStyle(color.color, 1);
                    pic.fillPointShape(new Phaser.Math.Vector2(0, 0), 1);
                    pic.setScale(0.1);

                    this.tweens.add({

                        targets: pic,
                        duration: 2000,
                        x: dx,
                        y: dy,
                        scaleX: 1,
                        scaleY: 1,
                        angle: 360,
                        delay: y* 15,
                        yoyo: true,
                        repeat: -1,
                        repeatDelay: 600,
                        hold: 600

                    });

                }

                x++;

                if (x === source.width) {
                    x = 0;
                    y++;
                }
            }
        }

        update() {
        }
    }
}