December 18, 2016
Custom sprite sorting
2D in Unity
It's common to hear that Unity is 'bad for 2d'.
But that is completely false, at best, you can say that Unity's current support for 2D games it a bit lacking, but since Unity is very easy to extend, nothing forbids you from getting something working nice in 2D.
A major question is how to do sorting of sprites in a intuitive way. Meaning, can we define sprites ordering by the following attributes: position(x,y) , elevation from ground (z) and layer value, and get them rendered in the proper order?
(Note: The layer value is an optional value that can be used to sort sprites that are exactly in the same position, eg: different parts of a character rendered as separate sprites).
There is simple way indeed, that is what I call 'skewered sprite rendering', which consists of rendering sprites distorted along a diagonal plane, with some of them being rendered perpendicularly to the plane.
First, we need to categorize sprites as one of two types:
- Horizontal - Those are the sprites that will be "flat", co-planar with the diagonal axis. For example, ground sprites, or GUI elements. Those sprites will have a depth that spans many pixels.
- Vertical- Those are the sprites whose depth will be just a single pixel and will "stick up" in the diagonal plane. This is useful for things like characters or trees, etc.
Now, each sprite is typically rendered as a quad, with 2 vertices on top and 2 on bottom.
Assuming we already have the leftX, rightX, topY and bottomY of the sprite quad, then for each sprite we just need to calculate two more values, the topZ and bottomZ, which then will be used for the Z of the 2 top and 2 bottom vertices, respectively.
- Horizontal - Set bottomZ = -bottomY and set topZ = -topY
- Vertical- Set bottomZ = -bottomY and set topZ = -bottomY (Both are set to the same value, since those sprites depth is only 1 "pixel")
Now, how you use this values, well, it depends on how you are rendering your sprites in Unity, if you are using custom meshes, you can just plug them into the Z values of the vertices, otherwise you might pass them in the shaders or even calculate them inside the shaders directly from the Y coords, its up to you.
Adding extra sorting properties
The solution I described uses only takes into consideration the Y position of the sprites, what about adding the distance from ground or adding a way to sort sprites in the same coordinate?
- Layer value - For adding a way to sort sprites with the same coords, its easy, just add an offset to both the bottomZ and topZ values. This offset should be a number between 0 and 1. This works because the zBuffer supports non-integer values, so by doing this you're basically just adding a subpixel offset.
- Distance from ground - This one is also very easy. Just subtract the distance from the bottomY and topY values, after calculating the 2 z values. By using a negative distance you can have sprites half inside the ground.
And that's it!
By the way, this technique works in any engine that is able to render 3D objects.
Follow me on Twitter for more cool stuff!