Skip to content

MonkeyBlog

Lora Noto Sans Ag

More adventures

Hello World!

A little journey back to the past and why I started this blog

The Parallax Effect

Adding Cool Graphics

In the previous post, I focused on testing with Matter.js and creating a simple car. In this post, I’m going to take it to the next level. Since my background is in UX design, I really wanted to add some graphics to this game to make it more fun to play. Matter.js has some simple methods to add textures to different objects. First, we’ll add some graphics to the car elements. You can add a sprite to any Matter.Bodies element. It’s very simple:

render: {
    sprite: {
        texture: <url to texture.png>,
        xScale: 1, // 1:1 scaling
        yScale: 1 // 1:1 scaling
    }
}

My First Car

I wanted a game with some cool physics, and what better car to test that than a monster truck? It has big wheels and a big suspension. So, based on some real photos, I drew a monster truck. Monster Truck

So when I start the game it looks something like this Current State of the Game

Adding Parallax

I've always been a big fan of the parallax effect. It's such a simple yet powerful effect that gives the user a sense of depth. Since I'm building a 2D game, this is perfect. Some other inspiration I had came from a website for the game Firewatch. I really love the vibes of that website, and when it launched, it was a trendsetter! So, I drew a couple of layers that I could put in the background of the game. Oh, and just a small tip: I figured this out myself, but you can actually make the canvas element transparent! So, we're going to use some CSS and JavaScript to animate the different backgrounds. My HTML looks like this:

<div id="parallax-container">
    <div id="background1" class="parallax-layer" style="background-image: url('/assets/img/background1.png');"></div>
    <div id="background2" class="parallax-layer" style="background-image: url('/assets/img/background2.png');"></div>
    <div id="background3" class="parallax-layer" style="background-image: url('/assets/img/background3.png');"></div>
    <div id="background4" class="parallax-layer" style="background-image: url('/assets/img/background4.png');"></div>
    <div id="background5" class="parallax-layer" style="background-image: url('/assets/img/background5.png');"></div>
    <div id="canvas-container"></div>   
</div>

Now we need to animate the layers based on the camera position. But as I'm writing this, I realize I never told you how to set up a camera in Matter.js. Here we go! The Matter engine has some callback events where we can add functionality. So inside:

Matter.Events.on(engine, 'beforeUpdate', function(event) {
    // do stuff
});

I want to focus the camera on the car, so we get the x and y position of the car and use the lookAt function of Matter. This is how my code looks:

const zoomFactor = config.zoomFactor; 
let cameraX = car.carBody.position.x;
let cameraY = car.carBody.position.y;

Matter.Render.lookAt(render, {
    min: { x: cameraX - render.options.width / (2 * zoomFactor), y: cameraY - render.options.height / (2 * zoomFactor) },
    max: { x: cameraX + render.options.width / (2 * zoomFactor), y: cameraY + render.options.height / (2 * zoomFactor) }
});

To explain a bit what's happening here, the lookAt function positions and sizes the viewport around the given object bounds. In this case, we need a top-left corner (min) and a bottom-right corner (max) to center the car. I'm using the zoomFactor to zoom in or out as needed.

Back to the parallax effect. The trick of the parallax effect is that layers in the background move "slower" than layers in the foreground. This creates a sense of depth. For example, in the background, I want to have clouds or the sky, and in the foreground, some mountains or trees. To achieve this, we need to animate the background-position of each individual layer at different speeds. To adjust this per layer, I added an attribute to each layer like this:

<div id="parallax-container">
    <div id="background1" class="parallax-layer" data-speed="0" style="background-image: url('/assets/img/background1.png');"></div>
    <div id="background2" class="parallax-layer" data-speed="0.01" style="background-image: url('/assets/img/background2.png');"></div>
    <div id="background3" class="parallax-layer" data-speed="0.02" style="background-image: url('/assets/img/background3.png');"></div>
    <div id="background4" class="parallax-layer" data-speed="0.05" style="background-image: url('/assets/img/background4.png');"></div>
    <div id="background5" class="parallax-layer" data-speed="0.08" style="background-image: url('/assets/img/background5.png');"></div>
    <div id="canvas-container"></div>   
</div>

Next, we need to calculate the position for each layer, so let's create a function to go through each layer and update the background-position.

export function setupParallax() {
    const parallaxLayers = document.querySelectorAll('.parallax-layer'); // Array with all the layers

    function updateParallax(carBody) {
        parallaxLayers.forEach(layer => {
            const speed = layer.dataset.speed; // Get the speed attribute
            const offset = -carBody.position.x * speed; // Get the x position of the car and multiply it by the speed
            layer.style.backgroundPosition = `${offset}px 0`; // Set the background position
        });
    }

    return { updateParallax };
}

And that's it, we have a animated parallax effect, the game looks like this: