Once you start working on a 2D game, the question of how to set up a proper render order will come up very early on. Render order, at least in the way I will be using the term, determines which parts of your 2D game get rendered (drawn, displayed, shown) in front of (i.e. later than) other parts.
Unfortunately, you won't be able to figure out the requirements for a good system until after you need it. I just spent almost a week changing ours from what I thought would be good when we started our current project to what many months later turned out to make a lot more sense. Here's hoping that our findings will save you the trouble of going through that painful refactoring process and help you get it right from the start.
Please note:
Some of this makes sense for any 2D game, but some will be specific to a perfect side view typical for side-scrolling games.
This system accounts for real-time lighting; if that's no concern of yours, a simpler system might do.
It also accounts for multiple vertical layers, some of which are only revealed after a foreground sprite fades out. If your game doesn't have this, parts of our system will be irrelevant for you.
I will be using some Unity-specific terminology like "Sorting Layer" and "Order in Layer". Nevertheless, most of this article should be helpful on a conceptual level regardless of game engine.
1. Quick introduction: Render Order Terminology (in Unity)
In case you're working in Unity and are completely new to the topic, I thought I'd provide a short explanation of Unity's Render Order system (as of Unity 2019.3.13) and give you a quick intro into its usage. If you already know it, skip right ahead to Best Practices below.
The following assumes that you actually work in a 2-dimensional space and aren't using the Z axis to determine Render Order. If you want your sprites to use Unity's 2D Render Order system instead of rendering based on your sprites' Z positions, you need to select your camera and set its Projection to Orthographic instead of Perspective.
1.1 Sorting Layers
Once that is done, all your sprites will be rendered based on Sorting Layers ("SL" for short). These work essentially like layers in Photoshop or any other image editing software: All sprites on one SL will always be rendered in front of all sprites on another, thus adding depth on an "imagined Z axis".
Let's have a look at the available Sorting Layers. Add a SpriteRenderer component to a game object and select the Sorting Layer drop-down.
Hit Add Sorting Layer... at the bottom to open the Tags and Layers window. It shows all existing SLs and allows you to add any number of new ones. "Default" cannot be deleted, but you don't have to use it. The SL at the top of the list is rendered at the very back, the SL at the bottom is all the way in the front.
Most of the advice under Best Practices pertains to which layers you should set up, so I won't go into detail about it here. However, here's a Unity-specific tip: When adding new SLs, use a slash to nest them. Nested layers will then appear neatly grouped together in the SL drop-down on your Sprite Renderers. In the example below, we distinguish between middleground (MG) and background (BG), each of which encompass several SLs:
Once your SLs are set up, you can assign any sprite in your scene to them by selecting its SpriteRenderer component and picking the desired SL from the drop-down menu.
1.2 Order in Layer
Within each SL, sprites are again organized into an Order in Layer ("OiL" for short). While SLs have names, the OiL of a sprite is indicated as an integer. The higher it is, the further in front a sprite is rendered. Consider the following example containing three SLs: "Background", "Middleground", and "Foreground".
Sprite | Sorting Layer | Order in Layer |
---|---|---|
Sky | Background | -1 |
Moon | Background | 0 |
Skyline | Background | 1 |
House | Middleground | -1 |
Player character | Middleground | 0 |
Column | Middleground | 1 |
Laundry | Foreground | 0 |
Even though sky, moon, and skyline are all on the background layer, they are rendered in the correct order because of their Order in Layer. As you can see, an OiL can have a negative int value.
2. Best Practices
At this point, you might think that you know all you need to render your sprites in the right order. If you're going for a more simple look, that might be the case. However, if you want to go the extra mile (especially if you have dynamic lighting), here are some more specific tips for setting up Sorting Layers (SLs) and Orders in Layer (OiL) in a future-proof way. And even though the terminology comes from Unity, much of the following advice should apply to any engine on a conceptual level.
2.1 Background and Foreground
For our project, we have decided to distinguish between background (BG), middleground (MG), and foreground (FG). This is merely a conceptual distinction; on a technical level, each of these is made up of multiple Sorting Layers. We also added a Sorting Layer named "Dev" for sprites that we use for development only that's invisible to the player. Something we didn't do (but might in the future) is add another visible layer for all World Space UI to ensure that it renders in front of all non-UI sprites.
Hiding and showing the three main groups of Sorting Layers (BG, MG, and FG) using a custom script
No matter how you end up doing it, one thing to keep in mind when setting up SLs is lighting. Each 2D Light component in Unity can be set to affect or not affect any number of Sorting Layers:
This means that the system determining render order and the system determining which lights affect which sprites are intertwined. This may cause problems; for example, let's go back to the example shown under Sorting Order above: If you add a light to brighten the moon, it will inevitably (within its range) also brighten the sky and skyline because all three of them are on the same Sorting Layer. You might not want one light in the background to affect another background layer that seems very far away from it (on the imagined Z axis). Luckily, this can easily be resolved by making the system more granular, i.e. by adding more Sorting Layers. The moon and sky could remain on the same SL and the skyline could be put on a new one that's visually closer to the player. This way, the moon would light up the clouds, but not the skyline.