Introduction

Parallax scrolling occurs when there are different layers of backgrounds that move at different speeds in relation to the character to create a sense of depth. In other words, one thing is stationary while a background moves. Parallax scrolling can be beneficial by adding interest to an otherwise static website/game. Transitions from screen to screen and other transitions that happen when the user does something like press buttons, can also help the game feel smoother.

Some cool examples of Parallax Scrolling and resources:

Many other cool techy websites also have parallax!

First step: Overlaying backgrounds. Make sure to add images in slack in the images/background folder.

Define mountains as a background image; do this in the main file (2023-11-27-CSSE-oop-game-levels.md) in assets within the background asset.

  // Define assets for the game
  var assets = {
    obstacles: {
      tube: { src: "/images/platformer/obstacles/tube.png" },
    },
    platforms: {
      grass: { src: "/images/platformer/platforms/grass.png" },
      alien: { src: "/images/platformer/platforms/alien.png" }
    },
    thing: {
      coin: { src: "/images/Coin.png" }, //Add this one!
    },  
    platformO: {
      grass: { src: "/images/brick_wall.png" },
    },
    
    backgrounds: {
      start: { src: "/images/platformer/backgrounds/home.png" },
      hills: { src: "/images/platformer/backgrounds/hills.png" },
      planet: { src: "/images/platformer/backgrounds/planet.jpg" },
      castles: { src: "/images/platformer/backgrounds/castles.png" },
      end: { src: "/images/platformer/backgrounds/game_over.png" },
      mountains: { src: "/images/platformer/backgrounds/mountains.jpg"}
    },
    // other stuff
}

Add mountains as “background2” to the GameLevel constructor, also in the main game file. It is important to put this background with the other game assets in the “hills” tag.

new GameLevel( {tag: "hills", background: assets.backgrounds.hills, background2: assets.backgrounds.mountains, platform: assets.platforms.grass, player: assets.players.mario, tube: assets.obstacles.tube, callback: testerCallBack } );

Define backgroundImg2 in GameLevel.js file within the constructors. This allows us to link the parameters within the constructor within the class GameObject including canvas, image and speedRatio to the second background image. Order is important here!

class GameLevel {
    constructor(gameObject) {
        // conditional assignments from GameObject to instance variables
        this.tag = gameObject?.tag;
        this.backgroundImg2 = gameObject.background2?.file;
        this.backgroundImg = gameObject.background?.file;
        // other stuff
    }
}

Load backgroundImg2. This is basically “testing for the presence of images” and is also in the GameLevel.js file. Here, we just copied and pasted the code for the first background image and changed “backgroundImg” to “backgroundImg2.” Order is important here!

async load() {
        
    // test for presence of Images
    const imagesToLoad = [];
    if (this.backgroundImg2) { // NOTE: DO NOT TOUCH AT ALL PLEASE
        imagesToLoad.push(this.loadImage(this.backgroundImg2));
    }
    if (this.backgroundImg) {
        imagesToLoad.push(this.loadImage(this.backgroundImg));
    }
}

Below the comment “// Prepare HTML with Background Canvas (if backgroundImg is defined)” in the GameLevel.js file copy and paste backgroundImg statement except replace with backgroundImg2. It is important to put this chunk of code before backgroundImg if statement, or else will not be below it as the bottom layer.

try {
    // Do not proceed until images are loaded
    const loadedImages = await Promise.all(imagesToLoad);
    var i = 0;

    // Prepare HTML with Background Canvas (if backgroundImg is defined)
    if (this.backgroundImg2) {
        const backgroundCanvas = document.createElement("canvas");
        backgroundCanvas.id = "background";
        document.querySelector("#canvasContainer").appendChild(backgroundCanvas);
        const backgroundSpeedRatio = 1;
        new Background(backgroundCanvas, loadedImages[i], backgroundSpeedRatio);
        i++;
    }
    if (this.backgroundImg) {
        const backgroundCanvas = document.createElement("canvas");
        backgroundCanvas.id = "background";
        document.querySelector("#canvasContainer").appendChild(backgroundCanvas);
        const backgroundSpeedRatio = 0;
        new Background(backgroundCanvas, loadedImages[i], backgroundSpeedRatio);
        i++;
    }
}

Adding the actual parallax effect. Bottom background is moving while hills background is stationery.

Change backgroundSpeedRatio to 1 in the GameLevel.js file, make sure this is for background image 2. Potential problems: the mario character may not ago as fast as you want it to; in order to make it go faster set the backgroundSpeedRatio to greater value.

if (this.backgroundImg2) {
    const backgroundCanvas = document.createElement("canvas");
    backgroundCanvas.id = "background";
    document.querySelector("#canvasContainer").appendChild(backgroundCanvas);
    const backgroundSpeedRatio = 1;
    new Background(backgroundCanvas, loadedImages[i], backgroundSpeedRatio);
    i++;
}

Adding transition effect.

Add keyframes in the main game file in the brackets, in this case fade in and fadeout change opacity and flashing makes button flash with 0.5s between each flash. Referencing the ids created in the GameLevel.js file “background” and “platform.”

<style>
    #gameBegin, #controls, #gameOver {
        position: relative;
        z-index: 2; /*Ensure the controls are on top*/
    }

#toggleCanvasEffect, #background, #platform{
  animation: fadein 5s;
}

#startGame{
  animation: flash 0.5s infinite;
}

@keyframes flash {
  50% {
    opacity: 0;
  }
}
@keyframes fadeout{
    from {opacity: 1}
    to {opacity: 0}
}

@keyframes fadein{
    from {opacity: 0}
    to {opacity: 1}
}
</style>