6
\$\begingroup\$

http://puu.sh/iMXFc/64095967d6.png

My current sorting method works only for objects that occupy 1 tile and fails for multi-tile objects. For examle, the bed in the above picture occupies 1×2 tiles, and the sorting fails.

Also that bed picture is 1 whole image that covers 2 tiles.

How can I change my sort method to accomodate this?

\$\endgroup\$
3

2 Answers 2

7
\$\begingroup\$

Here is a solution without resorting to 3D coordinates or splitting your sprite into 1x1 elements, that works if your objects occupy rectangular areas of tiles.

Let (ei,ej) be the coordinates of the bottom tile of entity e. I assume the x axis is pointing north-east and the y axis is pointing north-west, so that (ei,ej) has the smallest coordinate values among all tiles of e. Likewise, consider an entity f with bottom tile (fi,fj).

In the case where e is made of only one tile, you would put entity e in front of entity f if ei+ej < fi+fj. If ei+ej = fi+fj, you don't care which is in front of which.

The objects f and e Simple check of which object is in the lowest horizontal diagonal.

To work with multi-tiles rectangle, you need to consider whether a tile in f is "behind" a tile in e or conversely. Since rectangle recedes uniformly from the bottom-most tile, you need to do this check only for the extremities, i.e. the left-most tile of e, and right-most tile of e. So f is behind e if you can find one tile of f that is behind the left-most tile of e or the right-most tile of e.

In my example, I will use the right most tile of e, which happens to be (ei,ej). Note that if you know if f is "on the left" or "on the right" of e, you can already choose to check only the left-most or the right-most tile. Here, we only need to check using the left-most tile of e. However I've draw my pictures too fast and they do use the right-most check, so we'll stick with the right-most check.

Tiles "behind" a tile (i,j), correspond to tiles above (i,j), in the vertical diagonal passing through (i,j) (directly behind) or on the neighbor vertical diagonals on the left and on the right (partially behind). So the three diagonals corresponds to tiles with coordinates of the form (i+k,j+k), (i+k+1,j+k), (i+k,j+k+1), for k≥0

we want to find the green tiles, i.e. the tile of f behind the dark orange tile of e We want to find the green tiles i.e. the tile of f behind the dark orange tile of e.

So for entities e and f, you need to checker whether f has a tile within those three diagonals. But since you use rectangular areas, you don't need to check every single tile out of f to see if one matches. Indeed note that if for some k>0, the tile(i+k,j+k) belongs to f, then you have a diagonal row of tiles joining (fi,fj) to (i+k,j+k). If f is on the left of e, i.e. if fi < ei, that means that the tile (i+(k-1),j+(k-1)+1) belongs to f. If instead f is on the right of e (fi>ei), then it is the tile (i+(k-1)+1,j+(k-1)) that must also be part of f.

To reach the purple tile, f must pass through the pink one To reach the purple tile, f must pass through the pink one

In my example, f is on the left of e and I'll assume that in the calculations. The case of f on the right is symmetric. Consider the front line of f, starting from (fi,fj) and moving along the x axis infinitely. If f reaches the left-most diagonal of the three diagonals (in light blue), then the intersection tile of that front line and the diagonal must be a tile that belong to f. In that case, f is behind e and you should have e sorted before f. To check whether f reaches the diagonal, compute the distance d between the intersection of the lines and compare it the width w of the rectangle occupied by f.

Find the pink intersection of the white and blue lines, i.e. the pink tile Find the pink intersection of the white and blue lines, i.e. the pink tile. Compare the distance between the intersection and the origin of f and the width of f Compare the distance d between the intersection and the origin of f and the width w of f.

If f is not behind e, then either e is behind f or neither is behind the other. In both these cases, you can safely have f sorted before e. You just want to stay consistent if you have many entities so that that "is behind" remains a well-defined order relation, so you can use a sorting algorithm.

If you allow-non rectangular area, you may have more trouble, since e can be in front of f for some parts, and f can be in front of e for another part. In that case, it's not a bad thing to split your entities into 1x1 tile entities and use you regular algorithm (as Hatoru Hansou described his/her wall)

\$\endgroup\$
5
\$\begingroup\$

Brief : This answer suggest to break big sprites into one by one tiles so the zPosition for each sprite which is used to sort them in depth will work correctly.

To understand my proposal you will have to stop thinking in tiles for a while, even if your finished game needs to guarantee that all objects will be tile aligned.

I suggest you to adopt the same design I'm following for my current project. Position your objects in 3D, they have x,y and z. In my case, x is positive to the right; y is positive upwards; z is positive downwards, overlaps y, and when rendering all z values are converted to y of screen coordinates by scaling by 0.5.

To help visualize it: when a character jumps in place, its position.y value changes. When a character walks, its position.x and position.z change.

To sort them at rendering time, you can use the z position alone. If you feel like doing something more complex than that, you can do a Vector3 subtraction and calculate the distance of the object origin to the camera near plane but that implies that your game must be aware of the concept of camera, near and far planes, and other things that are more from the 3D world. Just sorting by z value is enough. In my case z is positive down, so objects with higher z values will be drawn last.

Now, this design alone can achieve correct draw order of any character if you set the objects origin correctly, for characters, the recommended is between the feet, not in the head or in the top-left corner. For walls the origin should be at the down most possible position, not at the top. Your sprites will need an offset property to indicate the translation that should be applied when drawing them as the sprite rectangle center won't be most of the time at the same position that the character/entity/object origin.

Diagonal walls still suppose a problem. I handle it by dividing the diagonal walls in smaller parts, in your case these parts will have the width of a tile, each part having it own origin.


Some images to illustrate these concepts.

In the following image, the green circles represent objects origins.

Layers on/off

How to build a wall? The ideal solution (in terms of design time needed to implement) is to have a unique wall tile and build walls using adjacent wall sprites. But I admit this is only possible if the wall assets are authored to be used this way. As I'm using a solid gray wall in this example gif, for me this was super easy to implement, but depending of the complexity of your walls assets, you may have problems with this. If you cannot get to solve it using only a single wall tile for each type of wall you will have to have more tan one asset for each wall type, this is OK and unavoidable in some cases.

My wall tile looks like this:

Wall

A longer wall is constructed by positioning many sprites, one per tile, all referring to the same image asset. Look at the origins of the first image, as the z position is used to decide how sprites are ordering at rendering, the result looks as intended.

Again, in my case the Z position is parallel to the Y axis, my Z is not diagonal, but if you prefer the Z to be diagonal, you can easily do that but you will have to think for a while in the required formula to go from 3D simulation space to 2D screen coordinates. To avoid having to do that is that I decided to make the Z axis parallel to the Y axis.

What I mean with objects origins? The origin is a concept borrowed from 3D engines. Sprites x, y coordinates are in screen coordinates, and they are relative to the object 3D coordinates when converted to screen coordinates during rendering. You first convert to screen coordinates the objects origins, then you look at your sprites x and y coordinates and apply a translation to draw the sprite in the correct position.

\$\endgroup\$
6
  • \$\begingroup\$ Thanks for the answer, i will experiment some things and report back when it works. \$\endgroup\$
    – Matthew
    Jul 4, 2015 at 16:12
  • 1
    \$\begingroup\$ As my English is far from perfect I added some images that will help you visualizing my proposed design. If you decide to try it I'm sure that those images will help. You will need some kind of efficient sorting algorithm to help ordering the sprites. \$\endgroup\$ Jul 5, 2015 at 21:18
  • \$\begingroup\$ I have the same problem, sprites with one single tile are easily managed. a simple way to do your algorithm is to set aSprite.zPosition = 100*xtiles+ytiles but this address single tile sprites, and it does not solve the bed problem, imagine a 2 by 2 tile sprite, the zPosition is set to 100*xofTile2*2+YofTile2*2 and all four tiles of the sprite have the same zPostion and when a character is goining to pass throw the sprite, sprite will wrongly covers the character. I did the down vote and I will reconsider the vote if you can answer the questioned problem. \$\endgroup\$
    – Iman Nia
    Sep 13, 2015 at 13:07
  • \$\begingroup\$ @Iman For the proposed solution to work correctly in all situations, you have to follow my advice of divide multi tile sprites in parts, each part has it own x,y,z coordinates. Long walls must be partitioned, gigant sprites representing the level boss must be partitioned. A x-axis aligned wall (if your engine accept them) can be a single sprite, no problem, but a diagonal, tile aligned, wall must be partitioned as in the example image. This is an attempt to add some 3D concepts while maintaining the design as simple as possible regarding the math involved. \$\endgroup\$ Sep 14, 2015 at 2:23
  • \$\begingroup\$ @HatoruHansou You do not need to compute xyz for each tile, just set x+y for zPosition and it is done! But it doesn't matter the problem is you can not break sprites into tiles easily,it would be very hard to break them and control them to stick together in game logic while panning and moving, or deleting, Also you need to change a lot of code and a lot of work in 3D software just to break sprites into single tiles. I don't see it a good way, I actually know a better way but I though there must be a better way,though I think my method is not that bad now. I will add it.Not satisfied,I revoted \$\endgroup\$
    – Iman Nia
    Sep 14, 2015 at 9:33

You must log in to answer this question.

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