Architectural questions

Sep 30, 2011 at 11:38 AM

Conceptually, what are a World, a Scene, a DisplayObject, a Component, and what is none of these?

Q1. Is a World a container for Scenes? I haven't touched World yet.

Q2. I've been working with a Scene containing DisplayObjects, which include models, 3 kinds of lights, and a skybox. What else goes in there?

Q3. I've been using the FPS and Input components, and I tried out the GameConsole. Are there others worth using?I've written a simple HudScreen component, to help me with debugging. [GraphicsComponent seems to be a different beast entirely.]

Q4. Cameras (unlike everything else) can only be attached to a specific Scene. FreeCamera is updatable, but the others are not. Why not a component?

Q5. XNA provides a service mechanism so that components can find their GraphicDevice, instead of having to pass it around everywhere, but you don't seem to use it. Is that a calculated decision?

I'm trying to understand where Nine is at and where it's headed, so I don't do changes that cut across things you've thought about already. If these are just things that are not done yet, or it's in transition, then some pointers about the future will help me decide how to tackle some of these issues.

Sep 30, 2011 at 1:33 PM
Edited Sep 30, 2011 at 1:40 PM
The architectural designs will involve a lot of thinking. I'll try to explain it in detail as much as I can.

There are several high level goals that I really wanted to achieve when design this beast, though some of them might not be feasible to certain types of game.

1. Be able to create a game level using a readable xml format. This format should be extensible, new type of objects can be plugged into this format without having to worry about serialization code.

2. Be able to save and load any changes made to the above level any time when the game is running. Again, it should be extensible and there's no need to write the actual serialization code.

3. Be able to record the game and play the saved replay with trival effort.

4. Be able to synchronize accross the network.

To achieve the above goals, some restrictions or guidelines have to be made as to how the game code are organized.

Game logic classes must be serialization friendly to be able to save/load at runtime. The only available serializer accross all platforms is XmlSerializer, so all game objects must be xml serializable. However, most graphics objects are hard to be xml serializable, like a texture in a graphics object could be a Texture2D but when it is saved, it will become a filename. So for graphics, this is a one way process, it is easy to load a graphical scene, but it can be really hard to modify and save changes.

So the concept of "View Templates" come into play. There's no need to save the changes to the graphics, since graphics typically doesn't affect gameplay. In order to do that, the graphics must be seperated with the game logic. Ideally, the game should runs the same logic just fine without any graphics attached.

In Nine, World is the container for all game logic classes, scene is the container for graphics objects, GraphicsComponent is the bridge that connects the two. GraphicsComponent will be serialized to a simple string that represents the filename of the view template for a game object. When the world is initially constructed from XmlSerializer, it doesn't contain any graphical info, then the graphics for the world is created and for each game object in the world, a corresponding graphics object is created based on the view template. As the world updates itself and the game continues to run, the state of each game object changes, and GraphicsComponent will update the appearance of its graphics object based on the state of the game object.

The same design principle applies to how I'll integrate movement, physics, audio and network. So there could be NavigationComponent, PhysicsComponent... in the future.

Now back to your question:

Q1: World contains Scene for simplicity. Used to be I'm think about seperating the two, but as a game alway has a graphics, I put them together so it's easier to access. In the future, World could also contain a physics space, a navigation graph...

Q2: DisplayObject serves as a container of graphics objects. It will also be used as the "root" object for a view template. I'm also planing add transform binding and animations to DisplayObject.

Q3: I'm not a fan of components provided with the Xna framework. One of the reason is that they are a bit heavy weight, and they typically needs to interact with the Game class. (Though there are ways to decouple that). There is a ZipContentManager if you want to archive the content files. Besides them, there aren't much ready to use xna style components I think.

Q4: Free camera needs to use the pulled input, so it needs to respond to game updates. Other cameras are triggered by the InputComponent. My design of the input system is that there's a centrialized InputComponent that is the event source for all input events, and you can create as many Input classes as you want, it is lightweight and it doesn't require much proccessing. In fact, one of the use case for the Input class is to add a Input instance to each UI element in my original design.

Q5: Services are cool. I'm also using services or its equivalents. There are basically two ways to create a graphics object, create the object with GraphicsDevice or IServiceProvider passed in through constructor (GraphicsDevice is preferred in this case), or create the object using default constructor then initalize that object with a GraphicsDevice or IServiceProvider (IServiceProvider is preferred in this case). I'm using these two methods in different places. Generally it is a best practise to avoid using 2 step initialization. But sometime 2 step initialization is useful. The game object in a World is created using 2 steps, create an object from the default constructor (recall that XmlSerializer needs that) then initlalize that object during the OnAdded method.

Oct 3, 2011 at 3:18 AM

Thanks for this most thoughtful (and helpful) post. Much to think about.

For me, the engine should support loading a 'scene tree' from XML, and leave the concept of 'game level' to the game programmer. The 'scene tree' should contain game objects (models, textures, primitives, etc) and lights, with cameras attached to objects. I see most of game save/restore also as best left to the game programmer, Yes, I would like there to be an 'asset manager' with its own serialisation so that dynamically loaded assets are reloaded and a 'scene manager' that will help restore scene objects to the positions they had, but in my case much of the game state resides outside the games engine and I see that as my job.

Record and replay, and network synchronisation are just plain hard. If these are to be organising principles for the software, the question is whether it can satisfy other important goals.

From my perspective, the key organising principle for a piece of software of this kind is the degree to which it can be understood and used, and that means design principles like interfaces, components and services. Perhaps you can explain what you mean by 'component' in the Nine context: clearly not the same as XNA. I agree that passing in a Game object is to be avoided, but passing in a GraphicDevice is not much better. Some kind of service or dependency injection approach would seem better. I see that you have a GraphicsDeviceServiceProvider, but I can't see that it is used anywhere. And what about reusing a SpriteBatch? And wouldn't it be better to get an Input() from an InputServiceProvider rather than directly from the class using new?

BTW I don't like to use pulled input, even for a FreeCamera. I would rather that Ctrl+D and CursorRight both map into (say) Action.PanRight, and the camera has a state called (say) PanningRight, which is switched on and off by events. You can't nicely map multiple keystrokes into the same state if you depend on pulled input. [But you still need game updates, of course.]

I've been writing a camera. If it was an XNA Component I would have done most of my initialisation in Initialize(), not in the ctor. It seems to me that a basic component/servce structure similar to XNA has its merits, even if you don't like using a heavyweight class like Game.

Sorry this is a bit disjointed. Still just trying to figure out where Nine is, where it's come from and where it's going, and looking to see if I can help it on its journey.