- Home /
Sprite layer order determined by Y value
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?
Answer by deadgenre · Jan 23, 2014 at 03:18 AM
I ended up placing a function the following code in my update method:
GetComponent<SpriteRenderer>().sortingOrder = Mathf.RoundToInt(transform.position.y * 100f) * -1;
what if all characters are moving?! this need to be done in Update for all characters and it seems performance consu$$anonymous$$g!
The best answer (in year 2020) is by $$anonymous$$uskar.
In Unity: Edit -> Project Settings -> Graphics -> Transparency Sort $$anonymous$$ode: Custom Axis + Transparency Sort Axis: X=0, Y=1, Z=0
Old thread, but since happened to not-read the whole thread I thought to add a comment here.
Answer by Muskar · Nov 06, 2018 at 07:58 PM
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: https://unity3d.com/learn/tutorials/topics/2d-game-creation/sorting-groups-and-transparency-sort-axis
It also has some other good tips on sorting, like sorting groups.
This should be the accepted answer. Why would you do it by script when unity has a built-in option though?
Answer by CarterG81 · Jun 08, 2015 at 11:35 AM
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;
OrderOffset is not required. It's simply an integer which you can add to the equation so that you can offset the order by +/- any number.
I use OrderOffsets because my characters are multiple sprites, so their legs may be behind their bodies & their heads infront of their bodies. So Body's OrderOffset = 0, Legs = -1, Head = 1, etc.
Hi there,
New to Unity and attempting this solution to render my 2D sprites based on y position. axi0n's answer above is enticing in its simplicity, but I intend to layer sprites atop one another similar to how you have described yours for my game. However, I'm unsure exactly what you mean by theParent in your code (or rather how to set it appropriately in this code, I understand the concept of parent and children objects). In short, I intend to layer equipped items over the base player sprite but want to ensure each of the equipped items still follow the player sprite's actions and order in layer so that they move behind objects when the y value is greater than the world object's and vice versa.
Thanks in advance for the help!
Not sure why I was never notified of this comment. TheParent is just the root object for that game entity. The gameobject that all your child gameobjects are under.
"theParent" is the main gameobject, while I have child gameobjects which have other components. So "theParent" is just the gameobject that you want to actually move via Transform.
Answer by d2clon · Feb 17 at 04:10 PM
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
This isn't working for me with multipart characters. I have a base body sprite, then shirt and pants on top with +1 sorting order. When I have two of these characters standing close to one another, the character below will have their skin rendered below the clothes of character standing above.
Not sure how to solve this without manually setting the sorting orders based on Y-axis. :/
Edit: Ok the solution was quite simple when I just stopped to think for a moment. I made the Transparency Sort Axis X: 0, Y: 1 and Z: 1, then instead of having the body and clothes with different order in layer, I have the clothes Z-axis set to -0.0001 (just some small value to make sure they render on top of the body).
Your answer
Follow this Question
Related Questions
How to merge Sorting Layers ? 1 Answer
Sprite Rendering Cost Calculation 1 Answer
Background Sprite Setting 0 Answers
Render sprite only on top of a specific sorting layer 1 Answer
Shader vs Sprites - Sorting layer (ZWrite Off not working) 1 Answer