Phaser 3 : リボントレイルを作る② パスに沿ったメッシュを作成する

前回に引き続きリボントレイルを作成します、
前回は基本的なメッシュを作成したので、今回はパスに沿ったメッシュを作成したいと思います。 gpnotes.hatenablog.jp

Phaser.Curveでベジェ曲線を作成する

まずはPhaser.Curveでベジェ曲線を作成します。
CubicBezierで制御点を二つもつベジェ曲線を作成することができます。

//ベジェ曲線
public curve: Phaser.Curves.CubicBezier;

public CreateCurve() {
    //ベジエ曲線を作成
    var startPoint = new Phaser.Math.Vector2(0, 500);
    var controlPoint1 = new Phaser.Math.Vector2(50, 100);
    var controlPoint2 = new Phaser.Math.Vector2(600, 100);
    var endPoint = new Phaser.Math.Vector2(700, 500);

    this.curve = new Phaser.Curves.CubicBezier(startPoint, controlPoint1, controlPoint2, endPoint);

    //カーブを描画
    this.graphics = this.add.graphics();
    this.curve.draw(this.graphics);
}

f:id:Phaser_3:20181214152455p:plain

カーブのポイントと次ポイントのベクトルに直交するベクトルを作る

次にパスからメッシュの各頂点を計算します。
curve.getPoints()でパスを引数で分割した座標配列を取得することがでじゅます。

this.curvePoints = this.curve.getPoints(100);

この座標配列curvePointsを使用して頂点配列を作成していきます。
点A(x1 .y1)と点B(x2 , y2)を結ぶ直線に直交するベクトルを作成し頂点として使用すればパスを中心線としたメッシュを作れそうです。
まずは点Aと点Bを結ぶ直線を作成します。

利用しやすくするためにnormalizeで単位ベクトル化しています。

let vecA = new Phaser.Math.Vector2(this.curvePoints[i + 1].x - this.curvePoints[i].x, this.curvePoints[i + 1].y - this.curvePoints[i].y).normalize();

このベクトルに直交するベクトルの端点は
(-vecA.y , vecA.x)
(vecA.y ,-vecA.x) でそれぞれ得ることができます。
fillpintsで点を描画してみるとパスに沿った点が作成されているのがわかります。

var point1 = new Phaser.Geom.Point(-vecA.y*mul + this.curvePoints[i + 1].x, vecA.x*mul+this.curvePoints[i + 1].y );
var point2 = new Phaser.Geom.Point(vecA.y * mul + this.curvePoints[i + 1].x, -vecA.x*mul+this.curvePoints[i + 1].y );

//点の描画
this.graphics.fillStyle(0x123456);
this.graphics.fillPointShape(point1, 3);
this.graphics.fillStyle(0x654321);
this.graphics.fillPointShape(point2, 3);

f:id:Phaser_3:20181214154910p:plain

上の計算を基に頂点配列を作成し、カーブに沿った頂点からverticesを作成するようにCreateVertsを改造してみました。

public CreateVerts(width:number,points:number):number[] {
    let arr = [];

    let pointArray:Phaser.Geom.Point[][] = new Array();

    for (let i = 0; i < points; i++) {
        let mul = width
        let vecA = new Phaser.Math.Vector2(this.curvePoints[i + 1].x - this.curvePoints[i].x, this.curvePoints[i + 1].y - this.curvePoints[i].y).normalize();

        var point1 = new Phaser.Geom.Point(-vecA.y*mul + this.curvePoints[i + 1].x, vecA.x*mul+this.curvePoints[i + 1].y );
        var point2 = new Phaser.Geom.Point(vecA.y * mul + this.curvePoints[i + 1].x, -vecA.x * mul + this.curvePoints[i + 1].y);

        //点の描画
        this.graphics.fillStyle(0x123456);
        this.graphics.fillPointShape(point1, 3);
        this.graphics.fillStyle(0x654321);
        this.graphics.fillPointShape(point2, 3);

        pointArray.push([point1,point2]);

    }

    for (let i = 0; i < pointArray.length-1 ; i++) {


        //左上
        arr.push(pointArray[i + 1][0].x, pointArray[i + 1][0].y);

        //左下
        arr.push(pointArray[i][0].x, pointArray[i][0].y);

        //右下
        arr.push(pointArray[i][1].x, pointArray[i][1].y);

        //左上
        arr.push(pointArray[i + 1][0].x, pointArray[i + 1][0].y);

        //右下
        arr.push(pointArray[i][1].x, pointArray[i][1].y);

        //右上
        arr.push(pointArray[i + 1][1].x, pointArray[i + 1][1].y);

    }


    return arr;
}

f:id:Phaser_3:20181214155108p:plain

パスに沿ってテクスチャが張り付けられているのがわかります。
updateでuvをアニメーションさせることもできます。 f:id:Phaser_3:20181214155550g:plain

これでパスに沿ったメッシュの作成ができました。
次回からはリアルタイムなパスの変形に合わせてメッシュをアニメーションしてみたいと思います。
今回のソース全文はこちらです。

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

namespace MyGame {

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

    export class MyScene1 extends Phaser.Scene {

        //グラフィックオブジェクトを用意
        public graphics: Phaser.GameObjects.Graphics;
        public mesh: Phaser.GameObjects.Mesh;

        public curve: Phaser.Curves.CubicBezier;

        public curvePoints: Phaser.Math.Vector2[]=[];

        public drawDebug: boolean = false;    

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

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

        create() {

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

            this.CreateCurve();

            this.curvePoints = this.curve.getPoints(100);

            //ポリゴンの分割精度
            let points = 100;

            //頂点配列の作成
            let array = this.CreateVerts(5, points);

            //uv配列の作成
            let uv = this.CreateUVs(array.length/12);
            /*
            //頂点色を適当に変化
            let colors = []
            for (let i = 0; i < points; i++) {
                colors.push(i);
                colors.push(i * 2);
                colors.push(i * 2);
                colors.push(i);
                colors.push(i * 2);
                colors.push(i);
            }
            */
            this.mesh = this.make.mesh({
                key: 'block',
                x: 0,
                y: 0,
                vertices: array,
                uv: uv,
            });
            
        }

        public CreateVerts(width:number,points:number):number[] {
            let arr = [];

            let pointArray:Phaser.Geom.Point[][] = new Array();

            for (let i = 0; i < points; i++) {
                let mul = width
                let vecA = new Phaser.Math.Vector2(this.curvePoints[i + 1].x - this.curvePoints[i].x, this.curvePoints[i + 1].y - this.curvePoints[i].y).normalize();

                var point1 = new Phaser.Geom.Point(-vecA.y*mul + this.curvePoints[i + 1].x, vecA.x*mul+this.curvePoints[i + 1].y );
                var point2 = new Phaser.Geom.Point(vecA.y * mul + this.curvePoints[i + 1].x, -vecA.x * mul + this.curvePoints[i + 1].y);

                if (this.drawDebug) {
                    //点の描画
                    this.graphics.fillStyle(0x123456);
                    this.graphics.fillPointShape(point1, 3);
                    this.graphics.fillStyle(0x654321);
                    this.graphics.fillPointShape(point2, 3);

                }

                pointArray.push([point1,point2]);

            }

            for (let i = 0; i < pointArray.length-1 ; i++) {


                //左上
                arr.push(pointArray[i + 1][0].x, pointArray[i + 1][0].y);

                //左下
                arr.push(pointArray[i][0].x, pointArray[i][0].y);

                //右下
                arr.push(pointArray[i][1].x, pointArray[i][1].y);

                //左上
                arr.push(pointArray[i + 1][0].x, pointArray[i + 1][0].y);

                //右下
                arr.push(pointArray[i][1].x, pointArray[i][1].y);

                //右上
                arr.push(pointArray[i + 1][1].x, pointArray[i + 1][1].y);

            }


            return arr;
        }

        public CreateUVs(points: number): number[] {
            let arr = [];
            let uvincr = 1 / points;

            for (let i = 0; i < points; i++) {
                //左上
                arr.push(0);
                arr.push(uvincr * i);

                //左下
                arr.push(0);
                arr.push(uvincr * (i +1));

                //右下
                arr.push(1);
                arr.push(uvincr * (i+1));

                //左上
                arr.push(0);
                arr.push(uvincr * i);

                //右下
                arr.push(1);
                arr.push(uvincr * (i+1));

                //右上
                arr.push(1);
                arr.push(uvincr * i);
            }


            return arr;
        }

        public CreateCurve() {
            //ベジエ曲線を作成
            var startPoint = new Phaser.Math.Vector2(0, 500);
            var controlPoint1 = new Phaser.Math.Vector2(50, 100);
            var controlPoint2 = new Phaser.Math.Vector2(600, 100);
            var endPoint = new Phaser.Math.Vector2(700, 500);

            this.curve = new Phaser.Curves.CubicBezier(startPoint, controlPoint1, controlPoint2, endPoint);
            this.graphics = this.add.graphics();
            if (this.drawDebug) {

                this.curve.draw(this.graphics);
            }
            }

        update() {
            for (let i = 0; i < this.mesh.uv.length;i++) {
                    this.mesh.uv[i] += 0.01;
            }
        }   
    }
}