- Home /
Character sprite rendering order
I have a character made of multiple sprites so I can change a specific sprite. For example, I can make sprite sheet of shirts and change the "shirt"-sprite to change character's shirt.
Sprites rendering order has to be reasonable (hat is on upper layer than head etc). This can be done in several ways, but the problems come when character interferes with another character.
What I want:
Characters that have smaller y-position always to be rendered first.
Character body parts to be rendered in specific order, but this shouldn't break the previous rule.
All this should work with x-amount of moving characters.
Here's what I've tried so far:
Use sprites "order in layer"-system for all character sprites. Then I change character's z-value depending on y-position. Works with one character but when character interferes with other, higher layer sprites will always be rendered first, no matter what the z-value is.
Use only the z-position for rendering the character different parts (sprite layers is set to 0). I Change parts z-value (first rendered parts to smaller values), but I use really small differences (last rendered sprite z is 0, second -0.0001 etc). I also change the character's main z-value depending on y-position. All seems to work fine in editor, but when I press play, sprites start to flickering randomly. This won't happen if I use higher differences for parts z-value, but when two character y-values are close to each other, sprites that has smaller z will always be rendered first.
I think Unity SpriteRenderer should have sublayers.
Why you are not using the "Order-In-Layer" Setting in the SpriteRenderer-Component? smaller values are rendered later than higher values.
Thank you for answering, but changing SpriteRenderer "order in layer" didn't work in this case as I said in my question.
Answer by keburanuil · Jan 05, 2015 at 04:19 PM
Thanks for your answers
I solved this partially by writing a method that changes all the 'order in layers' of the character's sprites depending on the y-position of the character. The method triggers only if there's a change in the character's y-position.
I changed the character's 'local' sprite order by adjusting sprites' position z-values with numbers large enough. I think Unity should include some kind of a sublayer-system here.
However, if there are different sized objects, in which layers also need to be updated, you'll have to consider the point of sprite where you start calculating the right layer. Usually you'll want to start calculating from the bottom of the sprite. So basically you compare sprites' bottom point of the y-position and render the lower first.
Answer by Squabbler · Aug 05, 2014 at 01:55 PM
You could try putting the player's character on a higher sorting layer than the NPCs or enemies. Then you can still have ordered sprites within your player, and still control how they overall stack in the game.
Example
Create two new sorting layers under edit layers (player and NPCs)
On your player prefab, set the sprite renderers to the player sorting layer.
Change the order of the sprites in the player using the order in layer on the sprite renderers
Do the same for the other characters (in this example NPCs) except use the NPCs sorting layer
This could be the answer, but the problem is that there is x amount of characters and characters move. So I should make new sorting sorting layer for every new character and switch them if two character y values overtake each others.
Are the characters supposed to be running through each other? Because if not, then you can prevent them overlapping with box colliders. Do you have a screenshot of the game?
Characters are supposed to be running through each other. I added a screenshot to my question.
Ah, okay. Seeing this, you might want to look into what Patel Sagar said below (ordering them in script based on Y value), especially if the number of characters is dynamic.
Answer by Patel-Sagar · Aug 05, 2014 at 01:56 PM
Well that depends on how many characters you have in scene?
If number of characters are static and less(e.g. 3) than you can define new Layer in 'Sorting layers' in project settings->tags and layers.
If number of characters are dynamic then I think you have to fill 'order in layer' value from script as per character movements.
Let me know if you need detailed explanation with your requirement.
But then I should change character every sprite's layer (there's 15 of them) always when moving and it could affect performance. Or maybe I could write a script that changes all layers only if characters y-position change a certain amount and invoke it like every 0.1s.
Answer by CarterG81 · Jan 04, 2015 at 11:22 PM
I was sorely disappointed with the answers. I'm not sure if this will work for everyone, but it works for me.
Here is how you do it. It was actually easier than I thought, but it took time for it to click and for me to get that lightbulb moment.
What you want to do, is have one script which takes all of the Sprites, and adjusts their OrderInLayer based on their Transform (in my example, Transform.position.Z, but in 2D games it would be Transform.position.Y), but keeping the original sorting order of each piece of the character.
So what you do, is simple math:
OrderInLayer = Transform.Position.Y; Then simply add the original offset OrderInLayer.
It's as simple as during Start() of the script, grab the original layer using: OriginalLayer = CharacterPartGameObject.GetComponent().sortingOrder;
Then during update, simply apply the math:
CharacterPartGameObject.GetComponent().sortingOrder = (transform.position.z + HeadLayer) * -1;
Here is a script which takes my character, who is comprised of a Head, Torso, and two parts for his arms/legs.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class CharacterOrderInLayer : MonoBehaviour
{
//GameObject LimbPart, is the gameobject with the SpriteRenderer (the gameobject which has the Sprite).
//int LimbLayer, is the original OrderInLayer of the sprite. This is what you want to keep, relative to all other limbs.
//Link all of the gameobjects in the inspector, or you could theoretically use a GetComponentsInChildren method to grab by tag or name or whatever automatically.
//Character's Head Sprite
public GameObject HeadPart;
int HeadLayer;
//Character's Torso Sprite
public GameObject TorsoPart;
int TorsoLayer;
//Character's Right Arm
public GameObject RightForeArmPart;
int RightForeArmLayer;
public GameObject RightBicepPart;
int RightBicepLayer;
//Character's Left Arm
public GameObject LeftForeArmPart;
int LeftForeArmLayer;
public GameObject LeftBicepPart;
int LeftBicepLayer;
//Character's Right Leg
public GameObject RightThighPart;
int RightThighLayer;
public GameObject RightShinPart;
int RightShinLayer;
//Character's Left Leg
public GameObject LeftThighPart;
int LeftThighLayer;
public GameObject LeftShinPart;
int LeftShinLayer;
void Start()
{
HeadLayer = HeadPart.GetComponent<SpriteRenderer>().sortingOrder;
TorsoLayer = TorsoPart.GetComponent<SpriteRenderer>().sortingOrder;
RightForeArmLayer = RightForeArmPart.GetComponent<SpriteRenderer>().sortingOrder;
RightBicepLayer = RightBicepPart.GetComponent<SpriteRenderer>().sortingOrder;
LeftForeArmLayer = LeftForeArmPart.GetComponent<SpriteRenderer>().sortingOrder;
LeftBicepLayer = LeftBicepPart.GetComponent<SpriteRenderer>().sortingOrder;
RightThighLayer = RightThighPart.GetComponent<SpriteRenderer>().sortingOrder;
RightShinLayer = RightShinPart.GetComponent<SpriteRenderer>().sortingOrder;
LeftThighLayer = LeftThighPart.GetComponent<SpriteRenderer>().sortingOrder;
LeftShinLayer = LeftShinPart.GetComponent<SpriteRenderer>().sortingOrder;
}
// Update is called once per frame
void Update ()
{
int Zlayer = Mathf.RoundToInt(transform.position.z); //Turn the gameobject's Z position into an integer, so it can be converted to the layer. (TLDR: Transform.Position.Z = SortingOrder). Change this from Z to Y if you are using Unity2D.
Zlayer -= 90; //Custom Offset, because my sprite is +90z when at position (0x,0y,0z). This is NOT required.
//Each of these takes the individual sprites which make up the character, and then sets the correct SortingOrder based on their Transform.Position, while keeping all of the sprite's relative positioning.
HeadPart.GetComponent<SpriteRenderer>().sortingOrder = (Zlayer - HeadLayer) * -1;
TorsoPart.GetComponent<SpriteRenderer>().sortingOrder = (Zlayer - TorsoLayer) * -1;
RightForeArmPart.GetComponent<SpriteRenderer>().sortingOrder = (Zlayer - RightForeArmLayer) * -1;
RightBicepPart.GetComponent<SpriteRenderer>().sortingOrder = (Zlayer - RightBicepLayer) * -1;
LeftForeArmPart.GetComponent<SpriteRenderer>().sortingOrder = (Zlayer - LeftForeArmLayer) * -1;
LeftBicepPart.GetComponent<SpriteRenderer>().sortingOrder = (Zlayer - LeftBicepLayer) * -1;
RightThighPart.GetComponent<SpriteRenderer>().sortingOrder = (Zlayer - RightThighLayer) * -1;
RightShinPart.GetComponent<SpriteRenderer>().sortingOrder = (Zlayer - RightShinLayer) * -1;
LeftThighPart.GetComponent<SpriteRenderer>().sortingOrder = (Zlayer - LeftThighLayer) * -1;
LeftShinPart.GetComponent<SpriteRenderer>().sortingOrder = (Zlayer - LeftShinLayer) * -1;
}
}
You want the range of your bodypart's order in layers to be as narrow as possible.
$$anonymous$$y body parts range from 0 to 5, and that works fine. However, the more narrow the range (0 to 1, for example) the better.
Also, you should make your range where the $$anonymous$$ is 0. If you have a range from -5 to 5, then it may not work correctly.
Your answer
Follow this Question
Related Questions
Shader Graph accessing the Stencil Buffer with URP + 2D Renderer 1 Answer
Anti aliasing with sprites 0 Answers
Apply a shader to multiple 2D sprites 2 Answers
Sprite rendering problem 0 Answers
[SOLVED] Sprite showing up in "Scene", but not in "Game" 11 Answers