Landscape Path

class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://cdn.phaserfiles.com/v385');
        this.load.image('plane', 'assets/sprites/plane.png');
        this.load.image('sky', 'assets/skies/bigsky.png');
    }

    create ()
    {
        this.add.image(400, 300, 'sky').setScrollFactor(0);

        this.graphics = this.add.graphics();

        this.topPath = new Phaser.Curves.Path(0, Phaser.Math.Between(200, 100));
        this.bottomPath = new Phaser.Curves.Path(0, Phaser.Math.Between(400, 500));

        this.plane = this.add.image(100, 300, 'plane');

        this.input.on('pointermove', pointer => {

            this.plane.x = pointer.worldX;
            this.plane.y = pointer.worldY;

        });

        //  Create a random land which is 1000px long (800 for our screen size + 200 buffer)

        let ty = Phaser.Math.Between(200, 100);
        let by = Phaser.Math.Between(400, 500);

        for (let x = 200; x <= 1000; x += 200)
        {
            this.topPath.lineTo(x, ty);
            this.bottomPath.lineTo(x, by);

            ty = Phaser.Math.Between(200, 100);
            by = Phaser.Math.Between(400, 500);
        }

        this.offset = 0;
    }

    update ()
    {
        //  Scroll the camera at a fixed speed
        const speed = 4;

        this.cameras.main.scrollX += speed;
        this.plane.x += speed;
        this.offset += speed;

        //  Every 200 pixels we'll generate a new chunk of land
        if (this.offset >= 200)
        {
            //  We need to generate a new section of the land as we've run out
            let ty = Phaser.Math.Between(200, 100);
            let by = Phaser.Math.Between(400, 500);

            const topEnd = this.topPath.getEndPoint();
            const bottomEnd = this.bottomPath.getEndPoint();

            this.topPath.lineTo(topEnd.x + 200, ty);
            this.bottomPath.lineTo(bottomEnd.x + 200, by);

            this.offset = 0;
        }

        //  Get the position of the plane on the path
        const x = this.plane.x / (1000 + this.cameras.main.scrollX - this.offset);

        //  These vec2s contain the x/y of the plane on the path
        //  By checking the plane.y value against the top.y and bottom.y we know if it's hit the wall or not
        const top = this.topPath.getPoint(x);
        const bottom = this.bottomPath.getPoint(x);

        //  Draw it
        this.graphics.clear();

        //  This will give a debug draw style with just lines:

        // this.graphics.lineStyle(1, 0x000000, 1);
        // this.topPath.draw(this.graphics);
        // this.bottomPath.draw(this.graphics);

        //  And this will give a filled Graphics landscape:
        this.drawLand(this.topPath, 0);
        this.drawLand(this.bottomPath, 600);

        //  Draw the markers to show where on the path we are
        this.graphics.fillStyle(0x00ff00);
        this.graphics.fillRect(top.x - 2, top.y - 2, 5, 5);
        this.graphics.fillRect(bottom.x - 2, bottom.y - 2, 5, 5);
    }

    drawLand (path, offsetY)
    {
        const points = [ { x: 0, y: offsetY }];

        let lastX;

        for (let i = 0; i < path.curves.length; i++)
        {
            const curve = path.curves[i];

            points.push(curve.p0, curve.p1);

            lastX = curve.p1.x;
        }

        points.push({ x: lastX, y: offsetY });

        this.graphics.fillStyle(0x7b3a05);
        this.graphics.fillPoints(points, true, true);
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#efefef',
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);