[SOLVED] How to handle Z index on top-down games?

I’m doing some experiments in Defold building a top-down world.
After some issues with the camera (not understanding why my sprites were not rendering) I found out that Defold orthographic camera projection limits the value of Near Z to -1 and Far Z to 1.
I also found out that decimal values are not taken into consideration.
Does that mean I can only handle 3 different Z levels for the whole game, -1, 0 and 1?
This would be very limiting and I hope that’s not the case.
Can you help me understand how to manage the Z index of game objects in Defold?

EDIT: sorry, decimal values do work. I should have checked the Z position of my tilemap layers. So we have more levels to handle, let’s say… 20? This system is very confusing though.

The 32bit float values used in rendering can represent 6 decimals, so you could have z values in the range [-.999999, +.999999]. Don’t use -1 and 1, since positioning objects on the near or far plane will cause z-fighting.

In reality, you probably don’t want to use such small numbers and increments, since it’s possible rounding errors from applying transformations will cause them to cross each other. Personally, I prefer [-.999, +.999]. With that I can define the first digit as the major layer, the second as the sub-layer, and the third as the sub-sub-layer (if ever needed).

Rather than setting the z value on many, many game objects and/or sprites, I would recommend creating a game object per layer, and make all game objects of that layer its children. That way you only need to set the z value on that one game object.

You could potentially use script properties to allow you to set the layer for a game object by specifying the layer’s name in the editor’s property panel. I’m thinking, the script has a string script property, and on init you use that string to look up a z value in a module, and set the game object’s z value to the value you got back.

2 Likes

Those are the default values and can be changed through a camera component, a third-party camera, or a simple render message.

2 Likes

I would also note that z positions accumulate when parenting e.g. if parents z position is equal to 0.5 and child’s z position is equal to 0.1, the world position for the child’s z would equal 0.6.

1 Like

Do you agree that is very confusing? I mean, I see a free input on the camera component (in the editor inspector), Near Z and Far Z. I set those values to the values I prefer. It’s not mentioned anywhere that those values can’t be less than X or more than X, but if I do so, my game objects are not rendered anymore, and I find it out only once I’m in play mode 🫤
Now you’re saying I can change those values with a simple render message. What do you mean? Can you please share an example? That’s confuses me even more, because you’re saying it’s possible to freely set those values with a simple render message but if I do that from the inspector it doesn’t work? 🫤

1 Like

Are you using any camera library? If so, which one?

The render script supports (or used to?) messages to set the projection, which also allowed you to set near and far.

msg.post("@render:", "use_fixed_fit_projection", { near = -1, far = 1 })

If you’re using the camera component, you can set near and far with the set_camera message.

msg.post("#camera", "set_camera", {aspect_ratio = 16/9, fov = math.pi * 0.5, near_z = 0.1, far_z = 500})

If you have a camera component, you can also use its property values you see in the editor (from what I read in the manual).

msg.post("@render:", "use_camera_projection")

If you’re using britzl’s orthographic camera library, have a look at how to configure the near and far values.

With your collection open in the editor, you can rotate the view so that you can see the z order of objects. Do this by holding down ctrl and click-and-drag. If you have a camera component, it will draw its frustrum (the volume it will render). Unfortunately, the orthographic camera doesn’t draw the frustrum correctly. But you get a sense for what falls within it.

Sure, you can set the Far Z to 1000, but then if you set the Z position of a game object to, for example, 20, it won’t render. And it won’t render because, independently of the values you set in your camera Near Z and Far Z, the orthographic camera will render only the game objects that are on a Z position between -1 and 1 because, apparently, Defold only supports that range.
That’s what’s really weird to me.
Example:


Camera is set to orthographic, Far Z to 1000, player Z position to 20 and its sprite to 0 = player is not rendered.
This happens both with the default camera and the defold-orthographic plugin.

What is the map Z order? Be sure of there’s no sprite/ objects having z value higher than your player’s

It’s below the player


The problem is not with my project. You can find out this behaviour even starting a brand new project: Near Z must be -1 and Far Z must be 1. Any different range won’t work.
This is a brand new project:
My Project.zip (53.7 KB)
Player Z position is 2. You’ll notice the player won’t be rendered until you set its Z position to something between -1 and 1. Even if the camera has Far Z set to 1000.

The problem is stated in Camera component manual that :

For reasons of backwards compatibility the default render script ignores the projection provided by the camera and always uses an orthographic stretch projection. Learn more about the render script and the view and projection matrices in the Render manual.

So for example when we are acquiring the camera we can also use the projection like so:

local camera_url = "/cam_pivot#camera"

function init(self)

	msg.post(camera_url, "acquire_camera_focus") -- acquire the camera
	msg.post("@render:", "use_camera_projection") -- Also use the projection
	
end

My Project_TESTCAM.zip (5.8 KB)

2 Likes

I might be wrong, but I am quite sure that it’s the FAR Z for the camera not the world. If your camera is still at Z=0 but your player is at 2. That means that your player is behind the camera and it’s the NEAR plane that should be changed.

1 Like

Looking at your example you have a camera but are not using it (there is no message sent to set it).

If you do not care about cameras and “just want this to work”. You can always duplicate the render script. This is how I tend to do thing because I eventually end up needing to change how the game renders things anyway.

  • Duplicate the files builtins/render/default.render and builtins/render/default.render_script
  • Name them game.render and game.render_script
  • Go into the game.render and change the script from /builtins/render/default.render_script to game.render_script.
  • Open the game.render_script and change the "DEFAULT_NEAR" and "DEFAULT_FAR".
  • Open game.project navigate to Bootstrap (at the top) and change the Render to your game.render

Here’s the example with a changed render_script
My Project.zip (30.4 KB)

Edit: I changed NEAR to -10 and FAR to 10. But here you could only change NEAR to -10 to get it to work. Again - we are changing the negative value because the character is behind your camera.

Second Edit: Here is your example but with the camera properly setup.
My Project.zip (28.4 KB)

2 Likes

Oh my god. Thank you.
So the problem was… that I wasn’t using the camera I was setting up? So the project was just creating a brand new camera at runtime with default values of Near Z -1 and Far Z 1? 😵‍💫
I can’t believe it :sob:
Thank you very much for the help!

One part is probably that you in some cases you were not using the camera.

But I think the bigger issue is that you didn’t realize that the FAR and NEAR plane are relative to the camera and not the world. That in combination that positive Z is toward you means that if your camera (or the default camera) is at origin (0, 0, 0) and you “increase” the z value of a sprite it is moved towards you, which means it’s now behind the camera.

This means that you should decrease the negative value of NEAR Z (e.i. changing that from -1 to -10) as you want to include things behind the camera.

1 Like

The camera z direction dilemma is the cause of many unnecessary debugging hours and chronic headaches for developers around the world. :sob:

If only we could all come together and make a decision, the correct decision of course being that the camera looks down the positive z axis by default. :wink:

(But yes, in Defold’s / OpenGL’s case, the camera looks down the negative z axis by default.)

2 Likes