Struggling to change render order of lists of objects (XNA)

Started by
3 comments, last by majorbrot 11 years, 2 months ago

Hi all.

I am making a driving game where the object is to run over zombies who are walking in the road, in front of you. The view is a perspective one and you are driving forward so lots of stuff on the road and around it coming down the screen and being scaled up in size.

I have two C# lists at the moment; one containing zombies and the other debris which appears in the road. I wanted to get the render order right so that when the zombie is higher up, than a debris object it would be rendered behind the debris and vise versa. If it was just two separate objects, this would be easier - I would use the Y pos value to determine which to draw to the screen first. However, I need to compare every zombie on the screen to every piece of debris.

So I constructed the following nests of loops and if statements to try and get this correct render order.


            if (debrisList.Count != 0 && zombies.Count != 0)  // If both types of sprite are on screen
            {
                foreach (Zombie zom in zombies)
                {
                    foreach (Debris deb in debrisList)
                    {
                        if (zom.ReturnPosition().Y >= deb.ReturnPos().Y)
                        {
                            deb.Draw(this.spriteBatch);
                            zom.Draw(this.spriteBatch);
                        }
                    }
                    foreach(Debris deb in debrisList)
                    {
                        if (zom.ReturnPosition().Y <= deb.ReturnPos().Y)
                        {
                            zom.Draw(this.spriteBatch);
                            deb.Draw(this.spriteBatch);
                        }
                    }
                }
            }
            else
            {
                foreach (Zombie zom in zombies)
                {
                    zom.Draw(this.spriteBatch);
                }
                foreach (Debris deb in debrisList)
                {
                    deb.Draw(this.spriteBatch);
                }
            }

What the code is supposed to do (in my mind) is first of all check if both lists are not empty. If one is empty then it draws to screen as normal and no consideration is given to the order. If both lists are not empty what should be going on is that the zombies list is iterated through and for each zombie, I compare its Y position to the Y position of each debris item and render accordingly.

However, the zombie is always rendered on top of the debris.

Hopefully I have explained what I am trying to do and hopefully someone can help me. This has been driving me crazy for a few hours now!

Thanks

Advertisement
Would not just be easier to assign all your objects a z or depth value. This can simply be the distance between the object and your camera or car. Then just sort list based on the depth so that farthest away are drawn first. Then all you have to do is call the render function instead of having to iterate through your list for every object.

Ive only played with xna a little. I think my suggestion will require you to keep every object in one list. Hope that helps a little. I can go in more depth when im home.


Also your question why the zombie is always on top. Im going to take a stab in the dark and say your zombie sprite is taller than your debris sprite. And since youre useing the y value as your comparison the zo.bie wins everytime being taller in all.
[ dev journal ]
[ current projects' videos ]
[ Zolo Project ]
I'm not mean, I just like to get to the point.

Thanks for the reply.

The problem with putting the objects in the same list is that they are two completely different objects with very different behaviors. For example, I am trying to hit the zombie with the truck but avoid the road debris, the zombie moves around and is animated where are the debris is static so I need separate lists for the zombie object and debris object. Otherwise, I would have have indeed used a Z or depth value as you suggested.

Also, the Zombie is taller than the debris objects but... I am returning the Y pos, not the size of the sprite. I also have an origin set up for each of the sprites which is the sprite texture height so at the bottom. So the Y position should relate to the very bottom of each sprite.

OK taking a better look at it im now really confused with the code you posted.

First of all you're double drawing everything, just doing it backwards the second time. and the way you are drawing, for every zombie your all the debris that is behind it, or so I'm assuming. Also redrawing the zombie on top of that debris everytime. Then you sortve repeat the process but this time its all the debris in front of the zombie gets draw on top of a redrawn zombie.

That's only if there's something in both the lists, others wise you just draw everything....?

1: Do you even need the checks for if there is something in any of the lists? will foreach not just immediately exit like a for loop?

2: Both your zombie and debris containers use the exact same code to render themselves, could both objects not be inherited from a base renderable object. That way you could have them in the same container, sort by depth and render that way?

using two list in complicates things more than I would bother with but I think youre trying to get somthing like this, but you're doing all your logic for each zombie regardless of anything else.

*Psuedo*


 
// sort both list by they're 'Y' value
 
// I'm not sure of the container you're using but I'll use a std::vector  (c++)
std::vector<debris>::iterator debrisIterator = debris.begin();
std::vector<zombie>::iterator zombieIterator = zombie.begin();
 
// aslong as you haven't drawn all the objects continue
while (debrisIterator != debris.end() && zombieIterator != zombie.end())
{
   float debrisY = -1;
   float zombieY = -1; // -1 means the containers was empty.
 
   // get the 'Y' values of the iterators, set -1 if empty
 
   if (debrisY >= zombieY)
   {
      draw(debris)
      debrisiterator++;
    }
   else 
   {
      draw(zombie)
      zombieIterator++;
   }
}
 

Also, sprite batch is batching by texture? if so it won't matter what order you put them in, the last texture will always be on top unless you're using a depth buffer to prevent this. Just thinking.

[ dev journal ]
[ current projects' videos ]
[ Zolo Project ]
I'm not mean, I just like to get to the point.
the simplest thing you can do is to use the SpriteSortMode.BackToFront. a SpriteBatch.Begin()-overload takes it as a parameter. in the Draw()-call you can pass a layerdepth (a value between 0 and 1, with 0 being in the front). so to assign a layerdepth do something like

layerdepth = (screenHeight - objPos.y) / screenHeight

i didnt test it, but this or something similar should do the job without that manual sorting.

This topic is closed to new replies.

Advertisement