Adjusting Z Buffer for 3d Effect in 2D

I need to create a feeling of depth for my 2D game. What I got so far is this:

1584009--94657--$behindtree.PNG

1584009--94656--$beforetree.PNG

The tree sprite is partly transparent, if the player’s z position lies behind the tree, it looks like he’s standing behind the tree. The problem is to set the depth value correctly. For the trees that’s rather simple. I created this script:

	// y coordinates go from -10 to 10
	public float offset=0;
	public float deltaZ=1;
	public float size=1;
	
	void Update () {
		transform.position = new Vector3(
			transform.position.x,
			transform.position.y, 
			offset + deltaZ * (transform.position.y-size + 10) / 20);
	}

The script increases the z position, if the object moves up. That means that objects with a higher y coordinate appear behind objects that are “lower”. The strange calculation of the z coordinate works like this:

  • The offset is used to manage different layers of the game. If something should be in the background, you can increase the offset. For example: Offset 1 = background, Offset 0 = middleground, Offset -1 = foreground.

  • deltaZ is the maximum increase in the z value. If the object is at the lowest possible y coordinate, it just gets the offset as its z position. If it’s at the very top of the scene, it’s got a z value of offset+deltaZ

  • size is half the size of the object. This is necessary because in unity3d the position of an object is its middle-point. So the position of a sprite of a normal person would be somewhere around its waist. I need to position the objects according to their feet. Therefore the size is substracted from the y coordinate.

  • Since y coordinates in my scene go from -10 to 10, the y position +10 is somewhere between 0 and 20. Divide this by 20 and you got the percentage of deltaZ that needs to be added.

The script actually works rather nice. I added it to all objects in the scene. But there’s a problem.
What do I do with objects that are rotated ? Here’s a wall with the above script.

1584009--94654--$behindwall_right.PNG

1584009--94655--$behindwall_wrong.PNG

The player needs to be drawn behind the wall if he approaches from the left but before the wall if he comes from the right. I could probably add another script that modifies the z coordinate based on the x coordinate. But it’s already very difficult to get the correct behaviour out of the script. There’s a lot of experimenting involved till you find the right value for the size. I fear this would be complicated. Moreover, even if I modified my ZBuffer script, what about walls that are turned in the other direction ? Then I would have to invert the sorting.

Right now I’m using polygon colliders as a dirty workaround. The player is prevented from standing in positions were the graphical errors could be seen. But that means that his movement is rather restricted.

Is there a way to work around this ? Do you know a solution ? Right now I’m thinking about something like raycasting. I would use the basic ZBuffering based on the y position for simple things like trees. For these walls i could create a script that casts a ray from the players feet up along the y axis each frame. If the ray hits something, it must be behind the player. Do you think this would be a performance issue ? Eventually there will be other characters in the game. A few small animals maybe. A raycast for each of them for each frame ? I wouldn’t like to start creating this script, which would surely take me hours, only to find out that the hardware can’t handle it.

I wondered how to implement the raycast and found this problem:

1584025--94660--$conflict.PNG

The two persons have the same y coordinate and therefore the same z coordinate. Both start a raycast to adjust the z coordinates correctly. One ray will hit the wall, one will miss it. Therefore the wall’s z coordinate should be higher AND lower than the z coordinate of the two players, which is the same.

The problem is solved. I added a public bool variable to the script. If it’s checked the script performs a raycast in addition to the standard modification of the z value based on the y position.

If something is hit by the raycast, the script chooses the minimum of either: the calculated z position, based on the y position, or the z position of the collider that was hit.

1584083--94663--$raycast1.PNG

1584083--94664--$raycast2.PNG