3
\$\begingroup\$

I'm having a lot of trouble sorting it in such a way that it works with a moving entity that is "taller" than a "block" size in this map. I've recorded my problem here: https://www.youtube.com/watch?v=Zlo07gTZf9M

(and here it is in a choppy gif for easier access): Choppy problem.. The head's cut off

The numbers on the blocks/tiles represent their draw order. Basically, currently, it's incremented one after an entity has been passed, so that after that everything is drawn on top.

The draw order is per row, then per column, then per level (height). This allows the levels, created in Tiled, to basically be imported directly into Godot and the order of insertion is the initial render order (so no need for ZIndexing).

Currently, the sorting code is this:

int index = 0;
for (int level = 0; level < _stacksGrid.GetLength(2); ++level)
{
    for (int y = 0; y < _stacksGrid.GetLength(1); ++y)
    {
        for (int x = 0; x < _stacksGrid.GetLength(0); ++x)
        {
            // Check if there's a tile here
            Tile tile = _orderableTiles[x, y, level];
            if (null != tile)
            {
                tile.SetZ(index);
            }

            // Then check if there's an orderable here
            IIsometricOrderable orderable = _orderablesGrid[x, y, level];
            if (null != orderable)
            {
                // Move them all up from now on
                orderable.SetZ(index++);
            }
        }
    }
}

I've tried "stacking" tiles that are orderable. Basically making them into a single object, and only the grounded position is used for ordering. However, this unfortunately brings with it a bunch of new issues.

I'm hoping this is solvable without splitting the enity (the player in this case) in multiple parts.

If somebody has a good tutorial, book, example, open source project (I've looked at OpenRCT but it's a different rendering method), or just straight up answer.. I'm all ears. I've been breaking my head over this for way too long.

\$\endgroup\$
3
  • \$\begingroup\$ I did check them and implemented quite a few of those answers- but none seem able to deal with objects taller than 1 tile (block?) without splitting it up into multiple parts, unfortunately \$\endgroup\$ Jul 23, 2022 at 14:47
  • 1
    \$\begingroup\$ It's definitely the case that some shapes cannot be drawn using only ordering without splitting — see this example which is about polygons but the example also applies to boxes in an isometric view. Seems like it should be possible for this case, but I suspect it might need actually changing the order of the tile drawing depending on where the character is, not just picking where the character is in the fixed order of tiles. Consider that a tall character walking between tiles is effectively occupying a box of 4 (or 8) tiles, thus "splitting" 2 rows. \$\endgroup\$
    – Kevin Reid
    Jul 23, 2022 at 15:52

1 Answer 1

3
\$\begingroup\$

Usually we would sort objects by their ground position. In fact, we can argue there are two orders:

  • Sort objects by their ground position, back to front.
  • Sort object by their elevation, bottom to top. This is sorting by level.

And the first one should take priority. For example, here:

Character in front of stack of blocks, with some blocks behind drawn on top

The character should be in front of the whole stack that is behind, because of its GROUND SCREEN position. This is done by sorting by the SCREEN y position. For what I understand the block exist in a xy grid, but those aren't the screen position, so I'm not talking about that y position. Also keep in mind that different levels will result in a different screen y position. We want the lowest level. This is why I call it GROUND position. To be fair, I'm calling it screen y, it can be without scaling (zoom) and translating (pan).

Of course, on the stack, you should draw what is higher on top. This is sorting by level.

I remind you that you need to follow the same rule to position the character.


I guess you would want to put that on a single integer z-index. If you know the range of high for the stacks we can do that easily: You will use multiples of the maximum stack size for the first order, and then units for the second.

Something like this:

zIndex = ScreenY * MaxStackSize + Level

If you view a range of levels (for example, if you slice the stacks for visibility), you could do it like this:

zIndex = ScreenY * (MaxLevel - MinLevel) + (Level - MinLevel)

You could consider the characters to be on a level higher than the floor it is standing on.

Alternatively, you could consider the character to be on the same level, but make the block take even values for z-index (the computation above but multiplied by two), and the characters take the odd values (the computation above but multiplied by two and plus one). So characters and the blocks do not compete for a slot.


If you are doing this with Sprites in Godot you can accomplish this by using the YSort node. Except your stacked blocks should all have the same position (so the YSort do not sort them), which you can accomplish with the offset property of the Sprite. And you would still be responsible for sorting the stack.

In fact, I would make all the blocks of the stack child of a single node… So that YSort sorts those parent nodes, and you only need to sort the children.

And since you need to follow the same rule for the character, you would use offset on the character sprite to change the level.

And you would have to compensate for that when positioning with the mouse. if the mouse pointer is on a top face of a block you can put the character there, otherwise, you would have to put the character on one of the stacks that are in front, and the character might have to be floating in the air to match the mouse pointer position.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .