I've been getting into the real meat of the GameObject class today: drawing shapes and children. Unfortunately, I've run into a little tangle that I need to sort out before I go much further.
A brief explanation GameObject first. GameObject is analogous to Rubygame's Sprite class, in that it's meant to be the base class for space ships, dragons, penguins, and whatever else you can see on the screen. But it has some important differences.
A GameObject itself is not visible, but rather holds Shapes, which are visible and have colors, styles, textures, etc. Shapes are boxes, circles, lines, 2D polygon meshes, and maybe some other things of that nature. In addition to appearance, shapes will be the base for collision and physics, once I integrate Chipmunk.
Also, GameObject is hierarchical, i.e. each object can have child objects (and those can have children, and so on). The children inherit the parent's transformation, and can add their own local transformation. So if the parent is at position <-3, 0> (global space) and the child is at <-1,0> (local space), the child will appear to the user to be at <-4, 0>, because its position is relative to the parent. So, you can make a character out of multiple objects (maybe one for the head, one for the body, and so on), or make a katamari ball that other objects stick to and rotate with, or whatever else you want. Thanks to OpenGL, it's all extremely simple to program.
But, as I said, there's a snag. Shapes all have depths, to decide which shape appears in front of the other. If shape A has a depth of 50, and shape B has a depth of 25, B is closer, so it should be in front. But I hadn't thought about how shapes in separate GameObjects relate to each other. Do GameObjects have depths? If they do, and object O is in front of object P, but O's shapes are deeper than P's shapes, which shapes draw in front of each other? Or what if O and P have multiple shapes, with their depths alternating like two stacks of cards shuffled together?
There are two ways I could go. The lazy way is to just say that Shape's depths only apply within the GameObject, so all shapes of the nearer object will appear on top of all shapes of the deeper object. In this case, it's very simple to do: just render each object in the scene by descending depth, and within each object render its shapes also in descending depth. It'd just be a matter of sorting, really.
The less-lazy way is to say that GameObjects don't have depths, and only the Shapes' depths matter. But in this case, I have to maintain a list of all shapes in the scene, regardless of which object they belong to, sorted by depth, and then draw those in order by descending depth. (For those of you familiar with graphics rendering and wondering why I don't just use the depth buffer: I can't. Shapes can have partially transparent textures, and those wouldn't render correctly if I relied on the depth buffer.)
The first, lazy way has a disadvantage, which is the snag that caught my attention and started this train of thought: how is an object sorted relative to its children? Do the children go in front? Or behind? Well, really, you'd want to choose on a case-by-case basis, but then I have to think about a relative depth system. (Hrm, actually, it wouldn't be too hard — parent depth is 0, children can have negative depths to be in front, positive to be in back.)
Also weighing in is an advantage of the second way: shapes exist in a consistent, cohesive world, where depth is absolute. This feels "cleaner" to me, and it would also allow for neat stuff like having a shadow which is part of the object, but will always appear behind other objects. (To do something like this with the lazy system, you'd need to have the shadow as a separate, deeper object that follows the main object around like, well, a shadow.)
Either way would be fine for most games, so it's just a matter of deciding whether the effort to redesign things and implement the second system is worth the cleanliness.
I'm going to sleep on it. But if I were going to choose right now, I'd go with the first way. It's simpler, and has a certain hierarchical appear.