Creating a Wrap Around Effect in Phaser

The Flintstone Effect

This blog post is in response to a query by one of my Udemy students. We were discussing wrap-around effects. Originally we were discussing tilemap wraparounds which he had seen in a game. Though I haven’t quite gotten that one figured out yet, I did mention using two groups to create this effect in games. This is what I also call the Flintstone effect. This comes from the cartoon The Flinstones which I spent a good deal of my childhood watching. The main characters  Fred and Barney would run past the same rock over and over.

The website TvTropes describes it like this

Animated characters running, driving or in a chase scene will pass the same two bushes, three rocks, and four trees over and over again. It is, or was, a cost-cutting measure — the eternally-looping background saves a lot of time and effort on the part of the animators, no matter where the characters are going and how they’re supposed to get there. It occasionally results in Special Effect Failure if the cut between the end of the loop and the beginning isn’t as smooth as it should be. In the industry, this is known as a “repeat pan.”

I had to make a few adjustments to avoid the special effect failure by adding a pixel here and there.

I couldn’t explain it in words how to do it so I have made this example.

Here is the effect:

click the image to open the example in a new window – Use the arrow keys to move the scene left or right

And here is how it is done:

var StateMain = {
    preload: function() {
        //preload the images
        var imageList = ['ground', "house1", "house2", "tree1", "bg_jungle"];
        var len = imageList.length;
        for (var i = 0; i < len; i++) {
            var key = imageList[i];
            var path = "images/" + key + ".png";
            game.load.image(key, path);
        }
    },
    create: function() {
        this.group1 = this.makeGroup();
        this.group2 = this.makeGroup();
        //place group 2 at the end of group 1 
        this.group2.x = game.width;
    },
    /**
     * make a group and fill it with objects
     * return the group
     */
    makeGroup: function() {
        var g = game.add.group();
        var bg = g.create(0, 0, "bg_jungle");
        bg.height = game.height;
        bg.width = game.width;
        var ground = g.create(0, game.height - 48, "ground");
        var house1 = g.create(10, game.height - 245, "house1");
        var house2 = g.create(house1.x + house1.width, game.height - 245, "house2");
        var tree = g.create(30, house1.y, "tree1");
        g.width = game.width;
        g.scale.y = g.scale.x;
        return g;
    },
    update: function() {
        if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) {
            //move the groups to the left
            this.group1.x--;
            this.group2.x--;
            //if the group is all the way off the stage to the left
            //which is the x position plus the width of the group
            //then place it off stage at the right hand side
            //
            if (this.group1.x + this.group1.width < 1) {
                this.group1.x = game.width;
            }
            if (this.group2.x + this.group2.width < 1) {
                this.group2.x = game.width;
            }
        } else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) {
            this.group1.x++;
            this.group2.x++;
            //if the group is all the way off the stage to the right
            //then place it off stage at the left hand side
            if (this.group1.x > game.width) {
                this.group1.x = -this.group1.width + 1;
            }
            if (this.group2.x > game.width) {
                this.group2.x = -this.group2.width + 1;
            }
        }
    }
}

Leave a Comment