A common problem immediately facing the developer of a top-down 2D game in Unity is sprite layering. For example, you may want some GameObjects to render in front or behind the player, depending on where the player is relative to the object. Following are two methods which I've used to implement this behavior. Both work by modifying the sortingOrder of the Sprite Renderer component.
My preferred method is what I call "Absolute Sorting Order." This method sets the sortingOrder based on the y position of the GameObject. Below is the complete AbsoluteSortingOrder.cs script. This script should be attached to every object on the same sorting layer as the player, including the player.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class AbsoluteSortingOrder : MonoBehaviour { public float offset; SpriteRenderer spr; void Start() { spr = gameObject.GetComponent<SpriteRenderer>(); } //sets an objects sorting order relative to its Y position void Update() { spr.sortingOrder = (Mathf.RoundToInt((transform.position.y + offset) * 100f) / 4) * -1; } }
This method sorts all objects based on their position, so any object can be moved during runtime, and it will automatically appear in front of or behind any other objects which it is in front of or behind, respectively, as long as they also have this script attached. It may be possible to overflow the sortingOrder using this script, since it does not constrain the sortingOrder to any maximum value. So far, I have not made a scene large enough for this to be a problem, but it's something to keep in mind.
This script also provides a useful feature: suppose you want to define a point at which one object should be considered in front of or behind another. For example, in the gif at the top of this page, we might want the player to be considered "behind" the tree as soon as the bottom of the player's sprite passes the bottom of the tree's sprite. In order to achieve this behavior, we can add an offset value. The offset for a particular object can be set in the Unity editor, ideally on the prefab of the object. Setting it to a negative value decreases the value of the sortingOrder and the opposite. Thus, if we want to make the tree sprite sort based on the bottom of the sprite instead of the center (or whichever point transform.position.y is derived), we can set offset = -0.2.
The previous method which I used is what I call "Relative Sorting Order." Here, we have the player remain on some predefined sorting order, say 5. Then, we have any GameObjects which we want to sort relative to the player have the following script, called RelativeSortingOrder.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RelativeSortingOrder : MonoBehaviour { public float offset; public int layerAdjHi = 0, layerAdjLo = 0; public string targetTag = "Player"; GameObject target; SpriteRenderer sprRenderer; float buffer = 0.05f; void Start(){ sprRenderer = this.gameObject.GetComponent<SpriteRenderer>(); target = GameObject.FindGameObjectWithTag(targetTag); } void Update(){ if(target == null) { target = GameObject.FindGameObjectWithTag(targetTag); } else { if (target.transform.position.y >= (gameObject.transform.position.y + offset + buffer)) { sprRenderer.sortingOrder = 8 + layerAdjHi; } else { sprRenderer.sortingOrder = 3 + layerAdjLo; } } } }
This script is quite a bit more complex, and the behavior can be less than desirable. For instance, it only allows an object to sort relative to a single object, which we find using a tag (this script in particular looks for the tag "Player"). To work around this, there are two "adjustment" values, called layerAdjHi and layerAdjLo, for the higher and lower sortingOrders respectively. These values could be adjusted to make sure that if two objects overlap, they sort onto the correct layers relative to each other as well as to the target. This script similarly provides the offset functionality as above.
Besides the fact that this script avoids the potential overflow problem in the Absolute Sorting Order (again, I've not had this issue and it's likely an easy fix), it's clearly the worse choice. For example, if your game should have an NPC, or any other GameObject which moves around the level, the script will not sort the objects relative to them, only the player. This script does have the advantage of using hardcoded sortingOrder values, which means that objects which do not conform to this sprite sorting scheme can be sorting according to those values. However, these same behaviors can be achieved with the Absolute Sorting Order method as well, perhaps using different sorting layers or sprite masks.
I've included this method here to show a previous attempt of mine to solve this sprite ordering problem and explain it's drawbacks. You should probably use the Absolute Sorting Order method if you find it useful.