Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am in the process of writing a 2D game engine, and a dilemma emerged. Let me explain the situation...

I have a Scene class, to which various objects can be added (Drawable, ParticleEmitter, Light2D, etc), and as this is a 2D scene, things will obviously be drawn over each other.

My first thought was that I could have basic add and remove methods, but I soon realized that then there would be no way for the programmer to control the order in which things were drawn.

So I can up with two options, each with its pros and cons.

A) Would be to split the scene in layers. By that I mean instead of having the scene be a container of objects, have it be a container of layers, which are in turn the containers of objects.

B) Would require to have some kind of z-coordinate, and then have the scene sorted so objects with lower z get drawn first.

Option A is pretty solid, but the problem is with the lights. In what layer do I add it? Does it work cross-layer? On all bottom layers? And I still need the Z coordinate to calculate the shadow!

Option B would require me to change all my code from having Vector2D positions, to some kind of class that inherits from Vector2D and adds a z coordinate to it (I don't want it to be a Vector3D because I still need all the same methods the 2D kind has, just with .z clamped on).

Am I missing something? Is there an alternative to these methods?

I'm working in Javascript, if that makes a difference.

Edit: it was hard to decide with which option should I go, but I chose option B, adding the z coordinate. Some other design choices that might help others reading this question:

  • The actual map is a 2D space, and therefor, all positions should be Vector2D instances. But, as I also need a third coordinate, I made a new class, called ScenePosition, which inherits from Vector2D and just adds a z option. What was crucial was to keep the methods thinking that they are still operating on a Vector2D instance.

  • There is no way that sorting the scene by the z axis on every frame would be efficient, so watch out when adding things to the scene. For example: you would add a 4 at the end of this list, not the beginning: [1, 2, 3].

  • I chose to have a changedOrder property on the Scene, which you set to true when you change the z coordinate of some object. Then, the array is sorted before rendering, and changedOrder is set to true.

share|improve this question
    
Is this a side scroller? Many (if not all) the old school console side scrollers were done with option A and the artists were responsible for building the map into proper layers. Do you have lights already implemented? I'm curious what you're planning on doing with a 2D world and dynamic lights. –  Patrick Hughes Jun 28 '12 at 0:58
    
No, it's actually a game engine. I didin't implement the lights, although that will be fairly easy once the rest of the engine is built. I just thought that the lights would be interesting to make and use. –  jco Jun 28 '12 at 1:47
1  
In which case I recommend Byte56's option A for two reasons: first is no rewrite required and second is if you DO end up having to rewrite you can easily convert layers into the Z you need to speed up the process. –  Patrick Hughes Jun 28 '12 at 2:17
1  
There is really very little difference between the two. Layers just mean that you have a pre-sorted collection of objects at the same Z depth, which will be faster than resorting all objects (especially if you have lots and lots of objects). You can even dybamically add and remove layers for any arbitrary depth, so you dont need to limit yourself to a fixed set if layers. The only thing having an explicit Z coordinate (eg a Vec3) might give is "smoother" animations between depths, but I can't imagine any actually good use cases for that in a 2D/orthographic game. –  Sean Middleditch Jul 4 '12 at 20:42
    
Thanks for the advice, @seanmiddleditch, but I think that having layers adds simply too much unneeded complexity. I don't even need to sort the array usually, only when I'm adding things to it. As you see, I think that the convenience gained is more important than performance lost. Flying through an array of 100 000 000 elements costs me around... 120ms. I doubt that will ever be a problem. –  jco Jul 4 '12 at 21:26

3 Answers 3

up vote 4 down vote accepted

I'd go for option B, and yes this will require that you slighly modify the base classe(s) of you renderable objects so it contains at least the following:

  • A 2D vector for the position;
  • A Z-coordinate for depth sorting.

Using layers (option A) might seem to be a good idea when you start, but it's not necessarily the best option. This way your objects don't know where they are: this adds extra levels of indirection when they need to retrieve their depth.

That's why, to me, option B is the way to go if you want to avoid code complexity on the long term. It'll take a bit more time to implement first, sure. But I've seen a big option A -> option B refactoring on a quite mature engine and a quite advanced production, and this wasn't pretty.

There are some caveats, such as making sure you'll always sort the same 2 objects with identical Z-coordinates (hint: give them UIDs). Otherwise that's a call for Z-fighting.

A good idea, but not necessarily a must, is to use a floating-point format for your Z-coordinate. You can have a few preset values (eg PlayerZ = 0.0, EnemiesZ = -1.0, BackgroundZ = 10.0). But you keep an almost-infinite number of "layers" when you need to change your draw order. Let's say you need to add a weapon to your player and enemies: just use a WeaponsZOffset = -0.1 for them. Simply more flexible than integers.

share|improve this answer
    
Layers do not require an extra level of indirection for an object to get their depth. Just store the depth in the object like you otherwise would, and update it whenever the object is moved between layers. –  Sean Middleditch Jul 4 '12 at 20:37
    
This works around some indirections, but not all of them. If your object needs to change depth, it'll still need to unregister from the old layer and register to the new one. This also duplicates/caches data, which can lead to weird incoherences if handled incorrectly (such as somebody changing the cached depth without changing layer). –  Laurent Couvidou Jul 5 '12 at 8:14
    
Of you make even the most basic use of proper coding practices in any modern language, those kinds of issues just won't happen. The only methods even allowed to modify layers would also update the index in the object as well. When you start getting into very high performance data-oriented programming models, you pretty much have to start writing properly encapsulated code like that anyhow. :) –  Sean Middleditch Jul 5 '12 at 17:37
    
Well well let me wrap that in a "modern practice" speech. There's this principle called encapsulation: storing a game object property such as its depth in a separate object called a layer kind of breaks the principle. Using layers for this is just over-complicating things for no real benefit; you could call this modern practice, I call this code bloat. –  Laurent Couvidou Jul 6 '12 at 15:01
    
Your definition of encapsulation and "bloat" would also make any and all physics broad phase data structures invalid. Might want to rethink that. :) –  Sean Middleditch Jul 6 '12 at 19:35

Option A seems best to me. I don't do much in 2D, but isn't the draw order what's really important?

Each item has an index, things added last get drawn last therefore being on top. You can give the user the option to change the order ("send to back", "bring to front" type things). I don't know how you're using lights, but it seems like those could be at any layer as well. Meaning things on top of lights would be dark and things under lights would be lit. That's just my rambling thoughts, perhaps telling me how I'm wrong will spark a new idea :)

share|improve this answer

I currently implement Option B in my engine, but it was built from the ground up with z-indexes in mind (and in C++). 2D Vectors are just 3D Vectors with the third parameter set to 1, so refactoring a z-index into your 2D vectors wouldn't be hard.

See SourceMaking.com's page on the subject (Well, not really, it's for adding a parameter to a method, but it should still work)

share|improve this answer
1  
But wont all additions and other operations alter the z axis (which you don't want altered)? –  jco Jun 28 '12 at 8:31

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.