Sorting Isometric Objects

Started by
17 comments, last by wodinoneeye 17 years, 4 months ago
I'm working on a 3d isometric game in C++ and Direct3d and I just can't seem to figure out how to sort my isometric objects. For example: Using objects such as 1x1 Block or 2x1 block Photobucket - Video and Image Hosting Currently I have all of the objects in a std:list and I'm using sort() comparing their ZOrder variable. Currently 0,0 on the object would be the furthest south corner. This variable is made like this: ZOrder = PositionX-PositionY; And so far it seems to work perfectly fine as long as the objects are square (ie: 1x1 or 5x5) but when I make an object that is 1x2 tiles long my sorting function doesn't work very well. Any help is appreciated.
Advertisement
In my own game I check for the lowest y value of the sprite (with the crates in your case that'd be somewhere near coords 16,0 (assuming 0,0 is the bottomleft)), if one is higher than the other I switch it.

That method has worked pretty well for me.
Hmm well let me see if I can explain what I'm looking for.
3 seperate blocks
The picture above is 3 separate blocks colored red,green, and blue.
Using the formula: ZOrder = PositionX-PositionY; (which simply finds out which one is lower on the screen) you get something like this.
3 separate blocks
Now that works great for blocks that are only 1 unit long but what about blocks that are say 3 or 4 units long.
1x4 block and 1x1 blockPhotobucket - Video and Image Hosting

Now this is where it becomes a problem. These two blocks now have a zorder that is equal to eachother even though common sense tells us that the longer block should be in the back. Here is another example.

Photobucket - Video and Image HostingPhotobucket - Video and Image Hosting
The second image is how the game would draw it.

So I need a new formula that takes into account the length and or width of the box but I can't seem to put my finger on how that would even work.
Avoid using larger objects. Break the large ones into single boxes, and repeat them as normal. Should get the same effect if they're made right. Might not be the answer you are looking for, and for some things might take a few special case issues to draw, but for most shouldn't work too badly.
Old Username: Talroth
If your signature on a web forum takes up more space than your average post, then you are doing things wrong.
How about instead of just having one entry in your list for the row of green boxes, you have four entries - one for each tile occupied. Then sort the list as normal. Iterate through the list and once you have drawn an object, mark it so it doesn't get drawn again.
Seems to me you can solve it by making sure the ones with lower x-values are always drawn before the ones with higher ones. So maybe ZOrder = PositionX * (height* of the map) - PositionY; ... or some such.

*height, like, as if it was a normal square-tiles-map; max y-value+1
Well the only problem with that is the game is built in a 3d plane where there is no real limit to x,y,z and 0,0,0 is the middle of the screen.
Photobucket - Video and Image Hosting
The game uses only objects (no tiles) and as so I really need to make sure that my drawing order for the objects is spot on.

Confusing I know. The renderer converts those coordinates to d3dvectors.

0,0,0 on the object = D3DXVECTOR3(PositionX+PositionY, -PositionX/2+PositionY/2+PositionZ-Width/2, 0.0f );
Ah. Yeah, need to sort some different way then, I suppse.

I have about one week of C++ experience. Not awfully familiar just that sorting function, but... From what I got from a little Google-search; the sort function compares objects using the less-than operator, right? So, the current less-than operator for them objects returns a.ZOrder < b.ZOrder or some such? If that isn't how things are done at all, then, uh, at least you know how I thought it was, so maybe the point comes across anyways :P

So:

bool operator<(const Somethingsomething& a, const Somethingsomething& b) {
if (a.PositionX != b.PositionX) return a.PositionX < b.PositionX;
return (a.PositionY > b.PositionY);
}

... so that the return-value is based on the x-coordinate, unless objects has the same x, then it's based on the y-value. I think I got the ><-things right :S
Ok well I followed your idea and found out that for some reason you can't use more than one variable in a std:list's sorting function. Not sure why but it doesn't matter since I can just combine the to variables into one.

Figuring out the Zorder (not sure how to display code yet)
Quote:for (std::list <_LObject*>::iterator j = Objects.begin(); j!=Objects.end();++j)
{
(*j)->ZOrder = 0;
for (std::list <_LObject*>::iterator i = Objects.begin(); i!=Objects.end();++i)
{
if ((*j)->PositionX > (*i)->PositionX || (*j)->PositionY < (*i)->PositionY)
(*j)->ZOrder++;

}
}

Sorting function looks like this
Quote:
if (o1->ZOrder < o2->ZOrder)
return true;

Seems to work great although the are a few instances in which the result seems random. I'm guessing that happens when object j has a larger X and object i has a larger Y. Still will need to do some more research.

Anyway I also need to figure out how to add a Z variable. An object that has a higher Z should be drawn last unless the other object is in front of it.
6 blocks
Quote:Ok well I followed your idea and found out that for some reason you can't use more than one variable in a std:list's sorting function. Not sure why but it doesn't matter since I can just combine the to variables into one.

That sounds quite odd. Tried some myself now, and pretty much managed to do what I suggested in my last post :|

And seems there's a [ source ]-tag.

bool sortingFunction(const _LObject& o1, const _LObject& o2) {  if (o1.PositionX != o2.PositionX) return o1.PositionX < o2.PositionX;  if (o1.PositionY != o2.PositionY) return o1.PositionY > o2.PositionY;  return o1.PositionZ < o2.PositionZ;}// sort with:Objects.sort(sortingFunction);


I'm able to run that code 'tleast, and I'd imagine it would deal with X/Y/Z-values properly.


Also:
Quote:if ((*j)->PositionX > (*i)->PositionX || (*j)->PositionY < (*i)->PositionY)
(*j)->ZOrder++;

That will in some cases increase ZOrder even tho j's X is lower than i's X (like you said, pretty much).

if ((*j)->PositionX != (*i)->PositionX) {    if (*j)->PositionX > (*i)->PositionX) (*j)->ZOrder++;} else if ((*j)->Positiony != (*i)->PositionY) {    if (*j)->PositionY < (*i)->PositionY) (*j)->ZOrder++;} else if (*j)->PositionZ > (*i)->PositionZ) (*j)->ZOrder++;


I'd much rather try'n get something like that sorting function above to work tho, and avoid all that extra iterating through the list in addition to whatever sort() does...

This topic is closed to new replies.

Advertisement