Elegant parallax scrolling in Unity

Between how did the world start and how to stop global warming, there is another question I’m sure every one of use had asked ourselves at least once in our lives. And that is how to do parallax scrolling in Unity in an elegant way.

In this post I’m going to show you exatcly this.

What is parallax scrolling?

Essentially, parallax scrolling is a technique where background images move at a different speed. The closer they are to the point of view, the faster they move and vice-versa.

Remember the last time you were on a train, looking out of a window. The trees that are closest to you would move past you very fast. But if you focused on the faraway mountains, you would notice they remain relatively static.

So, by just following this simple principle in our games we can easily add more dynamic and depth to our backgrounds.

Parallax scrolling example

Enough talking, show me how to do it

Coding in a parallax system is relatively simple. I will be showing you an example of the game I’m working on currently. It’s done in Unity, but the principle applies to any game engine or framework out there. You can probably do it in a similar way in assembly, but although we have all this time during the quarantine, I don’t recommend doing that if you want to stay sane.

Here’s the background that we’re going to use

Parallax scrolling in Unity

1. Seperate the background to layers

The first step is to separate visually each layer of the background. Since we want them to move at a different speed, we need to have them in different assets.

Exactly how to do this depends on the software you use for making your assets. But most of them have layers, so all you got to do (which is good practice in general) is to sepearete each part of the image to a seperate layer. This will allow us to export it as a separate image.

Parallax scrolling in Unity

This leaves us with 4 assets, I’m going to call them:

  • Mountains Close (layer 1)
  • Mountains Far (layer 2)
  • Clouds (layer 3)
  • Sky

Let’s add them to Unity, each in it’s separate Sprite renderer.

Parallax scrolling in Unity

2. Make them move

The result is rather boring, we need to make them move. This is simple, we’ll just create a SpriteMovement script that will update the x position of the sprites every frame.

/**
 * Moves a sprite along the X axes using a predefined speed
 */
public class SpriteMovement : MonoBehaviour
{
    public float speed = 1f;

    private void Update()
    {
        //Save the current position, so we can edit it
        var newPosition = transform.position;
        //Move the position along the x axis by an amount that depends on the
        //defined speed and the deltaTime, so we can get a framerate independent movement
        newPosition.x -= speed * Time.deltaTime;
        //Update our position
        transform.position = newPosition;
    }
}

No we add it to each of our sprites and let’s see the results!

3. Sprite duplication

All good, but we have a problem. The sprites go to the left and start disappearing. To fix this we must duplicate each sprite at a precise point, so that the sprites on the screen are seamless and move together as one big sprite.

For that, we will create the boringly named behavior SpriteDuplicator.

Now, there are a couple of ways to go about this. The most obvious one would be to instantiate a copy of the sprite to the right of the current one, until we have enough to fill the screen. Then, when a sprite reaches a far enough point along the x-axis, ie. goes to the left a certain amount, we can destroy it and create another copy at the right.

In theory, it sounds good, we will have a seamless traffic of sprites flowing around. And in a simple game like our example, it could be good enough.

But since I’m here, teaching you something, let me at least try and teach you the better thing to do. The above way of doing things is not ideal, because creating and destroying objects is less efficient than disabling or reusing them.

If you haven’t caught up to what I’m about to do, you should introduce yourself to object pooling. Essentially, it is a pattern where commonly used objects, for example bullets, enemies, or in our case – background images, get reused, instead of throwing them away and then creating new ones.

There can be very complicated pooling systems out there, but for this effect, we need a simple pool. We won’t deactivate the objects and let them sit somewhere in the void of Unity. What we will do is keep a fixed amount of our objects in an array. We will only create those sprites at the start. Then every time we would destroy one sprite and create another, we will instead just move the sprite to the new position. That way our leftmost sprite will become our rightmost, and so on.

Now, a particular thing about the way I created this script is that there is not something like “SpriteDuplicationManager”. Or in other words – the script that manages the duplication of sprites is in the sprite object itself. I’ve chosen to do it this way, mainly because it’s simpler to work with in the inspector. You attach the script to your sprite, no need for prefabs, no need for managers. Just a script and a sprite. The only downside to this is that if we keep the duplicator script in all our copies, they will duplicate to infinity and make Unity crash. A simple fix to that problem is to remove the duplicator script from all our copies and only keep it in the “main” sprite object.

Anyway, here’s the code:

/**
 * Creates copies of this object and arranges them seamlessly, so that one is right next to the other.
 */
[RequireComponent(
    typeof(SpriteRenderer))] //Make sure we have a SpriteRenderer, because we need the width of the sprite
public class SpriteDuplicator : MonoBehaviour
{
    /**
     * The number of copies of the object that we will have at any given time, including this object
     */
    [SerializeField] private int poolSize = 5;

    //Adjust this if you have gaps between your sprites. Smaller correction -> more gap. Bigger correction -> less gap")
    [SerializeField] private float spriteRepositionCorrection = 0.01f;
   

    /**
     * An array of all current copies of this object, including this object
     */
    private Transform[] duplicatesPool;

    /**
     * Here we keep the width of the sprite, so we can later calculate where to place other objects
     */
    private float spriteWidth;

    private void Start()
    {
        //First, we instantiate a number of objects that we will reuse. This is also called pooling. 
        //We do this, because it's more efficient than creating and destroying objects over and over again.
        duplicatesPool = new Transform[poolSize];

        //Now, we need to get the width of our sprite, because we will position the objects based on how wide they are
        spriteWidth = GetComponent<SpriteRenderer>().bounds.size.x;

        //Let's populate our array with enough items. First add this object, as it's part of the pool.
        duplicatesPool[0] = transform;

        var startingPos = transform.position;

        //Next duplicate this and add the objects, until we fill our pool
        for (var i = 1; i < poolSize; i++)
        {
            var position = new Vector2(startingPos.x + i * spriteWidth, startingPos.y);
            duplicatesPool[i] = Instantiate(gameObject, position, Quaternion.identity, transform.parent).transform;
            //It's very important to remove the sprite duplicator script from the copied object
            //Otherwise we will get an infinite loop of sprite duplication
            Destroy(duplicatesPool[i].GetComponent<SpriteDuplicator>());
        }
    }


    private void Update()
    {
        //We need to check if any of our sprites has gone of screen. 
        //We will reposition it to be on the right side if it is
        foreach (var duplicate in duplicatesPool)
        {
            //Let's assume that 2 sprites are enough to fill the screen
            if (duplicate.transform.position.x < -spriteWidth * 2)
            {
                //In order to reposition it, we need to get which sprite is the rightmost currently
                var rightmostSprite = GetRightMostSprite();
                
                //Let's position it to the right of that sprite
                var startingPos = rightmostSprite.position;
               //Let's calculate the new position. We will use a reposition correction, in case there is a gap between the two sprites
                var position = new Vector2(Mathf.FloorToInt(startingPos.x + spriteWidth) - spriteRepositionCorrection * transform.lossyScale.magnitude, startingPos.y);
                duplicate.transform.position = position;
            }
        }
    }

    private Transform GetRightMostSprite()
    {
        var rightmostX = Mathf.NegativeInfinity;
        Transform rightmostSprite = null;
        foreach (var duplicate in duplicatesPool)
        {
            if (!(duplicate.position.x > rightmostX)) continue;
            
            rightmostSprite = duplicate;
            rightmostX = duplicate.position.x;
        }

        return rightmostSprite;
    }
}

After we attach this component to each of our sprites, we get this:

4. Adding parallax scrolling

So far so got, but they all move at the same speed. Where’s the parallax in that?

To add the parallax scrolling effect, we need to have to do two things:

  • Point out which object will move at what speed
  • Synchronize the speed of objects of the same type, so that we can control it in one place

Now, we can write a complex system of mono behaviors that will collect all of the moving objects and adjust their speed. But who has the time for complex things? Especially when we can do it simply and elegantly. We will instead leverage Unity’s Scriptable object.

Let’s abstract away the actual speed of our object. In our moving objects we will only specify a Type of Movement speed, not the speed itself. That way we can categorize our moving objects into different groups, each with its own speed. And we can adjust those speed centrally, because they are not hardcoded in the objects. That way, if we have to change the speed of, for example, all close background objects, we don’t have to go over each one of them manually.

If that sounded complicated, don’t worry, just read on and see the sample code. I’m sure it will click then.

Let’s create a scriptable object called MovementSpeedType that we will use for that purpose. It needs only one parameter – the actual speed of the objects.

P.S. (You can see more info about this and many more usages of Scriptable Objects in this great talk)

[CreateAssetMenu(fileName = "MovementSpeedType", menuName = "Movement Speed Type")]
public class MovementSpeedType : ScriptableObject
{
    public float speed = 1f;
}

Now, let’s create a couple of those. (Right click in project view => Movement Speed Type.

I have created:

  • Slowest (with speed 0.2)
  • Slow (with speed 1)
  • Medium (with speed 3)
  • Fast (with speed 6)

Now, we only need to add a MovementSpeedType parameter in our SpriteMovement class, so that we can specify different speed for each of our objects. And also, we need to get our speed from it, in case we have set a MovementSpeedType:

public class SpriteMovement : MonoBehaviour
{
    public MovementSpeedType movementSpeedType;

    [Tooltip("Used only if no movement speed type is specified")]
    public float speed = 1f;

    private void Awake()
    {
        if (movementSpeedType)
            speed = movementSpeedType.speed;
    }
    ...
}

After that, we go and specify for each of our sprites the movement speed type. I’ve set mine as follows:

  • Sky: Slowest
  • Clouds: Slow
  • MountainFar: Medium
  • MountainClose: Fast

Hopefully you got the idea, you can create as many movement speed types as you need for your game.

Having a centralized system like this one can feel a bit overengineered, but imagine you had 50 objects, where you had hardcoded speed values. Now imagine you want to change their speed. Yeah, going over 50 objects and synchronizing their speeds doesn’t sound like a fun thing to do.

Final result

Hopefuly, you got something useful out of that. Now, this is my way of doing this, there might be other techniques and methods to do it. But that’s the fun about programming things yourself – it’s a sandbox, where you can play and experiment your way into something functional.

Please leave a comment if you see something that I’ve missed or if you find something unclear, I’m always open to help. Also, I’ll be happy to know if you have a different way of doing this and think mine sucks, don’t hesitate to leave an angry comment if that’s the case!

Have fun and stay safe 🙂

PS: If you’re still havign trouble with parallax, or if you want to get in touch with all the code from this tutorial, here is an Unity asset store package that can help you:

Easy Parallax

3 thoughts on “Elegant parallax scrolling in Unity”

  1. Great article, but if you want to be “Elegant” at least mention object pooling on SpriteDuplicator.cs . That made my eyes hurt even if it’s just simple way to make someone understand duplication and not overloading memory in the process. But i think, at least a mention would be efficient.
    Also what happens if you add distance between layers? (Z Axis). Does this script needs some tweeks on its movespeed?
    Keep it up!

    Reply
    • Thank you!
      That’s a fair point, didn’t put much attention to pooling. I updated with more info!

      About the Z-axis – in my example, I’ve used sprite layer ordering instead of having different Z-axis, but it wouldn’t be a problem if you do. It’s just moving sprites on the X-axis, the Z will not affect it.

      Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.