I’m working on a top-down sprite-based RPG in Unity and I’ve encountered somewhat of a problem: because of the 3/4 perspective I’m using, sometimes a player should be drawn above an object and other times, below. I’ve achieved this effect in MonoGame by simply sorting sprites by their Y position values and drawing in that order; however, in Unity this doesn’t appear to be possible. Is there any way to achieve this effect?
I ended up placing a function the following code in my update method:
GetComponent<SpriteRenderer>().sortingOrder = Mathf.RoundToInt(transform.position.y * 100f) * -1;
An old question, but now the answer is either to pivot all your sprites to the bottom and then do the global **Edit ** → Project Settings → Graphics → Transparency Sort Mode: Custom Axis + Transparency Sort Axis: X=0, Y=1, Z=0
But I wanted to do it by script:
using UnityEngine;
public enum SortingOrigin
{
Pivot, Bottom, Top, Left, Right
}
public enum SortingAxis
{
X, Y
}
[RequireComponent(typeof(Renderer))]
public class LayerSorter : MonoBehaviour {
[SerializeField]
private int _originOrder = 100;
[SerializeField]
private float _precision = 1f;
[SerializeField]
private int _offset = 0;
[SerializeField]
private bool _runOnlyOnce = false;
[SerializeField]
private SortingOrigin _sortingSelector;
[SerializeField]
private SortingAxis _sortingAxis = SortingAxis.Y;
private Renderer _renderer;
private float _timeLeft;
private float _updateFrequency = .1f;
private void Awake () {
_renderer = GetComponent<Renderer>();
}
private void Start()
{
UpdateSortOrder();
if (_runOnlyOnce)
{
enabled = false;
}
}
private void LateUpdate ()
{
UpdateSortOrder();
}
private void UpdateSortOrder()
{
_timeLeft -= Time.deltaTime;
if (_timeLeft <= 0)
{
_timeLeft = _updateFrequency;
Vector2 pos = _renderer.bounds.center;
float width = _renderer.bounds.size.x;
float height = _renderer.bounds.size.y;
switch (_sortingSelector)
{
case SortingOrigin.Bottom:
pos += Vector2.down * height / 2;
break;
case SortingOrigin.Top:
pos += Vector2.up * height / 2;
break;
case SortingOrigin.Left:
pos += Vector2.left * width / 2;
break;
case SortingOrigin.Right:
pos += Vector2.right * width / 2;
break;
default:
pos = transform.position;
break;
}
float posFromAxis = _sortingAxis == SortingAxis.X ? pos.x : pos.y;
_renderer.sortingOrder = (int)(_originOrder - posFromAxis / _precision + _offset);
}
}
}
Here’s where I started: Learn game development w/ Unity | Courses & tutorials in game design, VR, AR, & Real-time 3D | Unity Learn
It also has some other good tips on sorting, like sorting groups.
Since this was one of the major results when googling a similar problem, I figure I’d link to my solution based on this answer.
http://forum.unity3d.com/threads/order-in-layer-16-bit-limit-huge-world-in-one-scene.331646/
Order In Layer is 16 bits, so OrderInLayer = Position.Z is limited to 32767,32767.
Unity gives a floating point limitation warning after 99999, which is a little more than 3x the OrderInLayer limitations.
The solution? Divide by 3!
If you change Order In Layer every 3 integers/position instead of every 1, it is really closer to the floating point limitation for positions (32767 * 3 = almost 99999). I tested this by changing Order In Layer every 5, and I didn’t notice any difference in my game. (1 position = 1 pixel). It’s a 3D world with 2D sprites and a perspective camera, so this was not easy after trying so many solutions. I tried other solutions as you can see in the thread (and more that I didn’t mention in the thread.)
Anyway, here is the code:
int pos = Mathf.RoundToInt(theParent.transform.position.z);
pos /= 3;
spriteRenderer.sortingOrder = (pos * -1) + OrderOffset;
As someone is trying to say this is already solved by Unity, but if you are as me and need to have a visual description of the configuration here you are:
Edit > Project Settings > Graphics > Transparency Sort Mode: Custom Axis
Edit > Project Settings > Graphics > Transparency Sort Axis: X=0, Y=1, Z=0
If you are using Tiles I also recommend to configure:
Tilemap Renderer > Mode > Individual