Unity 2D Parallax Tutorial: Create Infinite Scrolling Backgrounds From Scratch

 

The Artist's Trick: Creating a Dynamic Parallax Background Effect in Unity 2D

In the world of 2D game development, one of the most powerful and timeless techniques for creating an illusion of depth is parallax scrolling. It’s the magic that makes the moon seem to follow you as you walk, and the distant mountains slide by more slowly than the trees in the foreground. This simple effect instantly elevates a game from a flat, static world into a dynamic, immersive environment.

This guide will teach you everything you need to know to master this essential technique. We'll start with the core principle, build a simple and reusable parallax script, and then advance to creating an infinitely scrolling background that never runs out, no matter how far your player travels.

The Core Concept: How Parallax Works

The principle behind parallax is beautifully simple and comes directly from how we perceive the world. Objects that are farther away from you appear to move more slowly than objects that are close to you.

Think about looking out the window of a moving car. The guardrail on the side of the road zips by in a blur (very fast), the trees a little farther away slide by at a moderate pace, and the mountains on the horizon barely seem to move at all.

Our goal is to replicate this exact behavior in Unity. We will make our background layers move at different speeds relative to the camera's movement. A layer's "speed" will be directly tied to its perceived distance from the player.

Step 1: Preparing Your Art Assets and Scene

Before we write a single line of code, a proper setup is crucial.

1. Separate Your Layers
Your background needs to be composed of multiple, separate images. Each image will be a different layer of your parallax effect. For example, you might have:

  • sky_layer.png (The farthest back, will barely move)

  • mountains_layer.png

  • distant_forest_layer.png

  • midground_trees_layer.png (The closest, will move the fastest)

Export each of these as a PNG with a transparent background.

2. Configure Import Settings
For a crisp, pixel-perfect look, import each layer into Unity with these settings:

  • Texture Type: Sprite (2D and UI)

  • Filter Mode: Point (no filter)

  • Compression: None

3. Set Up Sorting Layers
We need to tell Unity the correct order in which to draw these layers. Don't rely on the object's Z-position; use Sorting Layers for 2D.

  1. Go to Edit > Project Settings > Tags and Layers.

  2. Under Sorting Layers, click the + icon to add new layers. Create layers that match your art, in order from back to front (e.g., Sky, Distant Mountains, Forest, Midground).

  3. The layer at the top of the list is drawn first (farthest back).

4. Assemble the Scene

  1. Drag each of your layer sprites into the Hierarchy to create new GameObjects.

  2. Position them in your scene view to compose your background.

  3. For each background GameObject, find the Sprite Renderer component and set its Sorting Layer to the appropriate layer you created in the previous step. This ensures the sky is always behind the mountains, and so on.

Your scene is now prepared. It looks static, but all the pieces are in the right place, ready to be brought to life.

Step 2: The Parallax Script - A Reusable Component

We will create a single, elegant script that can be attached to any background layer to make it move.

  1. In your Assets folder, right-click and go to Create > C# Script. Name it ParallaxLayer.

  2. Double-click the script to open it in your code editor.

  3. Replace the default code with the following:

codeC#

using UnityEngine;


public class ParallaxLayer : MonoBehaviour

{

    // The main camera that is following the player.

    [SerializeField] private Transform cameraTransform;


    // A value between 0 and 1. 0 means it doesn't move (like the sky).

    // A value closer to 1 means it moves almost as fast as the player.

    [SerializeField] [Range(0f, 1f)] private float parallaxEffectMultiplier;


    private Vector3 lastCameraPosition;

    private float textureUnitSizeX;


    void Start()

    {

        // Store the starting position of the camera.

        lastCameraPosition = cameraTransform.position;


        // Get the width of the sprite to calculate when it needs to loop.

        Sprite sprite = GetComponent<SpriteRenderer>().sprite;

        Texture2D texture = sprite.texture;

        textureUnitSizeX = (texture.width / sprite.pixelsPerUnit) * transform.localScale.x;

    }


    void LateUpdate()

    {

        // Calculate the distance the camera has moved since the last frame.

        Vector3 deltaMovement = cameraTransform.position - lastCameraPosition;


        // Move the background layer by that distance multiplied by our effect multiplier.

        // This creates the core parallax effect.

        transform.position += new Vector3(deltaMovement.x * parallaxEffectMultiplier, deltaMovement.y * parallaxEffectMultiplier, 0);


        // Update the last camera position for the next frame.

        lastCameraPosition = cameraTransform.position;


        // --- This section handles the infinite scrolling ---


        // Check if the camera has moved far enough to the right that we need to loop the texture.

        if (Mathf.Abs(cameraTransform.position.x - transform.position.x) >= textureUnitSizeX)

        {

            float offsetPositionX = (cameraTransform.position.x - transform.position.x) % textureUnitSizeX;

            transform.position = new Vector3(cameraTransform.position.x + offsetPositionX, transform.position.y);

        }

    }

}

Understanding the Script:

  • cameraTransform: A reference to our main camera. We track its movement.

  • parallaxEffectMultiplier: This is the magic number. A value of 0 means the layer is locked to the camera's Z-axis and won't move horizontally or vertically relative to it. A value of 1 would mean the layer moves perfectly with the camera (which would make it look like it's part of the foreground). We will choose values in between.

  • Start(): We store the camera's initial position and calculate the world-space width of our sprite. This is needed for the infinite scrolling logic.

  • LateUpdate(): We use LateUpdate because it runs after all other Update functions. This is a best practice for camera-related logic, as it ensures the player and camera have finished their movement for the frame before we calculate the background's position, preventing any visual jitter.

  • deltaMovement: This is the key calculation. We find out how much the camera has moved since the last frame.

  • transform.position += ...: We move our background layer, but not by the full camera delta. We move it by the delta multiplied by our effect multiplier. This is what makes it move slower.

  • Infinite Scrolling Logic: The if statement at the end is our elegant solution for infinite backgrounds. It checks if the camera has moved a full texture-width away from the background's center. If it has, it instantly repositions the background piece to be centered on the camera again, creating a seamless loop.

Step 3: Putting It All Together

Now, let's apply our script and see the magic happen.

  1. Select your first background layer GameObject in the Hierarchy (e.g., midground_trees).

  2. Drag the ParallaxLayer.cs script onto it in the Inspector.

  3. You will see two fields:

    • Camera Transform: Drag your Main Camera from the Hierarchy into this slot.

    • Parallax Effect Multiplier: This is where you define the speed. For the closest layer, set it to a higher value, like 0.6.


  4. Repeat this process for all your background layers, assigning the camera and decreasing the multiplier for each layer that is farther away:

    • midground_trees: 0.6

    • distant_forest: 0.3

    • mountains: 0.1

    • sky: 0.01 (or even 0 if you want it completely static).


  5. Important: Make sure your player character has a script that moves it and that your camera has a script to follow the player.

Hit Play!

Now, as you move your character, you will see your background come to life. The layers will slide at different speeds, creating a rich and convincing illusion of depth. As you move far to the left or right, the infinite scrolling logic will seamlessly reposition the textures, allowing your world to stretch on forever. You've now mastered one of the most fundamental and visually rewarding techniques in 2D game development.


Comments

Popular posts from this blog

Step-by-Step Guide on How to Create a GDD (Game Design Document)

Unity Scriptable Objects: A Step-by-Step Tutorial

Unity 2D Tilemap Tutorial for Procedural Level Generation