Procedural Scrolling Landscape Path

Create an infinitely scrolling landscape using dynamic Paths and Graphics. This example demonstrates extending paths with random segments and using fillPoints to render filled terrain shapes.

Procedural Scrolling Landscape Path, running
800 × 600
GitHub
class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://cdn.phaserfiles.com/v410');
        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 = {
    scale: { mode: Phaser.Scale.FIT },
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#efefef',
    parent: 'game-container',
    scene: Example
};

const game = new Phaser.Game(config);

More in Paths13 examples