Well, it’s been a while but I finally think I came up with a solution, which is surprisingly simple, although a bit devious.
Okay, as I noted before, the problem is the sorting algorithm only takes into account the Y, and completely ignores the X. That works fine for the tiles as they are always in the same relative spot at each Y interval, but mobiles go between those spots and the sorting becomes ambiguous because of this.
So after fiddling around a bunch with shaders and shelving that as just ineffective as a solution, I went back to basics and fixed my issue in the first attempt which is very sad. The reason I missed this before was I was incorrectly using my collider position as what the sorting position is which is wrong as it wasn’t at the same spot (close enough that my error wasn’t obvious, but off enough to look like the issue was not solvable).
So the solution, pretty simple:
- Ensure your character graphic sorting point is under the character. You can either use a pivot point change on the graphic, or change the graphic itself (I tried both, and both seem to work). To change this you have to both change the settings on the sprite itself and the sprite render. You want your graphic to have some room below it to deal with some edge clipping, but don’t need too much (mine is using 3 pixels from a 32x32 sprite).
- Figure out which tile the sorting point is in. Now, since the sorting point is now your transform.position of the sprite, this is pretty easy. Just use the grid or the tilemap’s worldtocell function. Only trick to note here is to effectively ignore the Z for now since you’ll be dynamically altering it. So probably do the worldtocell on a new Vector3 using only the position’s x and y and a baseline, usually the ground Z.
- Next convert that Vector3Int back into a Vector3 to get the default position of the tile.
- Find the relative position within the cell of your character using your transform.position - defaultposition.
- Inspect the Y coordinate of this and figure out if you are in the bottom half of the ground diamond. For me this was simply Y < .25. If your anchors for your grid are different though you may have to test out values (it should be a .25 increment of some kind).
- If the character is in the in the bottom half of the diamond, set their Z to ground level + 1, else ground level + 2.
Do that in your update routine and sorting should work out appropriately now.
Here is the code I used on my test script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Testscript2 : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
public Vector3 relativePos;
public Vector3 pos;
// Update is called once per frame
void Update()
{
pos = new Vector3 (transform.position.x, transform.position.y, 0);
Grid g = GameResources.Current.GameGrid;
Vector3Int tilepos = g.WorldToCell(pos);
Vector3 tileworldpos = g.CellToWorld(tilepos);
relativePos = pos - tileworldpos;
if (relativePos.y < .25)
transform.position = new Vector3(transform.position.x, transform.position.y, 1);
else
transform.position = new Vector3(transform.position.x, transform.position.y, 2);
}
}
The only way this seems to go awry is if you allow your character to move into tiles (like you’re going into a cave or the like). You’d have to experiment more if that is the case, although I suspect it is just a matter of keeping the Z always at 1 in that case.
Damn, I’ve been at this issue for weeks, sad that the solution was one I attempted real early but got just a bit wrong.