Sorting 2D sprites

As sprites move around the screen in an isometric 2D adventure game, they need to show up in front of and behind other sprites as they move.  Most of the Unity 2d tutorials only address basic sorting and mainly in side-scroller type games.

The way I solve this in Bik is to attach a Baseline.cs script to any sprite that needs to move. If the sprite moves in the XY plane, the script will calculate the Z index depth that the sprite should be at.

Make sure the sprite is aligned bottom so it’s feet are at the transform coordinates of the sprite GameObject.

Here is a simplified version of the script I use in Bik.

//store the new Z position
float newZPos;
//the camera looking at the sprite
Camera cam;
//the local transform for speed
Transform trans;

void Start()
    cam = Camera.main;
    trans = transform;

void Update()
    //don't run if sprite is not visible to any camera

    //store the viewport position of this GameObject.  
    Vector3 viewportPos = cam.WorldToViewportPoint(trans.position);

    //now we convert our position in the viewport to a usable number for the z order.	 
    //This will always give us a number between 0 and 5. based on the Y value from the viewportPos.
    //0 will be the top of the screen and 5 would be the bottom. In the isometric view, objects higher on the screen would be "behind" objects lower on the screen.
    newZPos = ConvertScale(viewportPos.y, 0,1,5,0); //See my convert scale method below
    //change the Z position of the GameObject to the newly calculated position between 0 - 5.  
    trans.position = new Vector3(trans.position.x,trans.position.y,newZPos);

//this is a static float I use to convert scales.  This is normally placed in a helper script, but i'll show you it here.
public static float ConvertScale (float old_value, float old_min, float old_max, float new_min, float new_max)
	float new_value = ((old_value - old_min) / (old_max - old_min)) * (new_max - new_min) + new_min;
	return new_value;

Instead of running this in the Update it would be smart to have this script subscribe to a C# event on your Movement script for this character/object. So when the character moves this would be run in a coroutine and then stopped when the character stops moving.
Check out my previous posts for more about Events.

Or just have this as a method directly in your movement script!

Hope this helps someone out! Let me know any questions or comments!


This entry was posted in DevBlog. Bookmark the permalink.

5 Responses to Sorting 2D sprites

  1. jonyfries says:

    Thank you so much for this script! This is exactly what I was looking for. Not sure if I did something differently in setting up my scene but I had to put a negative in front of the newZPos in order to get it to work correctly.

    • Mike Pinto says:

      Glad it could help! Yea the zpos would be negative if you use the default unity 2d camera pos. This was made before unity 2d and the camera was facing the other direction.

  2. Joseph says:

    Thanks a bunch for this Script. I’ve been pulling my hair out all day and night trying to find a way to get this to work. You’ve turned a simple concept and made it very easy to understand. In isometric 2d games its obvious the player needs to be able to move in front and behind sprites. Its addressed very scaresly over the forums and Unity Answers. In the Smooth moves unity asset echo 17 made a decent attempt to mimic this behavior by literally spreading all his 2d sprties along the z axis. Now of course when you switch the camera to orthographic the Za is depth is gone. However the issue of what tree, shrub, bush, or building that’s supposed to be where its supposed to be and overlap the player only if you walk behind it and so on and so forth is solved. But from testing Echoes Chef asset where this idea is implemented it really doesn’t work. The colliders ridgebodies and everything are all screwed up.

  3. Joseph says:

    Hey could you upload an example project with this. That would be a real help. Thanks

  4. Mike Pinto says:

    Hi Joseph,
    I’m glad this has been useful for you!
    If you have 3D Colliders, the colliders will most likely be messed up because they may be thick enough that they still collide even though the sprite is being rendered behind the “front” sprite. To account for this, you could increase the number 5 in the convertscale line to a larger number so that it pushes the objects further on the Z axis.

    The other option and something that must be done for 2d colliders is to place a check at the beginning of your collision methods that checks how close each objects baselines(y pos) are to each other. So it would check the y position and if they are 1 unit (or some other low number) from each other allow the collision, if they are more, than interrupt the collision. You’ll need to continually check this number in a coroutine. You can start the coroutine ontriggerenter and stop it ontrigger exit. Does that make sense?

Leave a Reply

Your email address will not be published. Required fields are marked *